From b73601f483d2d364c135dd5a9e58e71d156aa2aa Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Tue, 2 Jun 2020 23:28:32 +0200 Subject: [PATCH] Added hash and lastModified check to the fragment synthesis cache. Location of cache changed to always be inside fragments-folder. Cache is enabled by default and can be disabled by command-line switch. #469 --- .../fragment/cache/fragment-cache-mos6502.asm | 227 +++++++++--------- .../cache/fragment-cache-mos6502x.asm | 141 +++++------ .../java/dk/camelot64/kickc/Compiler.java | 4 - src/main/java/dk/camelot64/kickc/KickC.java | 19 +- .../kickc/fragment/AsmFragmentSystemHash.java | 66 +++++ .../fragment/AsmFragmentTemplateCache.java | 61 ++++- .../AsmFragmentTemplateMasterSynthesizer.java | 10 +- .../AsmFragmentTemplateSynthesizer.java | 7 +- .../dk/camelot64/kickc/model/Program.java | 14 +- .../camelot64/kickc/test/TestFragments.java | 6 +- .../dk/camelot64/kickc/test/TestPrograms.java | 9 +- 11 files changed, 323 insertions(+), 241 deletions(-) create mode 100644 src/main/java/dk/camelot64/kickc/fragment/AsmFragmentSystemHash.java diff --git a/src/main/fragment/cache/fragment-cache-mos6502.asm b/src/main/fragment/cache/fragment-cache-mos6502.asm index d047599b5..c452ec7f1 100644 --- a/src/main/fragment/cache/fragment-cache-mos6502.asm +++ b/src/main/fragment/cache/fragment-cache-mos6502.asm @@ -1,113 +1,4 @@ -//FRAGMENT vbuz1=vbuc1 -lda #{c1} -sta {z1} -//FRAGMENT vbuz1_lt_vbuc1_then_la1 -lda {z1} -cmp #{c1} -bcc {la1} -//FRAGMENT pbuc1_derefidx_vbuz1=vbuc2 -lda #{c2} -ldy {z1} -sta {c1},y -//FRAGMENT vbuz1=vbuz2_band_vbuc1 -lda #{c1} -and {z2} -sta {z1} -//FRAGMENT vbuz1_neq_0_then_la1 -lda {z1} -cmp #0 -bne {la1} -//FRAGMENT vbuz1=vbuz1_plus_vbuc1 -lda #{c1} -clc -adc {z1} -sta {z1} -//FRAGMENT vbuz1=_inc_vbuz1 -inc {z1} -//FRAGMENT vbuaa_lt_vbuc1_then_la1 -cmp #{c1} -bcc {la1} -//FRAGMENT pbuc1_derefidx_vbuaa=vbuc2 -tay -lda #{c2} -sta {c1},y -//FRAGMENT pbuc1_derefidx_vbuxx=vbuc2 -lda #{c2} -sta {c1},x -//FRAGMENT pbuc1_derefidx_vbuyy=vbuc2 -lda #{c2} -sta {c1},y -//FRAGMENT vbuaa=vbuz1_band_vbuc1 -lda #{c1} -and {z1} -//FRAGMENT vbuxx=vbuz1_band_vbuc1 -lda #{c1} -and {z1} -tax -//FRAGMENT vbuyy=vbuz1_band_vbuc1 -lda #{c1} -and {z1} -tay -//FRAGMENT vbuz1=vbuxx_band_vbuc1 -txa -and #{c1} -sta {z1} -//FRAGMENT vbuaa=vbuxx_band_vbuc1 -txa -and #{c1} -//FRAGMENT vbuxx=vbuxx_band_vbuc1 -txa -and #{c1} -tax -//FRAGMENT vbuyy=vbuxx_band_vbuc1 -txa -and #{c1} -tay -//FRAGMENT vbuz1=vbuyy_band_vbuc1 -tya -and #{c1} -sta {z1} -//FRAGMENT vbuaa=vbuyy_band_vbuc1 -tya -and #{c1} -//FRAGMENT vbuxx=vbuyy_band_vbuc1 -tya -and #{c1} -tax -//FRAGMENT vbuyy=vbuyy_band_vbuc1 -tya -and #{c1} -tay -//FRAGMENT vbuaa_neq_0_then_la1 -cmp #0 -bne {la1} -//FRAGMENT vbuxx=vbuxx_plus_vbuc1 -txa -clc -adc #{c1} -tax -//FRAGMENT vbuyy=vbuyy_plus_vbuc1 -tya -clc -adc #{c1} -tay -//FRAGMENT vbuxx_lt_vbuc1_then_la1 -cpx #{c1} -bcc {la1} -//FRAGMENT vbuxx=vbuc1 -ldx #{c1} -//FRAGMENT vbuxx=_inc_vbuxx -inx -//FRAGMENT vbuyy=vbuc1 -ldy #{c1} -//FRAGMENT vbuyy_lt_vbuc1_then_la1 -cpy #{c1} -bcc {la1} -//FRAGMENT vbuyy=_inc_vbuyy -iny -//FRAGMENT vbuxx_neq_0_then_la1 -cpx #0 -bne {la1} +//KICKC FRAGMENT CACHE 1a8109f402 //FRAGMENT _deref_pbuc1=vbuc2 lda #{c2} sta {c1} @@ -119,6 +10,15 @@ sta {z1} lda #{c1} cmp {z1} beq {la1} +//FRAGMENT vbuz1=vbuc1 +lda #{c1} +sta {z1} +//FRAGMENT pbuc1_derefidx_vbuz1=vbuc2 +lda #{c2} +ldy {z1} +sta {c1},y +//FRAGMENT vbuz1=_inc_vbuz1 +inc {z1} //FRAGMENT vbuc1_neq_vbuz1_then_la1 lda #{c1} cmp {z1} @@ -138,6 +38,10 @@ lda #<{c1} sta {z1} lda #>{c1} sta {z1}+1 +//FRAGMENT vbuz1_lt_vbuc1_then_la1 +lda {z1} +cmp #{c1} +bcc {la1} //FRAGMENT pbuz1=pbuc1 lda #<{c1} sta {z1} @@ -229,6 +133,10 @@ bcc {la1} //FRAGMENT vbuz1=vbuz2 lda {z2} sta {z1} +//FRAGMENT vbuz1=vbuz2_band_vbuc1 +lda #{c1} +and {z2} +sta {z1} //FRAGMENT _deref_pbuc1=_inc__deref_pbuc1 inc {c1} //FRAGMENT _deref_pbuc1=_dec__deref_pbuc1 @@ -255,9 +163,22 @@ tay //FRAGMENT vbuc1_eq_vbuaa_then_la1 cmp #{c1} beq {la1} +//FRAGMENT pbuc1_derefidx_vbuaa=vbuc2 +tay +lda #{c2} +sta {c1},y +//FRAGMENT pbuc1_derefidx_vbuxx=vbuc2 +lda #{c2} +sta {c1},x +//FRAGMENT pbuc1_derefidx_vbuyy=vbuc2 +lda #{c2} +sta {c1},y //FRAGMENT vbuc1_neq_vbuxx_then_la1 cpx #{c1} bne {la1} +//FRAGMENT vbuaa_lt_vbuc1_then_la1 +cmp #{c1} +bcc {la1} //FRAGMENT pbuz1=pbuc1_plus_vbuaa clc adc #<{c1} @@ -335,14 +256,55 @@ ldx {z1} //FRAGMENT vbuz1=vbuaa_band_vbuc1 and #{c1} sta {z1} +//FRAGMENT vbuz1=vbuxx_band_vbuc1 +txa +and #{c1} +sta {z1} +//FRAGMENT vbuz1=vbuyy_band_vbuc1 +tya +and #{c1} +sta {z1} +//FRAGMENT vbuaa=vbuz1_band_vbuc1 +lda #{c1} +and {z1} //FRAGMENT vbuaa=vbuaa_band_vbuc1 and #{c1} +//FRAGMENT vbuaa=vbuxx_band_vbuc1 +txa +and #{c1} +//FRAGMENT vbuaa=vbuyy_band_vbuc1 +tya +and #{c1} +//FRAGMENT vbuxx=vbuz1_band_vbuc1 +lda #{c1} +and {z1} +tax //FRAGMENT vbuxx=vbuaa_band_vbuc1 and #{c1} tax +//FRAGMENT vbuxx=vbuxx_band_vbuc1 +txa +and #{c1} +tax +//FRAGMENT vbuxx=vbuyy_band_vbuc1 +tya +and #{c1} +tax +//FRAGMENT vbuyy=vbuz1_band_vbuc1 +lda #{c1} +and {z1} +tay //FRAGMENT vbuyy=vbuaa_band_vbuc1 and #{c1} tay +//FRAGMENT vbuyy=vbuxx_band_vbuc1 +txa +and #{c1} +tay +//FRAGMENT vbuyy=vbuyy_band_vbuc1 +tya +and #{c1} +tay //FRAGMENT vbuz1=vbuaa_rol_1 asl sta {z1} @@ -420,8 +382,19 @@ ora {z2} sta {z1} //FRAGMENT vbuz1=vbuxx_bor_vbuxx stx {z1} +//FRAGMENT vbuxx_lt_vbuc1_then_la1 +cpx #{c1} +bcc {la1} //FRAGMENT _deref_pbuc1=vbuyy sty {c1} +//FRAGMENT vbuxx=vbuc1 +ldx #{c1} +//FRAGMENT vbuyy=vbuc1 +ldy #{c1} +//FRAGMENT vbuxx=_inc_vbuxx +inx +//FRAGMENT vbuyy=_inc_vbuyy +iny //FRAGMENT vbuc1_neq_vbuyy_then_la1 cpy #{c1} bne {la1} @@ -431,6 +404,9 @@ beq {la1} //FRAGMENT vbuc1_eq_vbuyy_then_la1 cpy #{c1} beq {la1} +//FRAGMENT vbuyy_lt_vbuc1_then_la1 +cpy #{c1} +bcc {la1} //FRAGMENT vbuz1=vbuz2_bor_vbuyy tya ora {z2} @@ -530,9 +506,6 @@ ora $ff tax //FRAGMENT vbuyy=vbuaa tay -//FRAGMENT vbuyy_neq_0_then_la1 -cpy #0 -bne {la1} //FRAGMENT pbuz1=pbuz1_plus_vbuc1 lda #{c1} clc @@ -541,3 +514,31 @@ sta {z1} bcc !+ inc {z1}+1 !: +//FRAGMENT vbuz1_neq_0_then_la1 +lda {z1} +cmp #0 +bne {la1} +//FRAGMENT vbuz1=vbuz1_plus_vbuc1 +lda #{c1} +clc +adc {z1} +sta {z1} +//FRAGMENT vbuaa_neq_0_then_la1 +cmp #0 +bne {la1} +//FRAGMENT vbuxx=vbuxx_plus_vbuc1 +txa +clc +adc #{c1} +tax +//FRAGMENT vbuyy=vbuyy_plus_vbuc1 +tya +clc +adc #{c1} +tay +//FRAGMENT vbuxx_neq_0_then_la1 +cpx #0 +bne {la1} +//FRAGMENT vbuyy_neq_0_then_la1 +cpy #0 +bne {la1} diff --git a/src/main/fragment/cache/fragment-cache-mos6502x.asm b/src/main/fragment/cache/fragment-cache-mos6502x.asm index 3fc2b3a6e..51d8a6cf9 100644 --- a/src/main/fragment/cache/fragment-cache-mos6502x.asm +++ b/src/main/fragment/cache/fragment-cache-mos6502x.asm @@ -1,3 +1,4 @@ +//KICKC FRAGMENT CACHE 1a8109f402 //FRAGMENT vbuz1=vbuc1 lda #{c1} sta {z1} @@ -7510,6 +7511,8 @@ sta {z1} lda {z1}+1 sbc {z2}+1 sta {z1}+1 +//FRAGMENT vbuyy=pbuc1_derefidx_vbuxx +ldy {c1},x //FRAGMENT _stackpullbyte_1 pla //FRAGMENT vbsz1=_sbyte_vwuz2 @@ -7902,8 +7905,6 @@ beq {la1} cpy #{c1} bcc {la1} beq {la1} -//FRAGMENT vbuyy=pbuc1_derefidx_vbuxx -ldy {c1},x //FRAGMENT vbsaa=_inc_vbsaa clc adc #1 @@ -13824,6 +13825,62 @@ lda {z1} cmp #<{c1} bcc {la1} !: +//FRAGMENT vbuz1=_deref_pbuc1_bxor_vbuc2 +lda #{c2} +eor {c1} +sta {z1} +//FRAGMENT vbuz1=vbuc1_bxor_vbuaa +eor #{c1} +sta {z1} +//FRAGMENT vbuz1=vbuc1_bxor_vbuxx +txa +eor #{c1} +sta {z1} +//FRAGMENT vbuz1=vbuc1_bxor_vbuyy +tya +eor #{c1} +sta {z1} +//FRAGMENT vbuaa=vbuc1_bxor_vbuaa +eor #{c1} +//FRAGMENT vbuaa=vbuc1_bxor_vbuxx +txa +eor #{c1} +//FRAGMENT vbuaa=vbuc1_bxor_vbuyy +tya +eor #{c1} +//FRAGMENT vbuxx=vbuc1_bxor_vbuaa +eor #{c1} +tax +//FRAGMENT vbuxx=vbuc1_bxor_vbuxx +txa +eor #{c1} +tax +//FRAGMENT vbuxx=vbuc1_bxor_vbuyy +tya +eor #{c1} +tax +//FRAGMENT vbuyy=vbuc1_bxor_vbuaa +eor #{c1} +tay +//FRAGMENT vbuyy=vbuc1_bxor_vbuxx +txa +eor #{c1} +tay +//FRAGMENT vbuyy=vbuc1_bxor_vbuyy +tya +eor #{c1} +tay +//FRAGMENT vbuaa=_deref_pbuc1_bxor_vbuc2 +lda #{c2} +eor {c1} +//FRAGMENT vbuxx=_deref_pbuc1_bxor_vbuc2 +lda #{c2} +eor {c1} +tax +//FRAGMENT vbuyy=_deref_pbuc1_bxor_vbuc2 +lda #{c2} +eor {c1} +tay //FRAGMENT vbuz1=vbuz1_plus_vbuaa clc adc {z1} @@ -14849,18 +14906,6 @@ sta ({z1}),y //FRAGMENT vbuaa_eq_vbuz1_then_la1 cmp {z1} beq {la1} -//FRAGMENT vwsz1_lt_vbsc1_then_la1 -NO_SYNTHESIS -//FRAGMENT vwsz1_lt_vwuc1_then_la1 -lda {z1}+1 -bmi {la1} -cmp #>{c1} -bcc {la1} -bne !+ -lda {z1} -cmp #<{c1} -bcc {la1} -!: //FRAGMENT vbuz1=vbuxx_band_pbuz2_derefidx_vbuc1 ldy #{c1} txa @@ -17242,6 +17287,18 @@ lda {z1}+3 cmp {z2}+3 beq {la1} !: +//FRAGMENT vwsz1_lt_vbsc1_then_la1 +NO_SYNTHESIS +//FRAGMENT vwsz1_lt_vwuc1_then_la1 +lda {z1}+1 +bmi {la1} +cmp #>{c1} +bcc {la1} +bne !+ +lda {z1} +cmp #<{c1} +bcc {la1} +!: //FRAGMENT vwsz1=pwsc1_derefidx_vbuz2_minus_pwsc2_derefidx_vbuz2 ldy {z2} sec @@ -17627,62 +17684,6 @@ sta {c1},y lda #{c2} ora {c1},x sta {c1},x -//FRAGMENT vbuz1=_deref_pbuc1_bxor_vbuc2 -lda #{c2} -eor {c1} -sta {z1} -//FRAGMENT vbuz1=vbuc1_bxor_vbuaa -eor #{c1} -sta {z1} -//FRAGMENT vbuz1=vbuc1_bxor_vbuxx -txa -eor #{c1} -sta {z1} -//FRAGMENT vbuz1=vbuc1_bxor_vbuyy -tya -eor #{c1} -sta {z1} -//FRAGMENT vbuaa=vbuc1_bxor_vbuaa -eor #{c1} -//FRAGMENT vbuaa=vbuc1_bxor_vbuxx -txa -eor #{c1} -//FRAGMENT vbuaa=vbuc1_bxor_vbuyy -tya -eor #{c1} -//FRAGMENT vbuxx=vbuc1_bxor_vbuaa -eor #{c1} -tax -//FRAGMENT vbuxx=vbuc1_bxor_vbuxx -txa -eor #{c1} -tax -//FRAGMENT vbuxx=vbuc1_bxor_vbuyy -tya -eor #{c1} -tax -//FRAGMENT vbuyy=vbuc1_bxor_vbuaa -eor #{c1} -tay -//FRAGMENT vbuyy=vbuc1_bxor_vbuxx -txa -eor #{c1} -tay -//FRAGMENT vbuyy=vbuc1_bxor_vbuyy -tya -eor #{c1} -tay -//FRAGMENT vbuaa=_deref_pbuc1_bxor_vbuc2 -lda #{c2} -eor {c1} -//FRAGMENT vbuxx=_deref_pbuc1_bxor_vbuc2 -lda #{c2} -eor {c1} -tax -//FRAGMENT vbuyy=_deref_pbuc1_bxor_vbuc2 -lda #{c2} -eor {c1} -tay //FRAGMENT pbuc1_derefidx_vbuyy=pbuc1_derefidx_vbuyy_bor_vbuc2 lda #{c2} ora {c1},y diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 4d1783421..f831009f4 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -98,10 +98,6 @@ public class Compiler { program.setAsmFragmentBaseFolder(asmFragmentBaseFolder); } - public void setAsmFragmentCacheFolder(Path asmFragmentCacheDir) { - program.setAsmFragmentCacheFolder(asmFragmentCacheDir); - } - public AsmFragmentTemplateMasterSynthesizer getAsmFragmentMasterSynthesizer() { return program.getAsmFragmentMasterSynthesizer(); } diff --git a/src/main/java/dk/camelot64/kickc/KickC.java b/src/main/java/dk/camelot64/kickc/KickC.java index 788921f10..38de3542c 100644 --- a/src/main/java/dk/camelot64/kickc/KickC.java +++ b/src/main/java/dk/camelot64/kickc/KickC.java @@ -95,8 +95,8 @@ public class KickC implements Callable { @CommandLine.Option(names = {"-Onoloophead"}, description = "Optimization Option. Disabled experimental loop-head constant pass which identifies loops where the condition is constant on the first iteration.") private boolean optimizeNoLoopHeadConstant = false; - @CommandLine.Option(names = {"-Ocache"}, description = "Optimization Option. Enables a fragment cache file.") - private boolean optimizeFragmentCache = false; + @CommandLine.Option(names = {"-Onocache"}, description = "Optimization Option. Disables the fragment synthesis cache. The cache is enabled by default.") + private boolean optimizeNoFragmentCache = false; @CommandLine.Option(names = {"-Oliverangecallpath"}, description = "Optimization Option. Enables live ranges per call path optimization, which limits memory usage and code, but takes a lot of compile time.") private boolean optimizeLiveRangeCallPath = false; @@ -214,25 +214,14 @@ public class KickC implements Callable { if(fragmentDir == null) { fragmentDir = new File("fragment/").toPath(); } - - Path fragmentCacheDir = null; - if(optimizeFragmentCache) { - if(outputDir != null) { - fragmentCacheDir = outputDir; - } else { - fragmentCacheDir = new File(".").toPath(); - } - } + compiler.setAsmFragmentBaseFolder(fragmentDir); configVerbosity(compiler); - compiler.setAsmFragmentBaseFolder(fragmentDir); - compiler.setAsmFragmentCacheFolder(fragmentCacheDir); - Program program = compiler.getProgram(); // Initialize the master ASM fragment synthesizer - program.initAsmFragmentMasterSynthesizer(); + program.initAsmFragmentMasterSynthesizer(!optimizeNoFragmentCache); Path currentPath = new File(".").toPath(); diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentSystemHash.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentSystemHash.java new file mode 100644 index 000000000..aea6a91da --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentSystemHash.java @@ -0,0 +1,66 @@ +package dk.camelot64.kickc.fragment; + +import dk.camelot64.kickc.model.TargetCpu; + +import java.io.File; +import java.nio.file.Path; + +/** + * Capable of creating a hash code that describes the total state of the ASM fragment system. + *

+ * This hash is used to determine whether to use or discard the cache. + */ +public class AsmFragmentSystemHash { + + /** Hash of the fragment source files. */ + private long hash; + + /** Last modified date for the fragment source files. */ + private long lastModified; + + + AsmFragmentSystemHash(long hash, long lastModified) { + this.lastModified = lastModified; + this.hash = hash; + } + + /** + * Run through the fragment folder and calculate the hash/modify time + * @param baseFragmentFolder The fragment folder + * @return The fragment system hash + */ + public static AsmFragmentSystemHash calculate(Path baseFragmentFolder) { + long hash = 0; + long lastModified = 0; + final TargetCpu.Feature[] cpuFeatures = TargetCpu.Feature.values(); + for(TargetCpu.Feature cpuFeature : cpuFeatures) { + hash += cpuFeature.getName().hashCode(); + final Path cpuFeatureFolder = baseFragmentFolder.resolve(cpuFeature.getName()); + final File cpuFeatureFolderFile = cpuFeatureFolder.toFile(); + if(cpuFeatureFolderFile.exists() && cpuFeatureFolderFile.isDirectory()) { + final File[] files = cpuFeatureFolderFile.listFiles((dir, name) -> name.endsWith(".asm")); + if(files != null) + for(File file : files) { + hash += file.length(); + hash += file.getName().hashCode(); + if(file.lastModified() > lastModified) + lastModified = file.lastModified(); + + } + } + } + return new AsmFragmentSystemHash(hash, lastModified); + } + + public long getHash() { + return hash; + } + + public String getHashString() { + return Long.toHexString(hash); + } + + public long getLastModified() { + return lastModified; + } +} diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java index 236455acb..c2fcc3f5b 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java @@ -14,6 +14,12 @@ import java.util.Map; /** Cache for ASM fragments. The cache remembers all synthesized fragments allowing for faster access after the first synthesis. */ public class AsmFragmentTemplateCache { + /** Hash and lastModify information for the fragment system. */ + private AsmFragmentSystemHash fragmentSystemHash; + + /** Name of the sub-folder holding the fragment cache. */ + public static final String CACHE_FOLDER_NAME = "cache"; + /** The folder containing cached fragment files. */ private Path cacheFolder; @@ -26,9 +32,10 @@ public class AsmFragmentTemplateCache { /** Detects any modification of the cache. */ private boolean modified; - public AsmFragmentTemplateCache(Path cacheFolder, TargetCpu cpu, Map cache) { + AsmFragmentTemplateCache(Path cacheFolder, TargetCpu cpu, AsmFragmentSystemHash fragmentSystemHash, Map cache) { this.cacheFolder = cacheFolder; this.cpu = cpu; + this.fragmentSystemHash = fragmentSystemHash; this.cache = cache; this.modified = false; } @@ -36,7 +43,10 @@ public class AsmFragmentTemplateCache { /** Special singleton representing that the fragment can not be synthesized or loaded. */ public static AsmFragmentTemplate NO_SYNTHESIS = new AsmFragmentTemplate("NO_SYNTHESIS", "NO_SYNTHESIS", false); - /** The prefix for header.lines in the fragment cache file. */ + /** Prefix for the fragment hash file header. */ + public static final String HASH_HEADER = "//KICKC FRAGMENT CACHE "; + + /** The prefix for fragment header lines in the fragment cache file. */ public static final String FRAGMENT_HEADER = "//FRAGMENT "; /** @@ -68,25 +78,57 @@ public class AsmFragmentTemplateCache { return "fragment-cache-" + cpu.getName() + ".asm"; } + /** + * Creates an empty memory-only fragment cache + * + * @param cpu The CPU to make a cache for + * @return The new empty cache + */ + public static AsmFragmentTemplateCache memory(TargetCpu cpu) { + return new AsmFragmentTemplateCache(null, cpu, new AsmFragmentSystemHash(0, 0), new LinkedHashMap<>()); + } + /** * Attempt to load a fragment cache containing all best synthesized fragments * - * @param cacheFolder Folder containing the cache + * @param baseFragmentFolder Folder containing fragments. (The cache is localed in the sub-folder named "cache) * @param log The compile log * @return The map with all best fragments from the cache file. null if the cache file is not found. */ - public static AsmFragmentTemplateCache load(Path cacheFolder, TargetCpu cpu, CompileLog log) { + public static AsmFragmentTemplateCache load(TargetCpu cpu, Path baseFragmentFolder, CompileLog log) { + final AsmFragmentSystemHash fragmentSystemHash = AsmFragmentSystemHash.calculate(baseFragmentFolder); + Path cacheFolder = baseFragmentFolder.resolve(CACHE_FOLDER_NAME); final Date before = new Date(); - if(cacheFolder == null) { - return new AsmFragmentTemplateCache(null, cpu, new LinkedHashMap<>()); + if(!cacheFolder.toFile().exists()) { + if(log.isVerboseFragmentLog()) + log.append("Creating fragment cache folder " + cacheFolder.toAbsolutePath().toString()); + cacheFolder.toFile().mkdir(); + return new AsmFragmentTemplateCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>()); } try { File cacheFile = cacheFolder.resolve(getCacheFileName(cpu)).toFile(); if(!cacheFile.exists()) { - return new AsmFragmentTemplateCache(cacheFolder, cpu, new LinkedHashMap<>()); + return new AsmFragmentTemplateCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>()); + } + if(cacheFile.lastModified() < fragmentSystemHash.getLastModified()) { + if(log.isVerboseFragmentLog()) + log.append("Deleting outdated fragment cache file " + cacheFile.getAbsolutePath()); + cacheFile.delete(); + return new AsmFragmentTemplateCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>()); } LinkedHashMap cache = new LinkedHashMap<>(); BufferedReader fragmentCacheReader = new BufferedReader(new FileReader(cacheFile)); + String hashLine = fragmentCacheReader.readLine(); + final String hash = hashLine.substring(HASH_HEADER.length()); + // Compare cache file hash with fragment system hash + if(!hash.equals(fragmentSystemHash.getHashString())) { + // Cache file hash does not match fragment system hash + if(log.isVerboseFragmentLog()) + log.append("Deleting hash mismatch fragment cache file " + cacheFile.getAbsolutePath()); + cacheFile.delete(); + return new AsmFragmentTemplateCache(cacheFolder, cpu, fragmentSystemHash, new LinkedHashMap<>()); + } + // Read the first "real" line String cacheLine = fragmentCacheReader.readLine(); StringBuilder body = null; String signature = null; @@ -112,7 +154,7 @@ public class AsmFragmentTemplateCache { final long millis = after.getTime() - before.getTime(); if(log.isVerboseFragmentLog()) log.append("Loaded cached fragments " + cache.size() + " from " + cacheFile.getPath() + " in " + millis + "ms"); - return new AsmFragmentTemplateCache(cacheFolder, cpu, cache); + return new AsmFragmentTemplateCache(cacheFolder, cpu, fragmentSystemHash, cache); } catch(IOException e) { throw new RuntimeException("Error loading fragment cache file " + cacheFolder, e); } catch(StringIndexOutOfBoundsException e) { @@ -145,10 +187,11 @@ public class AsmFragmentTemplateCache { File cacheFile = this.cacheFolder.resolve(getCacheFileName(cpu)).toFile(); try { PrintStream fragmentFilePrint = new PrintStream(cacheFile); + fragmentFilePrint.println(HASH_HEADER + fragmentSystemHash.getHashString()); for(String signature : this.cache.keySet()) { AsmFragmentTemplate fragmentTemplate = this.cache.get(signature); fragmentFilePrint.println(FRAGMENT_HEADER + signature); - if(fragmentTemplate==NO_SYNTHESIS) { + if(fragmentTemplate == NO_SYNTHESIS) { fragmentFilePrint.println(NO_SYNTHESIS.getBody()); } else { if(fragmentTemplate.getBody() != null) diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateMasterSynthesizer.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateMasterSynthesizer.java index 58a8981c8..70dd45077 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateMasterSynthesizer.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateMasterSynthesizer.java @@ -14,8 +14,8 @@ public class AsmFragmentTemplateMasterSynthesizer { /** Fragment base folder. */ private final Path baseFragmentFolder; - /** Fragment cache folder. */ - private final Path cacheFolder; + /** Use the fragment synthesis cache */ + private boolean useFragmentCache; /** Compile Log. */ private CompileLog log; @@ -23,9 +23,9 @@ public class AsmFragmentTemplateMasterSynthesizer { private Map synthesizers; /** Create master synthesizer. */ - public AsmFragmentTemplateMasterSynthesizer(Path baseFragmentFolder, Path cacheFolder, CompileLog log) { + public AsmFragmentTemplateMasterSynthesizer(Path baseFragmentFolder, boolean useFragmentCache, CompileLog log) { this.baseFragmentFolder = baseFragmentFolder; - this.cacheFolder = cacheFolder; + this.useFragmentCache = useFragmentCache; this.log = log; this.synthesizers = new LinkedHashMap<>(); } @@ -33,7 +33,7 @@ public class AsmFragmentTemplateMasterSynthesizer { public AsmFragmentTemplateSynthesizer getSynthesizer(TargetCpu targetCpu) { AsmFragmentTemplateSynthesizer synthesizer = synthesizers.get(targetCpu); if(synthesizer==null) { - synthesizer = new AsmFragmentTemplateSynthesizer(baseFragmentFolder, targetCpu, cacheFolder, log); + synthesizer = new AsmFragmentTemplateSynthesizer(targetCpu, baseFragmentFolder, useFragmentCache, log); synthesizers.put(targetCpu, synthesizer); } return synthesizer; diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesizer.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesizer.java index 3d8475d10..987fb3d23 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesizer.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesizer.java @@ -26,12 +26,15 @@ import java.util.*; public class AsmFragmentTemplateSynthesizer { /** Create synthesizer. */ - public AsmFragmentTemplateSynthesizer(Path baseFragmentFolder, TargetCpu cpu, Path cacheFolder, CompileLog log) { + public AsmFragmentTemplateSynthesizer(TargetCpu cpu, Path baseFragmentFolder, boolean useFragmentCache, CompileLog log) { this.baseFragmentFolder = baseFragmentFolder; this.cpu = cpu; this.synthesisGraph = new LinkedHashMap<>(); this.bestTemplateUpdate = new ArrayDeque<>(); - this.fragmentCache = AsmFragmentTemplateCache.load(cacheFolder, cpu, log); + if(useFragmentCache) + this.fragmentCache = AsmFragmentTemplateCache.load(cpu, baseFragmentFolder, log); + else + this.fragmentCache = AsmFragmentTemplateCache.memory(cpu); } /** The folder containing fragment files. */ diff --git a/src/main/java/dk/camelot64/kickc/model/Program.java b/src/main/java/dk/camelot64/kickc/model/Program.java index ab3e8de4e..402af9064 100644 --- a/src/main/java/dk/camelot64/kickc/model/Program.java +++ b/src/main/java/dk/camelot64/kickc/model/Program.java @@ -35,8 +35,6 @@ public class Program { /** Base folder for finding ASM fragment files. (STATIC) */ private Path asmFragmentBaseFolder; - /** Cache folder for finding ASM fragment files. (STATIC) */ - private Path asmFragmentCacheFolder; /** The ASM fragment synthesizer responsible for loading/synthesizing ASM fragments. Depends on the target CPU. (STATIC) */ private AsmFragmentTemplateMasterSynthesizer asmFragmentMasterSynthesizer; @@ -192,14 +190,6 @@ public class Program { this.warnArrayType = errorArrayKickC; } - public Path getAsmFragmentCacheFolder() { - return asmFragmentCacheFolder; - } - - public void setAsmFragmentCacheFolder(Path asmFragmentCacheFolder) { - this.asmFragmentCacheFolder = asmFragmentCacheFolder; - } - public Path getAsmFragmentBaseFolder() { return asmFragmentBaseFolder; } @@ -212,8 +202,8 @@ public class Program { return asmFragmentMasterSynthesizer; } - public void initAsmFragmentMasterSynthesizer() { - this.asmFragmentMasterSynthesizer = new AsmFragmentTemplateMasterSynthesizer(asmFragmentBaseFolder, asmFragmentCacheFolder, getLog()); + public void initAsmFragmentMasterSynthesizer(boolean useFragmentCache) { + this.asmFragmentMasterSynthesizer = new AsmFragmentTemplateMasterSynthesizer(asmFragmentBaseFolder, useFragmentCache, getLog()); } public TargetPlatform getTargetPlatform() { diff --git a/src/test/java/dk/camelot64/kickc/test/TestFragments.java b/src/test/java/dk/camelot64/kickc/test/TestFragments.java index 3411406b4..3a06ae5a0 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestFragments.java +++ b/src/test/java/dk/camelot64/kickc/test/TestFragments.java @@ -24,7 +24,7 @@ public class TestFragments { @BeforeClass public static void setUp() { - asmFragmentTemplateSynthesizer = new AsmFragmentTemplateSynthesizer(new File("src/main/fragment/").toPath(), TargetCpu.MOS6502X, null, new CompileLog()); + asmFragmentTemplateSynthesizer = new AsmFragmentTemplateSynthesizer(TargetCpu.MOS6502X, new File("src/main/fragment/").toPath(), false, new CompileLog()); } @AfterClass @@ -193,7 +193,7 @@ public class TestFragments { */ private void testFragmentExists(String signature) { CompileLog log = new CompileLog(); - asmFragmentTemplateSynthesizer = new AsmFragmentTemplateSynthesizer(new File("src/main/fragment/").toPath(), TargetCpu.MOS6502X, null, new CompileLog()); + asmFragmentTemplateSynthesizer = new AsmFragmentTemplateSynthesizer(TargetCpu.MOS6502X, new File("src/main/fragment/").toPath(), false, new CompileLog()); log.setSysOut(true); //log.setVerboseFragmentLog(true); List templates = @@ -211,7 +211,7 @@ public class TestFragments { private void testFragments(String fileName, Collection signatures) throws IOException { CompileLog log = new CompileLog(); - asmFragmentTemplateSynthesizer = new AsmFragmentTemplateSynthesizer(new File("src/main/fragment/").toPath(), TargetCpu.MOS6502X, null, new CompileLog()); + asmFragmentTemplateSynthesizer = new AsmFragmentTemplateSynthesizer(TargetCpu.MOS6502X, new File("src/main/fragment/").toPath(), false, new CompileLog()); List sigs = new ArrayList<>(signatures); // Always test max 1000 signatures diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 6931e8ed9..d946c5851 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -4,7 +4,6 @@ import dk.camelot64.kickc.CompileLog; import dk.camelot64.kickc.Compiler; import dk.camelot64.kickc.SourceLoader; import dk.camelot64.kickc.asm.AsmProgram; -import dk.camelot64.kickc.fragment.AsmFragmentTemplateSynthesizer; import dk.camelot64.kickc.model.CompileError; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.TargetPlatform; @@ -4302,13 +4301,8 @@ public class TestPrograms { compileAndCompare("condition-type-mismatch.c"); } - static AsmFragmentTemplateSynthesizer asmFragmentSynthesizer; - @BeforeClass public static void setUp() { - //Path asmFragmentBaseFolder = new File("src/main/fragment/").toPath(); - //Path asmFragmentCacheFolder = new File("src/main/fragment/cache").toPath(); - //asmFragmentSynthesizer = new AsmFragmentTemplateSynthesizer(asmFragmentBaseFolder, TargetCpu.MOS6502X, asmFragmentCacheFolder, new CompileLog()); } @AfterClass @@ -4402,7 +4396,6 @@ public class TestPrograms { Compiler compiler = new Compiler(); compiler.setWarnFragmentMissing(true); compiler.setAsmFragmentBaseFolder(new File("src/main/fragment/").toPath()); - compiler.setAsmFragmentCacheFolder(new File("src/main/fragment/cache/").toPath()); if(compileLog != null) { compiler.setLog(compileLog); } @@ -4418,7 +4411,7 @@ public class TestPrograms { files.add(filePath); Program program = compiler.getProgram(); // Initialize the master ASM fragment synthesizer - program.initAsmFragmentMasterSynthesizer(); + program.initAsmFragmentMasterSynthesizer(true); final File platformFile = SourceLoader.loadFile(TargetPlatform.DEFAULT_NAME + "." + CTargetPlatformParser.FILE_EXTENSION, filePath, program.getTargetPlatformPaths()); final TargetPlatform targetPlatform = CTargetPlatformParser.parseTargetPlatformFile(TargetPlatform.DEFAULT_NAME, platformFile, filePath, program.getTargetPlatformPaths()); program.setTargetPlatform(targetPlatform);