From e9965580a47399f5d6de882ee9dca07fff59e3b9 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Wed, 3 Jan 2018 01:00:55 +0100 Subject: [PATCH] Iplemented graph based synthesis. Working on the kinks - eg. issue #75 --- .../java/dk/camelot64/kickc/CompileLog.java | 2 +- .../kickc/fragment/AsmFragmentClobber.java | 51 ++ .../kickc/fragment/AsmFragmentTemplate.java | 72 ++- .../fragment/AsmFragmentTemplateManager.java | 570 ------------------ .../AsmFragmentTemplateSynthesisRule.java | 466 ++++++++++++-- .../AsmFragmentTemplateSynthesizer.java | 471 +++++++++++++++ .../fragment/AsmFragmentTemplateUsages.java | 53 +- .../kickc/passes/Pass4CodeGeneration.java | 8 +- .../Pass4RegisterUpliftCombinations.java | 16 +- ...gisterUpliftPotentialRegisterAnalysis.java | 6 +- .../passes/Pass4RegisterUpliftRemains.java | 7 +- .../dk/camelot64/kickc/test/TestPrograms.java | 2 +- 12 files changed, 1028 insertions(+), 696 deletions(-) create mode 100644 src/main/java/dk/camelot64/kickc/fragment/AsmFragmentClobber.java delete mode 100644 src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateManager.java create mode 100644 src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesizer.java diff --git a/src/main/java/dk/camelot64/kickc/CompileLog.java b/src/main/java/dk/camelot64/kickc/CompileLog.java index 579113205..108e34535 100644 --- a/src/main/java/dk/camelot64/kickc/CompileLog.java +++ b/src/main/java/dk/camelot64/kickc/CompileLog.java @@ -35,7 +35,7 @@ public class CompileLog { /** * Should the log be output to System.out while being built */ - private boolean sysOut = false; + private boolean sysOut = true; public CompileLog() { this.log = new StringBuilder(); diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentClobber.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentClobber.java new file mode 100644 index 000000000..59331270c --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentClobber.java @@ -0,0 +1,51 @@ +package dk.camelot64.kickc.fragment; + +import dk.camelot64.kickc.asm.AsmClobber; + +/** The clobber profile for a fragment template. Only distinguishes the 3 registers A/X/Y and not the flags. */ +public class AsmFragmentClobber { + + private boolean clobberA; + private boolean clobberX; + private boolean clobberY; + + public AsmFragmentClobber(boolean clobberA, boolean clobberX, boolean clobberY) { + this.clobberA = clobberA; + this.clobberX = clobberX; + this.clobberY = clobberY; + } + + public AsmFragmentClobber(AsmClobber clobber) { + this(clobber.isClobberA(), clobber.isClobberX(), clobber.isClobberY()); + } + + public boolean isClobberA() { + return clobberA; + } + + public boolean isClobberX() { + return clobberX; + } + + public boolean isClobberY() { + return clobberY; + } + + @Override + public boolean equals(Object o) { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + AsmFragmentClobber that = (AsmFragmentClobber) o; + if(clobberA != that.clobberA) return false; + if(clobberX != that.clobberX) return false; + return clobberY == that.clobberY; + } + + @Override + public int hashCode() { + int result = (clobberA ? 1 : 0); + result = 31 * result + (clobberX ? 1 : 0); + result = 31 * result + (clobberY ? 1 : 0); + return result; + } +} diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java index 1db93c6e6..584833933 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java @@ -1,9 +1,14 @@ package dk.camelot64.kickc.fragment; +import dk.camelot64.kickc.asm.AsmClobber; +import dk.camelot64.kickc.asm.AsmProgram; +import dk.camelot64.kickc.model.*; import dk.camelot64.kickc.parser.KickCLexer; import dk.camelot64.kickc.parser.KickCParser; import org.antlr.v4.runtime.*; +import java.util.LinkedHashMap; + /** * An ASM fragment template usable for generating KickAssembler code for different bindings. * The AsmFragmentTemplateSynthesizer can generate multiple different templates usable for a specific fragment signature. @@ -16,14 +21,18 @@ public class AsmFragmentTemplate { private String signature; /** The fragment template body */ private String body; - /** The parsed ASM lines. Initially null. Will be non-null, is the template is ever used to generate ASM code. */ - private KickCParser.AsmLinesContext bodyAsm; /** The synthesis that created the fragment. null if the fragment template was loaded. */ private AsmFragmentTemplateSynthesisRule synthesis; - /** The sub fragment template that the synthesis modified to create this. null if the fragment template was loaded. */ private AsmFragmentTemplate subFragment; + /** The parsed ASM lines. Initially null. Will be non-null, is the template is ever used to generate ASM code. */ + private KickCParser.AsmLinesContext bodyAsm; + /** The ASM clobber of the fragment. */ + private AsmFragmentClobber clobber; + /** The cycles consumed by the ASM of the fragment. */ + private Double cycles; + public AsmFragmentTemplate(String signature, String body) { this.signature = signature; this.body = body; @@ -49,25 +58,50 @@ public class AsmFragmentTemplate { } /** - * Parse an ASM fragment. + * Initialize the fields that require parsing the ASM (bodyAsm, clobber, clobber). * - * @param fragmentBody The stream containing the fragment syntax - * @param fragmentFileName The filename (used in error messages) * @return The parsed fragment ready for generating */ - private static KickCParser.AsmLinesContext parseBody(String fragmentBody, final String fragmentFileName) { - CodePointCharStream fragmentCharStream = CharStreams.fromString(fragmentBody); + private void initAsm() { + // Parse the body ASM + CodePointCharStream fragmentCharStream = CharStreams.fromString(body); KickCLexer kickCLexer = new KickCLexer(fragmentCharStream); KickCParser kickCParser = new KickCParser(new CommonTokenStream(kickCLexer)); kickCParser.addErrorListener(new BaseErrorListener() { @Override public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { - throw new RuntimeException("Error parsing fragment " + fragmentFileName + "\n - Line: " + line + "\n - Message: " + msg); + throw new RuntimeException("Error parsing fragment " + signature + "\n - Line: " + line + "\n - Message: " + msg); } }); kickCParser.setBuildParseTree(true); - KickCParser.AsmFileContext asmFile = kickCParser.asmFile(); - return asmFile.asmLines(); + this.bodyAsm = kickCParser.asmFile().asmLines(); + // Generate a dummy instance to find clobber & cycles + ProgramScope scope = new ProgramScope(); + LinkedHashMap bindings = new LinkedHashMap<>(); + VariableVersion v1 = new VariableVersion("$tmp1", SymbolType.BYTE, null); + VariableVersion v2 = new VariableVersion("$tmp2", SymbolType.BYTE, null); + VariableVersion v3 = new VariableVersion("$tmp3", SymbolType.BYTE, null); + v1.setScope(scope); + v2.setScope(scope); + v3.setScope(scope); + v1.setAllocation(new Registers.RegisterZpByte(2)); + v2.setAllocation(new Registers.RegisterZpByte(4)); + v3.setAllocation(new Registers.RegisterZpByte(6)); + if(signature.contains("z1")) bindings.put("z1", v1); + if(signature.contains("z2")) bindings.put("z2", v2); + if(signature.contains("z3")) bindings.put("z3", v3); + if(signature.contains("c1")) bindings.put("c1", new ConstantInteger(10)); + if(signature.contains("c2")) bindings.put("c2", new ConstantInteger(20)); + if(signature.contains("c3")) bindings.put("c3", new ConstantInteger(30)); + if(signature.contains("la1")) bindings.put("la1", new Label("@1", scope, true)); + AsmFragmentInstance fragmentInstance = + new AsmFragmentInstance(new Program(), signature, ScopeRef.ROOT, this, bindings); + AsmProgram asm = new AsmProgram(); + asm.startSegment(null, signature); + fragmentInstance.generate(asm); + AsmClobber asmClobber = asm.getClobber(); + this.clobber = new AsmFragmentClobber(asmClobber); + this.cycles = asm.getCycles(); } public String getSignature() { @@ -80,11 +114,25 @@ public class AsmFragmentTemplate { public KickCParser.AsmLinesContext getBodyAsm() { if(bodyAsm == null) { - bodyAsm = parseBody(body, signature); + initAsm(); } return bodyAsm; } + public AsmFragmentClobber getClobber() { + if(clobber == null) { + initAsm(); + } + return clobber; + } + + public double getCycles() { + if(cycles == null) { + initAsm(); + } + return cycles; + } + public boolean isFile() { return file; } diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateManager.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateManager.java deleted file mode 100644 index e94452ecf..000000000 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateManager.java +++ /dev/null @@ -1,570 +0,0 @@ -package dk.camelot64.kickc.fragment; - -import dk.camelot64.kickc.CompileLog; -import dk.camelot64.kickc.asm.AsmProgram; -import org.antlr.v4.runtime.CharStream; -import org.antlr.v4.runtime.CharStreams; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.*; - -/** - * Provides fragments from their signature. - *

- * The first priority is loading a fragment file from the fragment-folder. - * If no fragment file is found for the signature the manager attempts to synthesise a fragment from another fragment. - */ -public class AsmFragmentTemplateManager { - - /** Cache for the best fragment templates. Maps signature to the best fragment template for the signature. */ - private static Map bestFragmentCache = new HashMap<>(); - - /** Caches all asm fragment templates for all encountered signatures. */ - private static Map> fragmentTemplateCache = new LinkedHashMap<>(); - - /** Special singleton representing that the fragment can not be synthesized or loaded. */ - private static AsmFragmentTemplate UNKNOWN = new AsmFragmentTemplate("UNKNOWN", null); - /** - * All the synthesize rules available. - */ - private static List fragmentSyntheses; - - static Map> getFragmentTemplateCache() { - return fragmentTemplateCache; - } - - public static AsmFragmentInstance getFragment(AsmFragmentInstanceSpec instanceSpec, CompileLog log) { - AsmFragmentTemplate bestTemplate = bestFragmentCache.get(instanceSpec.getSignature()); - if(bestTemplate == UNKNOWN) { - if(log.isVerboseFragmentLog()) { - log.append("Unknown fragment " + instanceSpec.getSignature()); - } - throw new UnknownFragmentException(instanceSpec); - } - if(bestTemplate == null) { - AsmFragmentTemplateSynthesizer synthesizer = new AsmFragmentTemplateSynthesizer(instanceSpec.getSignature(), log); - List candidates = synthesizer.loadOrSynthesizeFragment(instanceSpec.getSignature(), new AsmSynthesisPath()); - if(candidates.size() == 0) { - if(log.isVerboseFragmentLog()) { - log.append("Unknown fragment " + instanceSpec.getSignature()); - } - bestFragmentCache.put(instanceSpec.getSignature(), UNKNOWN); - throw new UnknownFragmentException(instanceSpec); - } - double minScore = Double.MAX_VALUE; - double maxScore = Double.MIN_VALUE; - AsmFragmentTemplate maxTemplate = null; - for(AsmFragmentTemplate candidateTemplate : candidates) { - AsmFragmentInstance candidateFragment = new AsmFragmentInstance( - instanceSpec.getProgram(), - instanceSpec.getSignature(), - instanceSpec.getCodeScope(), - candidateTemplate, - instanceSpec.getBindings()); - AsmProgram candidateAsm = new AsmProgram(); - candidateAsm.startSegment(null, instanceSpec.toString()); - candidateFragment.generate(candidateAsm); - double score = candidateAsm.getCycles(); - if(score < minScore) { - minScore = score; - bestTemplate = candidateTemplate; - } - if(score > maxScore) { - maxScore = score; - maxTemplate = candidateTemplate; - } - } - if(log.isVerboseFragmentLog()) { - log.append("Found fragment " + bestTemplate.getName() + " score: " + minScore + " from " + candidates.size() + " candidates"); - } - bestFragmentCache.put(instanceSpec.getSignature(), bestTemplate); - } - // Count usages - AsmFragmentTemplateUsages.incUsage(bestTemplate); - // Return the resulting fragment instance - return new AsmFragmentInstance( - instanceSpec.getProgram(), - instanceSpec.getSignature(), - instanceSpec.getCodeScope(), - bestTemplate, - instanceSpec.getBindings()); - } - - /** - * Look for a fragment on the disk. - * - * @param signature The fragment signature - * @return The fragment file contents. Null if the fragment is not on the disk. - */ - private static CharStream loadFragment(String signature) { - ClassLoader classLoader = AsmFragmentTemplateManager.class.getClassLoader(); - URL fragmentUrl = classLoader.getResource("dk/camelot64/kickc/fragment/asm/" + signature + ".asm"); - if(fragmentUrl == null) { - return null; - } - try { - InputStream fragmentStream = fragmentUrl.openStream(); - return CharStreams.fromStream(fragmentStream); - } catch(IOException e) { - throw new RuntimeException("Error loading fragment file " + fragmentUrl); - } - } - - static File[] allFragmentFiles() { - ClassLoader classLoader = AsmFragmentTemplateManager.class.getClassLoader(); - String path = classLoader.getResource("dk/camelot64/kickc/fragment/asm/").getPath(); - return new File(path).listFiles((dir, name) -> name.endsWith(".asm")); - - } - - private static List getFragmentSyntheses() { - if(fragmentSyntheses == null) { - fragmentSyntheses = initFragmentSyntheses(); - } - return fragmentSyntheses; - } - - private static List initFragmentSyntheses() { - Map mapZ = new LinkedHashMap<>(); - mapZ.put("z2", "z1"); - mapZ.put("z3", "z2"); - Map mapZ2 = new LinkedHashMap<>(); - mapZ2.put("z3", "z1"); - Map mapZ3 = new LinkedHashMap<>(); - mapZ3.put("z3", "z2"); - Map mapC = new LinkedHashMap<>(); - mapC.put("c2", "c1"); - mapC.put("c3", "c2"); - Map mapC3 = new LinkedHashMap<>(); - mapC3.put("c3", "c2"); - Map mapZC = new LinkedHashMap<>(); - mapZC.putAll(mapZ); - mapZC.putAll(mapC); - Map mapSToU = new LinkedHashMap<>(); - mapSToU.put("vbsz1", "vbuz1"); - mapSToU.put("vbsz2", "vbuz2"); - mapSToU.put("vbsz3", "vbuz3"); - mapSToU.put("vbsc1", "vbuc1"); - mapSToU.put("vbsc2", "vbuc2"); - mapSToU.put("vbsc3", "vbuc3"); - mapSToU.put("vbsaa", "vbuaa"); - mapSToU.put("vbsxx", "vbuxx"); - mapSToU.put("vbsyy", "vbuyy"); - mapSToU.put("vwsz1", "vwuz1"); - mapSToU.put("vwsz2", "vwuz2"); - mapSToU.put("vwsz3", "vwuz3"); - mapSToU.put("vwsc1", "vwuc1"); - mapSToU.put("vwsc2", "vwuc2"); - mapSToU.put("vwsc3", "vwuc3"); - - List synths = new ArrayList<>(); - - // NEW STYLE REWRITES - Utilizes that all combinations are tried - - // Replace first AA with XX - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)aa(.*)", "...aa=.*|.*xx.*", "tax", "$1xx$2", null, null)); - // Replace two AAs with XX - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)aa(.*vb.)aa(.*)", "...aa=.*|.*xx.*", "tax", "$1xx$2xx$3", null, null)); - // Replace second (not first) AA with XX - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)aa(.*vb.)aa(.*)", "...aa=.*|.*xx.*", "tax", "$1aa$2xx$3", null, null)); - - // Replace first AA with YY - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)aa(.*)", "...aa=.*|.*yy.*", "tay", "$1yy$2", null, null)); - // Replace two AAs with YY - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)aa(.*vb.)aa(.*)", "...aa=.*|.*yy.*", "tay", "$1yy$2yy$3", null, null)); - // Replace second (not first) AA with YY - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)aa(.*vb.)aa(.*)", "...aa=.*|.*yy.*", "tay", "$1aa$2yy$3", null, null)); - - // Replace first XX with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)xx(.*)", "...xx=.*|.*aa.*", "txa", "$1aa$2", null, null)); - // Replace two XXs with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)xx(.*vb.)xx(.*)", "...xx=.*|.*aa.*", "txa", "$1aa$2aa$3", null, null)); - // Replace second (not first) XX with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)xx(.*vb.)xx(.*)", "...xx=.*|.*aa.*", "txa", "$1xx$2aa$3", null, null)); - - // Replace first YY with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)yy(.*)", "...yy=.*|.*aa.*", "tya", "$1aa$2", null, null)); - // Replace two YYs with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)yy(.*vb.)yy(.*)", "...yy=.*|.*aa.*", "tya", "$1aa$2aa$3", null, null)); - // Replace second (not first) YY with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)yy(.*vb.)yy(.*)", "...yy=.*|.*aa.*", "tya", "$1yy$2aa$3", null, null)); - - // Replace Z1 with AA (only one) - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*|.*aa.*", "lda {z1}", "$1aa$2", null, mapZ)); - // Replace two Z1s with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*z1.*|.*aa.*", "lda {z1}", "$1aa$2aa$3", null, mapZ)); - // Replace first (not second) Z1 with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)z1(.*)", "...z1=.*|.*aa.*", "lda {z1}", "$1aa$2z1$3", null, null)); - // Replace second (not first) Z1 with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z1(.*vb.)z1(.*)", "...z1=.*|.*aa.*", "lda {z1}", "$1z1$2aa$3", null, null)); - - // Replace Z1 with YY (only one) - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*|.*yy.*", "ldy {z1}", "$1yy$2", null, mapZ)); - // Replace two Z1s with YY - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*z1.*|.*yy.*", "ldy {z1}", "$1yy$2yy$3", null, mapZ)); - // Replace first (not second) Z1 with YY - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)z1(.*)", "...z1=.*|.*yy.*", "ldy {z1}", "$1yy$2z1$3", null, null)); - // Replace second (not first) Z1 with YY - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z1(.*vb.)z1(.*)", "...z1=.*|.*yy.*", "ldy {z1}", "$1z1$2yy$3", null, null)); - - // Replace Z1 with XX (only one) - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*|.*xx.*", "ldx {z1}", "$1xx$2", null, mapZ)); - // Replace two Z1s with XX - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*z1.*|.*xx.*", "ldx {z1}", "$1xx$2xx$3", null, mapZ)); - // Replace first (not second) Z1 with XX - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)z1(.*)", "...z1=.*|.*xx.*", "ldx {z1}", "$1xx$2z1$3", null, null)); - // Replace second (not first) Z1 with XX - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z1(.*vb.)z1(.*)", "...z1=.*|.*xx.*", "ldx {z1}", "$1z1$2xx$3", null, null)); - - // Replace Z2 with AA (only one) - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*|.*aa.*", "lda {z2}", "$1aa$2", null, mapZ3)); - // Replace two Z2s with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*z2.*|.*aa.*", "lda {z2}", "$1aa$2aa$3", null, mapZ3)); - // Replace first (of 2) Z2 with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)z2(.*)", "...z2=.*|.*aa.*", "lda {z2}", "$1aa$2z2$3", null, null)); - // Replace second (of 2) Z2 with AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z2(.*vb.)z2(.*)", "...z2=.*|.*aa.*", "lda {z2}", "$1z2$2aa$3", null, null)); - - // Replace Z2 with YY (only one) - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*|.*yy.*", "ldy {z2}", "$1yy$2", null, mapZ3)); - // Replace two Z2s with YY - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*z2.*|.*yy.*", "ldy {z2}", "$1yy$2yy$3", null, mapZ3)); - // Replace first (of 2) Z2 with YY - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)z2(.*)", "...z2=.*|.*yy.*", "ldy {z2}", "$1yy$2z2$3", null, null)); - // Replace second (of 2) Z2 with YY - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z2(.*vb.)z2(.*)", "...z2=.*|.*yy.*", "ldy {z2}", "$1z2$2yy$3", null, null)); - - // Replace Z2 with XX(only one) - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*|.*xx.*", "ldx {z2}", "$1xx$2", null, mapZ3)); - // Replace two Z2s with XX - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*z2.*|.*xx.*", "ldx {z2}", "$1xx$2xx$3", null, mapZ3)); - // Replace first (of 2) Z2 with XX - synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)z2(.*)", "...z2=.*|.*xx.*", "ldx {z2}", "$1xx$2z2$3", null, null)); - // Replace second (of 2) Z2 with XX - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z2(.*vb.)z2(.*)", "...z2=.*|.*xx.*", "ldx {z2}", "$1z2$2xx$3", null, null)); - - // Rewrite comparisons < to > - //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_gt_(.*)_then_(.*)", null, null, "$2_lt_$1_then_$3", null, null)); - // Rewrite comparisons > to < - //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_lt_(.*)_then_(.*)", null, null, "$2_gt_$1_then_$3", null, null)); - // Rewrite comparisons <= to >= - //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_le_(.*)_then_(.*)", null, null, "$2_ge_$1_then_$3", null, null)); - // Rewrite comparisons >= to <= - //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_ge_(.*)_then_(.*)", null, null, "$2_le_$1_then_$3", null, null)); - // Rewrite comparisons swap == - //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_eq_(.*)_then_(.*)", null, null, "$2_eq_$1_then_$3", null, null)); - // Rewrite comparisons swap != - //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_neq_(.*)_then_(.*)", null, null, "$2_neq_$1_then_$3", null, null)); - - - // OLD STYLE REWRITES - written when only one rule could be taken - - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_(band|bor|bxor|plus)_(vb.aa)", ".*=vb.aa_.*", null, "$1=$4_$3_$2", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_(band|bor|bxor|plus)_(vb.xx)", ".*=vb.[ax][ax]_.*", null, "$1=$4_$3_$2", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_(band|bor|bxor|plus)_(vb.yy)", ".*=vb.[axy][axy]_.*", null, "$1=$4_$3_$2", null, null)); - - synths.add(new AsmFragmentTemplateSynthesisRule("vbuxx=(.*)", null, null, "vbuaa=$1", "tax\n", null)); - synths.add(new AsmFragmentTemplateSynthesisRule("vbsxx=(.*)", null, null, "vbsaa=$1", "tax\n", null)); - synths.add(new AsmFragmentTemplateSynthesisRule("vbuyy=(.*)", null, null, "vbuaa=$1", "tay\n", null)); - synths.add(new AsmFragmentTemplateSynthesisRule("vbsyy=(.*)", null, null, "vbsaa=$1", "tay\n", null)); - synths.add(new AsmFragmentTemplateSynthesisRule("vbuz1=(.*)", ".*=.*vb.z1.*", null, "vbuaa=$1", "sta {z1}\n", mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("vbsz1=(.*)", ".*=.*vb.z1.*", null, "vbsaa=$1", "sta {z1}\n", mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pb(.)c1=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1}\n", mapC)); - synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pb(.)c1=(.*c1.*)", null, null, "vb$1aa=$2", "sta {c1}\n", null)); - synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pbuz1=(.*)", ".*z1.*z1.*", null, "vbuaa=$1", "ldy #0\n" + "sta ({z1}),y\n", mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pbuz1=(.*z1.*)", null, null, "vbuaa=$1", "ldy #0\n" + "sta ({z1}),y\n", null)); - - synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuz1=(.*)", ".*z1.*z1.*|.*c1.*c1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapZC)); - synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuyy=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1},y\n", mapC)); - synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuxx=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1},x\n", mapC)); - synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)z1_derefidx_vbuz2=(.*)", ".*z1.*z1.*|.*z2.*z2.*", null, "vb$1aa=$2", "ldy {z2}\n" + "sta ({z1}),y\n", mapZ2)); - - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=_deref_pb(.)c1(.*)", ".*=.*aa.*", "lda {c1}\n", "$1=vb$2aa$3", null, mapC)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=_deref_pb(.)z1(.*)", ".*z1.*z1.*|.*=.*aa.*|.*=.*yy.*", "ldy #0\n" + "lda ({z1}),y\n", "$1=vb$2aa$3", null, mapZ)); - - // Convert array indexing with A register to X/Y register by prefixing tax/tay (..._derefidx_vbuaa... -> ..._derefidx_vbuxx... /... _derefidx_vbuyy... ) - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuaa(.*)", ".*=.*xx.*", "tax\n", "$1=$2_derefidx_vbuxx$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuaa(.*)", ".*=.*yy.*", "tay\n", "$1=$2_derefidx_vbuyy$3", null, null)); - // Convert array indexing with zero page to x/y register by prefixing ldx z1 / ldy z1 ( ..._derefidx_vbuzn... -> ..._derefidx_vbuxx... / ..._derefidx_vbuyy... ) - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz1(.*)", ".*=.*xx.*|.*z1.*z1.*", "ldx {z1}\n", "$1=$2_derefidx_vbuxx$3", null, mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz1(.*)", ".*=.*yy.*|.*z1.*z1.*", "ldy {z1}\n", "$1=$2_derefidx_vbuyy$3", null, mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz2(.*)", ".*=.*xx.*|.*z2.*z2.*", "ldx {z2}\n", "$1=$2_derefidx_vbuxx$3", null, mapZ3)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz2(.*)", ".*=.*yy.*|.*z2.*z2.*", "ldy {z2}\n", "$1=$2_derefidx_vbuyy$3", null, mapZ3)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz3(.*)", ".*=.*yy.*", "ldy {z3}\n", "$1=$2_derefidx_vbuyy$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz3(.*)", ".*=.*xx.*", "ldx {z3}\n", "$1=$2_derefidx_vbuxx$3", null, null)); - // Convert array indexing twice with A/zp1/zp2 to X/Y register with a ldx/ldy prefix ( ..._derefidx_vbunn..._derefidx_vbunn... -> ..._derefidx_vbuxx..._derefidx_vbuxx... ) - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuaa(.*)_derefidx_vbuaa(.*)", ".*aa.*aa.*aa.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "tax\n", null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuaa(.*)_derefidx_vbuaa(.*)", ".*aa.*aa.*aa.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "tay\n", null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz1(.*)_derefidx_vbuz1(.*)", ".*z1.*z1.*z1.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "ldx {z1}\n", mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz1(.*)_derefidx_vbuz1(.*)", ".*z1.*z1.*z1.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "ldy {z1}\n", mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz2(.*)_derefidx_vbuz2(.*)", ".*z2.*z2.*z2.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "ldx {z2}\n", mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz2(.*)_derefidx_vbuz2(.*)", ".*z2.*z2.*z2.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "ldy {z2}\n", mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuz1=(.*c1.*)", ".*z1.*z1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuz1=(.*z1.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapC)); - - // Convert X/Y-based array indexing of a constant pointer into A-register by prefixing lda cn,x / lda cn,y ( ...pb.c1_derefidx_vbuxx... / ...pb.c1_derefidx_vbuyy... -> ...vb.aa... ) - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c1_derefidx_vbuxx(.*)", ".*=.*aa.*|.*c1.*c1.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, mapC)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*c1.*)pb(.)c1_derefidx_vbuxx(.*)", ".*=.*aa.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c1_derefidx_vbuxx(.*c1.*)", ".*=.*aa.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c1_derefidx_vbuyy(.*)", ".*=.*aa.*|.*c1.*c1.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, mapC)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*c1.*)pb(.)c1_derefidx_vbuyy(.*)", ".*=.*aa.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c1_derefidx_vbuyy(.*c1.*)", ".*=.*aa.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c2_derefidx_vbuxx(.*)", ".*=.*aa.*|.*c2.*c2.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, mapC3)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*c2.*)pb(.)c2_derefidx_vbuxx(.*)", ".*=.*aa.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c2_derefidx_vbuxx(.*c2.*)", ".*=.*aa.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c2_derefidx_vbuyy(.*)", ".*=.*aa.*|.*c2.*c2.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, mapC3)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*c2.*)pb(.)c2_derefidx_vbuyy(.*)", ".*=.*aa.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c2_derefidx_vbuyy(.*c2.*)", ".*=.*aa.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, null)); - - // Convert zeropage/constants/X/Y in assignments to A-register using LDA/TXA/TYA prefix - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuz1(.*)", ".*z1.*=.*|.*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2vbuaa$3", null, mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsz1(.*)", ".*z1.*=.*|.*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2vbsaa$3", null, mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuz2(.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbuaa$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsz2(.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbsaa$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuz2(.*z3.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbuaa$3", null, mapZ3)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsz2(.*z3.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbsaa$3", null, mapZ3)); - - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbuxx", ".*=.*[ax][ax].*xx|.*derefidx_vb.xx", "txa\n", "$1=$2_vbuaa", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbsxx", ".*=.*[ax][ax].*xx|.*derefidx_vb.xx", "txa\n", "$1=$2_vbsaa", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbuyy", ".*=.*[ay][ay].*yy|.*derefidx_vb.yy", "tya\n", "$1=$2_vbuaa", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbsyy", ".*=-*[ay][ay].*yy|.*derefidx_vb.yy", "tya\n", "$1=$2_vbsaa", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbuz1", ".*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2_vbuaa", null, mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbsz1", ".*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2_vbsaa", null, mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbuz2", ".*=.*aa.*|.*z2.*z2.*", "lda {z2}\n", "$1=$2_vbuaa", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbuz3", ".*=.*aa.*|.*z3.*z3.*", "lda {z3}\n", "$1=$2_vbuaa", null, null)); - - synths.add(new AsmFragmentTemplateSynthesisRule("vbuz1=vbuz1(.*)", ".*=.*vb.aa.*|.*z1.*z1.*z1.*", "lda {z1}\n", "vbuaa=vbuaa$1", "sta {z1}\n", mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("vbsz1=vbsz1(.*)", ".*=.*vb.aa.*|.*z1.*z1.*z1.*", "lda {z1}\n", "vbsaa=vbsaa$1", "sta {z1}\n", mapZ)); - - synths.add(new AsmFragmentTemplateSynthesisRule("vbuz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*|.*z1.*z1.*", "lda {z1}\n", "vbuaa_$1_$2", null, mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("vbsz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*|.*z1.*z1.*", "lda {z1}\n", "vbsaa_$1_$2", null, mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pb(.)c1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*", "lda {c1}\n", "vb$1aa_$2_$3", null, mapC)); - synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pb(.)z1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*|.*vb.yy.*|.*z1.*z1.*", "ldy #0\n" + "lda ({z1}),y\n", "vb$1aa_$2_$3", null, mapZ)); - - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz1_(.*)", ".*z1.*z1.*|.*.yy.*", "ldy {z1}\n", "$1_derefidx_vbuyy_$2", null, mapZ)); - - - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*z1.*z1.*|.*vb.xx.*", "ldx {z1}\n", "$1_derefidx_vbuxx_$2_$3", null, mapZ)); - synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuyy_(lt|gt|le|ge|eq|neq)_(.*)", ".*c1.*c1.*|.*aa.*", "lda {c1},y\n", "vb$1aa_$2_$3", null, mapC)); - synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuyy_(lt|gt|le|ge|eq|neq)_(.*c1.*)", ".*aa.*", "lda {c1},y\n", "vb$1aa_$2_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuxx_(lt|gt|le|ge|eq|neq)_(.*)", ".*c1.*c1.*|.*aa.*", "lda {c1},x\n", "vb$1aa_$2_$3", null, mapC)); - synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuxx_(lt|gt|le|ge|eq|neq)_(.*c1.*)", ".*aa.*", "lda {c1},x\n", "vb$1aa_$2_$3", null, null)); - - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_ge_(vb.aa)_then_(.*)", ".*vb.aa.*_ge.*", null, "$2_le_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_ge_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_ge.*", null, "$2_le_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_ge_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_ge.*", null, "$2_le_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_lt_(vb.aa)_then_(.*)", ".*vb.aa.*_lt.*", null, "$2_gt_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_lt_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_lt.*", null, "$2_gt_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_lt_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_lt.*", null, "$2_gt_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_gt_(vb.aa)_then_(.*)", ".*vb.aa.*_gt.*", null, "$2_lt_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_gt_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_gt.*", null, "$2_lt_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_gt_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_gt.*", null, "$2_lt_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_le_(vb.aa)_then_(.*)", ".*vb.aa.*_le.*", null, "$2_ge_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_le_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_le.*", null, "$2_ge_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_le_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_le.*", null, "$2_ge_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_neq_(vb.aa)_then_(.*)", ".*vb.aa.*_neq.*", null, "$2_neq_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_neq_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_neq.*", null, "$2_neq_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_neq_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_neq.*", null, "$2_neq_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_eq_(vb.aa)_then_(.*)", ".*vb.aa.*_eq.*", null, "$2_eq_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_eq_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_eq.*", null, "$2_eq_$1_then_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_eq_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_eq.*", null, "$2_eq_$1_then_$3", null, null)); - - // Use unsigned ASM to synthesize signed ASM ( ...vbs... -> ...vbu... ) - synths.add(new AsmFragmentTemplateSynthesisRule("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_(eq|neq)_(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_then_(.*)", null, null, "$1_$2_$3_then_$4", null, mapSToU)); - synths.add(new AsmFragmentTemplateSynthesisRule("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)", null, null, "$1=$2", null, mapSToU)); - synths.add(new AsmFragmentTemplateSynthesisRule("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_(plus|band|bxor|bor)_(vbsz.|csoby.|vbsaa|vbsxx|vbsyy)", null, null, "$1=$2_$3_$4", null, mapSToU)); - synths.add(new AsmFragmentTemplateSynthesisRule("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=_(inc|dec)_(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)", null, null, "$1=_$2_$3", null, mapSToU)); - synths.add(new AsmFragmentTemplateSynthesisRule("(vwsz.|vwsc.)_(eq|neq)_(vwsz.|vwsc.)_then_(.*)", null, null, "$1_$2_$3_then_$4", null, mapSToU)); - synths.add(new AsmFragmentTemplateSynthesisRule("(vwsz.)=(vwsz.|vwsc.)", null, null, "$1=$2", null, mapSToU)); - synths.add(new AsmFragmentTemplateSynthesisRule("(v.sz.)=(v.s..)_(band|bxor|bor)_(v.s..)", null, null, "$1=$2_$3_$4", null, mapSToU)); - synths.add(new AsmFragmentTemplateSynthesisRule("(vbuz.|vbuaa|vbuxx|vbuyy)=_(lo|hi)_vws(z.|c.)", null, null, "$1=_$2_vwu$3", null, mapSToU)); - - // Use constant word ASM to synthesize unsigned constant byte ASM ( ...vb.c... -> vw.c... ) - synths.add(new AsmFragmentTemplateSynthesisRule("(vwuz.)=(vwuz.)_(plus|minus|band|bxor|bor)_vb.c(.)", null, null, "$1=$2_$3_vwuc$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(vwuz.)=vb.c(.)_(plus|minus|band|bxor|bor)_(vwuz.)", null, null, "$1=vwuc$2_$3_$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(vwsz.)=(vwsz.)_(plus|minus|band|bxor|bor)_vb.c(.)", null, null, "$1=$2_$3_vwsc$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(vwsz.)=vb.c(.)_(plus|minus|band|bxor|bor)_(vwsz.)", null, null, "$1=vwsc$2_$3_$4", null, null)); - - // Move constant words to the end of the ASM signature for symmetric operators ( ...vw.c...vw.z... -> ...vw.z...vw.c... ) - synths.add(new AsmFragmentTemplateSynthesisRule("(vwuz.)=(vwuc.)_(plus|band|bxor|bor)_(vwuz.)", null, null, "$1=$4_$3_$2", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(vwsz.)=(vwsc.)_(plus|band|bxor|bor)_(vwsz.)", null, null, "$1=$4_$3_$2", null, null)); - - // Use Z1/Z2 ASM to synthesize Z1-only code ( ...z1...z1... -> ...z1...z2... ) - synths.add(new AsmFragmentTemplateSynthesisRule("(v..)z1=(v..)z1_(plus|minus|band|bxor|bor)_(.*)", ".*z2.*", null, "$1z1=$2z2_$3_$4", null, mapZ, false)); - synths.add(new AsmFragmentTemplateSynthesisRule("(v..)z1=(.*)_(plus|minus|band|bxor|bor)_(v..)z1", ".*z2.*", null, "$1z1=$2_$3_$4z2", null, mapZ, false)); - synths.add(new AsmFragmentTemplateSynthesisRule("(v..)z1=_(neg|lo|hi)_(v..)z1", ".*z2.*", null, "$1z1=_$2_$3z2", null, mapZ, false)); - - // Convert INC/DEC to +1/-1 ( ..._inc_xxx... -> ...xxx_plus_1_... / ..._dec_xxx... -> ...xxx_minus_1_... ) - synths.add(new AsmFragmentTemplateSynthesisRule("vb(.)aa=_inc_(.*)", null, null, "vb$1aa=$2_plus_1", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("vb(.)aa=_dec_(.*)", null, null, "vb$1aa=$2_minus_1", null, null)); - - // Synthesize XX/YY using AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuxx(.*)", ".*=.*aa.*|.*derefidx_vb.xx.*", "txa\n", "$1=$2vbuaa$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsxx(.*)", ".*=.*aa.*|.*derefidx_vb.xx.*", "txa\n", "$1=$2vbsaa$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuyy(.*)", ".*=.*aa.*|.*derefidx_vb.yy.*", "tya\n", "$1=$2vbuaa$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsyy(.*)", ".*=.*aa.*|.*derefidx_vb.yy.*", "tya\n", "$1=$2vbsaa$3", null, null)); - // Synthesize constants using AA - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuc1(.*)", ".*=.*aa.*|.*c1.*c1.*|.*c1_deref.*", "lda #{c1}\n", "$1=$2vbuaa$3", null, mapC)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsc1(.*)", ".*=.*aa.*|.*c1.*c1.*|.*c1_deref.*", "lda #{c1}\n", "$1=$2vbsaa$3", null, mapC)); - - // Synthesize some constant pointers as constant words - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_(lt|gt|le|ge|eq|neq)_p..([cz].)_then_(.*)", null, null, "$1_$2_vwu$3_then_$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("p..([cz].)_(lt|gt|le|ge|eq|neq)_(.*)", null, null, "vwu$1_$2_$3", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([zc].)", null, null, "$1=vwu$2", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_(plus|minus|bor|bxor)_p..([cz].)", null, null, "$1=$2_$3_vwu$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([cz].)_(plus|minus|bor|bxor)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("p..([cz].)=(.*)_(sethi|setlo|plus|minus)_(.*)", null, null, "vwu$1=$2_$3_$4", null, null)); - synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([cz].)_(sethi|setlo|plus|minus)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null)); - - return synths; - } - - /** - * The synthesis path describes the different signatures being attempted to synthesize a fragment. - * Used to avoid infinite loops during synthesis. - */ - static class AsmSynthesisPath { - - private ArrayDeque signatures; - - public AsmSynthesisPath() { - this.signatures = new ArrayDeque<>(); - } - - private AsmSynthesisPath(ArrayDeque signatures) { - this.signatures = signatures; - } - - AsmSynthesisPath add(String signature) { - ArrayDeque signatures = new ArrayDeque<>(this.signatures); - signatures.add(signature); - return new AsmSynthesisPath(signatures); - } - - boolean has(String signature) { - return signatures.contains(signature); - } - - @Override - public String toString() { - StringBuilder str = new StringBuilder(); - boolean first = true; - for(String signature : signatures) { - if(first) { - first = false; - } else { - str.append(" < "); - } - str.append(signature); - } - return str.toString(); - } - } - - /** - * Capable of creating fragments from signatures by loading them or synthesizing them from other smaller fragments. - *

- * The synthesizer tries a lot of different combinations and keeps track of what has already been attempted. - */ - static class AsmFragmentTemplateSynthesizer { - - /** Signature of the fragment being synthesized. */ - private String creating; - - /** The log. */ - private CompileLog log; - - AsmFragmentTemplateSynthesizer(String creating, CompileLog log) { - this.creating = creating; - this.log = log; - } - - List loadOrSynthesizeFragment(String signature, AsmSynthesisPath path) { - if(path.has(signature)) { - // Synthesis loop - stop it here - if(log.isVerboseFragmentLog()) { - log.append("Finding fragment " + path.toString() + " - Stopping synthesis loop at " + signature); - } - return new ArrayList<>(); - } - // Add the current signature to the path - path = path.add(signature); - if(fragmentTemplateCache.get(signature) != null) { - if(log.isVerboseFragmentLog()) { - log.append("Finding fragment " + path.toString() + " - Using cached " + signature); - } - return fragmentTemplateCache.get(signature); - } - if(log.isVerboseFragmentLog()) { - log.append("Finding fragment " + path.toString() + " - Attempting " + signature); - } - List candidates = new ArrayList<>(); - // Synthesize the fragment from other fragments - List synths = getFragmentSyntheses(); - for(AsmFragmentTemplateSynthesisRule synth : synths) { - List synthesized = synth.synthesize(signature, path, this); - if(synthesized != null) { - if(log.isVerboseFragmentLog() && synthesized.size() > 0) { - log.append("Finding fragment " + path.toString() + " - Successfully synthesized " + synthesized.size() + " fragments "); - } - candidates.addAll(synthesized); - } - } - // Load the fragment from disk - CharStream fragmentCharStream = loadFragment(signature); - if(fragmentCharStream != null) { - try { - String body = fragmentCharStream.toString(); - candidates.add(new AsmFragmentTemplate(signature, body)); - - } catch(StringIndexOutOfBoundsException e) { - throw new RuntimeException("Problem reading fragment file " + signature, e); - } - if(log.isVerboseFragmentLog()) { - log.append("Finding fragment " + path.toString() + " - Successfully loaded " + signature + ".asm"); - } - } - if(candidates.size() == 0) { - if(log.isVerboseFragmentLog()) { - log.append("Finding fragment " + path.toString() + " - No synthesis/file found!"); - } - } - fragmentTemplateCache.put(signature, candidates); - return candidates; - } - - public String getCreating() { - return creating; - } - - public CompileLog getLog() { - return log; - } - } - - public static class UnknownFragmentException extends RuntimeException { - - private AsmFragmentInstanceSpec fragmentInstanceSpec; - - UnknownFragmentException(AsmFragmentInstanceSpec fragmentInstanceSpec) { - super("Fragment not found " + fragmentInstanceSpec.getSignature() ); - this.fragmentInstanceSpec = fragmentInstanceSpec; - } - - public String getFragmentSignature() { - return fragmentInstanceSpec.getSignature(); - } - - public String getFragmentDescription() { - return fragmentInstanceSpec.toString(); - } - - public AsmFragmentInstanceSpec getFragmentInstanceSpec() { - return fragmentInstanceSpec; - } - } - -} diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java index 66ae24214..3908d1dba 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java @@ -1,23 +1,21 @@ package dk.camelot64.kickc.fragment; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import dk.camelot64.kickc.CompileLog; + +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** AsmFragment synthesis mechanism based on matching fragment signature and reusing another fragment with added prefix/postfix and some bind-mappings */ class AsmFragmentTemplateSynthesisRule { - private String sigMatch; - private String sigAvoid; - private String asmPrefix; - private String sigReplace; - private String asmPostfix; - private Map bindMappings; - private boolean mapSignature; - private String subSignature; + final private String sigMatch; + final private String sigAvoid; + final private String asmPrefix; + final private String sigReplace; + final private String asmPostfix; + final private Map bindMappings; + final private boolean mapSignature; AsmFragmentTemplateSynthesisRule(String sigMatch, String sigAvoid, String asmPrefix, String sigReplace, String asmPostfix, Map bindMappings, boolean mapSignature) { this.sigMatch = sigMatch; @@ -29,75 +27,417 @@ class AsmFragmentTemplateSynthesisRule { this.mapSignature = mapSignature; } - public AsmFragmentTemplateSynthesisRule(String sigMatch, String sigAvoid, String asmPrefix, String sigReplace, String asmPostfix, Map bindMappings) { + AsmFragmentTemplateSynthesisRule(String sigMatch, String sigAvoid, String asmPrefix, String sigReplace, String asmPostfix, Map bindMappings) { this(sigMatch, sigAvoid, asmPrefix, sigReplace, asmPostfix, bindMappings, true); } + /** + * Is the rule match usable for synthesizing a fragment template + * + * @param signature The fragment template signature + * @return true if the rule matches the signature + */ + public boolean matches(String signature) { + return signature.matches(sigMatch) && (sigAvoid == null || !signature.matches(sigAvoid)); + } + + /** + * The signature of the sub-template to synthesize the template from + * + * @param signature The signature to synthesize + * @return Signature of the sub-template to synthesize the template from. null if the rule does not match the signature. + */ + public String getSubSignature(String signature) { + if(matches(signature)) { + String subSignature = regexpRewriteSignature(signature, sigMatch, sigReplace); + if(mapSignature && bindMappings != null) { + // When mapping the signature we do the map replacement in the signature + for(String bound : bindMappings.keySet()) { + subSignature = subSignature.replace(bound, bindMappings.get(bound)); + } + } + return subSignature; + } else { + return null; + } + } + + public AsmFragmentTemplate synthesize(String signature, AsmFragmentTemplate subTemplate) { + if(!matches(signature)) { + throw new RuntimeException("Synthesis error! Attempting to synthesize on non-matching signature signature:"+signature+" match:"+sigMatch+" avoid:"+sigAvoid); + } + if(!subTemplate.getSignature().equals(getSubSignature(signature))) { + throw new RuntimeException("Synthesis error! Attempting to synthesize on non-matching sub template sub-signature:"+subTemplate.getSignature()+" expecting:"+getSubSignature(signature)); + } + StringBuilder newFragment = new StringBuilder(); + if(asmPrefix != null) { + newFragment.append(asmPrefix).append("\n"); + } + String subFragment = subTemplate.getBody(); + if(bindMappings != null) { + if(mapSignature) { + // When mapping the signature we do the reverse replacement in the ASM + List reverse = new ArrayList<>(bindMappings.keySet()); + Collections.reverse(reverse); + for(String bound : reverse) { + subFragment = subFragment.replace("{" + bindMappings.get(bound) + "}", "{" + bound + "}"); + } + } else { + // When not mapping the signature we do the replacement directly in the ASM + for(String bound : bindMappings.keySet()) { + subFragment = subFragment.replace("{" + bound + "}", "{" + bindMappings.get(bound) + "}"); + } + } + } + newFragment.append(subFragment); + if(asmPostfix != null) { + newFragment.append("\n"); + newFragment.append(asmPostfix); + } + return new AsmFragmentTemplate(signature, newFragment.toString(), this, subTemplate); + } + static String regexpRewriteSignature(String signature, String match, String replace) { Pattern p = Pattern.compile(match); Matcher m = p.matcher(signature); String output = signature; if(m.find()) { - // getReplacement first number with "number" and second number with the first output = m.replaceAll(replace); } return output; } - public String getName() { - return sigMatch + (sigAvoid == null ? "" : ("/" + sigAvoid)); + @Override + public boolean equals(Object o) { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + + AsmFragmentTemplateSynthesisRule that = (AsmFragmentTemplateSynthesisRule) o; + + if(mapSignature != that.mapSignature) return false; + if(sigMatch != null ? !sigMatch.equals(that.sigMatch) : that.sigMatch != null) return false; + if(sigAvoid != null ? !sigAvoid.equals(that.sigAvoid) : that.sigAvoid != null) return false; + if(asmPrefix != null ? !asmPrefix.equals(that.asmPrefix) : that.asmPrefix != null) return false; + if(sigReplace != null ? !sigReplace.equals(that.sigReplace) : that.sigReplace != null) return false; + if(asmPostfix != null ? !asmPostfix.equals(that.asmPostfix) : that.asmPostfix != null) return false; + return bindMappings != null ? bindMappings.equals(that.bindMappings) : that.bindMappings == null; } - public List synthesize(String signature, AsmFragmentTemplateManager.AsmSynthesisPath path, AsmFragmentTemplateManager.AsmFragmentTemplateSynthesizer synthesizer) { - ArrayList candidates = new ArrayList<>(); - if(signature.matches(sigMatch)) { - if(sigAvoid == null || !signature.matches(sigAvoid)) { - subSignature = regexpRewriteSignature(signature, sigMatch, sigReplace); - if(mapSignature && bindMappings != null) { - // When mapping the signature we do the map replacement in the signature - for(String bound : bindMappings.keySet()) { - subSignature = subSignature.replace(bound, bindMappings.get(bound)); - } - } - List subFragmentTemplates = synthesizer.loadOrSynthesizeFragment(subSignature, path); - for(AsmFragmentTemplate subFragmentTemplate : subFragmentTemplates) { - if(subFragmentTemplate != null) { - StringBuilder newFragment = new StringBuilder(); - if(asmPrefix != null) { - newFragment.append(asmPrefix).append("\n"); - } - String subFragment = subFragmentTemplate.getBody(); - if(bindMappings != null) { - if(mapSignature) { - // When mapping the signature we do the reverse replacement in the ASM - List reverse = new ArrayList<>(bindMappings.keySet()); - Collections.reverse(reverse); - for(String bound : reverse) { - subFragment = subFragment.replace("{" + bindMappings.get(bound) + "}", "{" + bound + "}"); - } - } else { - // When not mapping the signature we do the replacement directly in the ASM - for(String bound : bindMappings.keySet()) { - subFragment = subFragment.replace("{" + bound + "}", "{" + bindMappings.get(bound) + "}"); - } - } - } - newFragment.append(subFragment); - if(asmPostfix != null) { - newFragment.append("\n"); - newFragment.append(asmPostfix); - } - candidates.add(new AsmFragmentTemplate(signature, newFragment.toString(), this, subFragmentTemplate)); - } - } - } + @Override + public int hashCode() { + int result = sigMatch != null ? sigMatch.hashCode() : 0; + result = 31 * result + (sigAvoid != null ? sigAvoid.hashCode() : 0); + result = 31 * result + (asmPrefix != null ? asmPrefix.hashCode() : 0); + result = 31 * result + (sigReplace != null ? sigReplace.hashCode() : 0); + result = 31 * result + (asmPostfix != null ? asmPostfix.hashCode() : 0); + result = 31 * result + (bindMappings != null ? bindMappings.hashCode() : 0); + result = 31 * result + (mapSignature ? 1 : 0); + return result; + } + + /** All the synthesize rules available. */ + private static List fragmentSyntheses; + + static List getSynthesisRules() { + if(fragmentSyntheses == null) { + fragmentSyntheses = initFragmentSyntheses(); } - return candidates; + return fragmentSyntheses; } - public String getSubSignature() { - return subSignature; - } + private static List initFragmentSyntheses() { + Map mapZ = new LinkedHashMap<>(); + mapZ.put("z2", "z1"); + mapZ.put("z3", "z2"); + Map mapZ2 = new LinkedHashMap<>(); + mapZ2.put("z3", "z1"); + Map mapZ3 = new LinkedHashMap<>(); + mapZ3.put("z3", "z2"); + Map mapC = new LinkedHashMap<>(); + mapC.put("c2", "c1"); + mapC.put("c3", "c2"); + Map mapC3 = new LinkedHashMap<>(); + mapC3.put("c3", "c2"); + Map mapZC = new LinkedHashMap<>(); + mapZC.putAll(mapZ); + mapZC.putAll(mapC); + Map mapSToU = new LinkedHashMap<>(); + mapSToU.put("vbsz1", "vbuz1"); + mapSToU.put("vbsz2", "vbuz2"); + mapSToU.put("vbsz3", "vbuz3"); + mapSToU.put("vbsc1", "vbuc1"); + mapSToU.put("vbsc2", "vbuc2"); + mapSToU.put("vbsc3", "vbuc3"); + mapSToU.put("vbsaa", "vbuaa"); + mapSToU.put("vbsxx", "vbuxx"); + mapSToU.put("vbsyy", "vbuyy"); + mapSToU.put("vwsz1", "vwuz1"); + mapSToU.put("vwsz2", "vwuz2"); + mapSToU.put("vwsz3", "vwuz3"); + mapSToU.put("vwsc1", "vwuc1"); + mapSToU.put("vwsc2", "vwuc2"); + mapSToU.put("vwsc3", "vwuc3"); + List synths = new ArrayList<>(); + + // NEW STYLE REWRITES - Utilizes that all combinations are tried + + // Replace first AA with XX + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)aa(.*)", "...aa=.*|.*xx.*", "tax", "$1xx$2", null, null)); + // Replace two AAs with XX + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)aa(.*vb.)aa(.*)", "...aa=.*|.*xx.*", "tax", "$1xx$2xx$3", null, null)); + // Replace second (not first) AA with XX + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)aa(.*vb.)aa(.*)", "...aa=.*|.*xx.*", "tax", "$1aa$2xx$3", null, null)); + + // Replace first AA with YY + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)aa(.*)", "...aa=.*|.*yy.*", "tay", "$1yy$2", null, null)); + // Replace two AAs with YY + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)aa(.*vb.)aa(.*)", "...aa=.*|.*yy.*", "tay", "$1yy$2yy$3", null, null)); + // Replace second (not first) AA with YY + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)aa(.*vb.)aa(.*)", "...aa=.*|.*yy.*", "tay", "$1aa$2yy$3", null, null)); + + // Replace first XX with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)xx(.*)", "...xx=.*|.*aa.*", "txa", "$1aa$2", null, null)); + // Replace two XXs with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)xx(.*vb.)xx(.*)", "...xx=.*|.*aa.*", "txa", "$1aa$2aa$3", null, null)); + // Replace second (not first) XX with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)xx(.*vb.)xx(.*)", "...xx=.*|.*aa.*", "txa", "$1xx$2aa$3", null, null)); + + // Replace first YY with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)yy(.*)", "...yy=.*|.*aa.*", "tya", "$1aa$2", null, null)); + // Replace two YYs with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)yy(.*vb.)yy(.*)", "...yy=.*|.*aa.*", "tya", "$1aa$2aa$3", null, null)); + // Replace second (not first) YY with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)yy(.*vb.)yy(.*)", "...yy=.*|.*aa.*", "tya", "$1yy$2aa$3", null, null)); + + // Replace Z1 with AA (only one) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*|.*aa.*", "lda {z1}", "$1aa$2", null, mapZ)); + // Replace two Z1s with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*z1.*|.*aa.*", "lda {z1}", "$1aa$2aa$3", null, mapZ)); + // Replace first (not second) Z1 with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)z1(.*)", "...z1=.*|.*aa.*", "lda {z1}", "$1aa$2z1$3", null, null)); + // Replace second (not first) Z1 with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z1(.*vb.)z1(.*)", "...z1=.*|.*aa.*", "lda {z1}", "$1z1$2aa$3", null, null)); + + // Replace Z1 with YY (only one) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*|.*yy.*", "ldy {z1}", "$1yy$2", null, mapZ)); + // Replace two Z1s with YY + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*z1.*|.*yy.*", "ldy {z1}", "$1yy$2yy$3", null, mapZ)); + // Replace first (not second) Z1 with YY + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)z1(.*)", "...z1=.*|.*yy.*", "ldy {z1}", "$1yy$2z1$3", null, null)); + // Replace second (not first) Z1 with YY + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z1(.*vb.)z1(.*)", "...z1=.*|.*yy.*", "ldy {z1}", "$1z1$2yy$3", null, null)); + + // Replace Z1 with XX (only one) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*|.*xx.*", "ldx {z1}", "$1xx$2", null, mapZ)); + // Replace two Z1s with XX + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*vb.)z1(.*)", "...z1=.*|.*z1.*z1.*z1.*|.*xx.*", "ldx {z1}", "$1xx$2xx$3", null, mapZ)); + // Replace first (not second) Z1 with XX + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z1(.*)z1(.*)", "...z1=.*|.*xx.*", "ldx {z1}", "$1xx$2z1$3", null, null)); + // Replace second (not first) Z1 with XX + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z1(.*vb.)z1(.*)", "...z1=.*|.*xx.*", "ldx {z1}", "$1z1$2xx$3", null, null)); + + // Replace Z2 with AA (only one) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*|.*aa.*", "lda {z2}", "$1aa$2", null, mapZ3)); + // Replace two Z2s with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*z2.*|.*aa.*", "lda {z2}", "$1aa$2aa$3", null, mapZ3)); + // Replace first (of 2) Z2 with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)z2(.*)", "...z2=.*|.*aa.*", "lda {z2}", "$1aa$2z2$3", null, null)); + // Replace second (of 2) Z2 with AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z2(.*vb.)z2(.*)", "...z2=.*|.*aa.*", "lda {z2}", "$1z2$2aa$3", null, null)); + + // Replace Z2 with YY (only one) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*|.*yy.*", "ldy {z2}", "$1yy$2", null, mapZ3)); + // Replace two Z2s with YY + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*z2.*|.*yy.*", "ldy {z2}", "$1yy$2yy$3", null, mapZ3)); + // Replace first (of 2) Z2 with YY + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)z2(.*)", "...z2=.*|.*yy.*", "ldy {z2}", "$1yy$2z2$3", null, null)); + // Replace second (of 2) Z2 with YY + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z2(.*vb.)z2(.*)", "...z2=.*|.*yy.*", "ldy {z2}", "$1z2$2yy$3", null, null)); + + // Replace Z2 with XX(only one) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*|.*xx.*", "ldx {z2}", "$1xx$2", null, mapZ3)); + // Replace two Z2s with XX + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*vb.)z2(.*)", "...z2=.*|.*z2.*z2.*z2.*|.*xx.*", "ldx {z2}", "$1xx$2xx$3", null, mapZ3)); + // Replace first (of 2) Z2 with XX + synths.add(new AsmFragmentTemplateSynthesisRule("(.*vb.)z2(.*)z2(.*)", "...z2=.*|.*xx.*", "ldx {z2}", "$1xx$2z2$3", null, null)); + // Replace second (of 2) Z2 with XX + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)z2(.*vb.)z2(.*)", "...z2=.*|.*xx.*", "ldx {z2}", "$1z2$2xx$3", null, null)); + + // Rewrite comparisons < to > + //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_gt_(.*)_then_(.*)", null, null, "$2_lt_$1_then_$3", null, null)); + // Rewrite comparisons > to < + //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_lt_(.*)_then_(.*)", null, null, "$2_gt_$1_then_$3", null, null)); + // Rewrite comparisons <= to >= + //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_le_(.*)_then_(.*)", null, null, "$2_ge_$1_then_$3", null, null)); + // Rewrite comparisons >= to <= + //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_ge_(.*)_then_(.*)", null, null, "$2_le_$1_then_$3", null, null)); + // Rewrite comparisons swap == + //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_eq_(.*)_then_(.*)", null, null, "$2_eq_$1_then_$3", null, null)); + // Rewrite comparisons swap != + //synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_neq_(.*)_then_(.*)", null, null, "$2_neq_$1_then_$3", null, null)); + + + // OLD STYLE REWRITES - written when only one rule could be taken + + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_(band|bor|bxor|plus)_(vb.aa)", ".*=vb.aa_.*", null, "$1=$4_$3_$2", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_(band|bor|bxor|plus)_(vb.xx)", ".*=vb.[ax][ax]_.*", null, "$1=$4_$3_$2", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_(band|bor|bxor|plus)_(vb.yy)", ".*=vb.[axy][axy]_.*", null, "$1=$4_$3_$2", null, null)); + + synths.add(new AsmFragmentTemplateSynthesisRule("vbuxx=(.*)", null, null, "vbuaa=$1", "tax\n", null)); + synths.add(new AsmFragmentTemplateSynthesisRule("vbsxx=(.*)", null, null, "vbsaa=$1", "tax\n", null)); + synths.add(new AsmFragmentTemplateSynthesisRule("vbuyy=(.*)", null, null, "vbuaa=$1", "tay\n", null)); + synths.add(new AsmFragmentTemplateSynthesisRule("vbsyy=(.*)", null, null, "vbsaa=$1", "tay\n", null)); + synths.add(new AsmFragmentTemplateSynthesisRule("vbuz1=(.*)", ".*=.*vb.z1.*", null, "vbuaa=$1", "sta {z1}\n", mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("vbsz1=(.*)", ".*=.*vb.z1.*", null, "vbsaa=$1", "sta {z1}\n", mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pb(.)c1=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1}\n", mapC)); + synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pb(.)c1=(.*c1.*)", null, null, "vb$1aa=$2", "sta {c1}\n", null)); + synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pbuz1=(.*)", ".*z1.*z1.*", null, "vbuaa=$1", "ldy #0\n" + "sta ({z1}),y\n", mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pbuz1=(.*z1.*)", null, null, "vbuaa=$1", "ldy #0\n" + "sta ({z1}),y\n", null)); + + synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuz1=(.*)", ".*z1.*z1.*|.*c1.*c1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapZC)); + //synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuyy=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1},y\n", mapC)); + //synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuxx=(.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "sta {c1},x\n", mapC)); + synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)z1_derefidx_vbuz2=(.*)", ".*z1.*z1.*|.*z2.*z2.*", null, "vb$1aa=$2", "ldy {z2}\n" + "sta ({z1}),y\n", mapZ2)); + + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=_deref_pb(.)c1(.*)", ".*=.*aa.*", "lda {c1}\n", "$1=vb$2aa$3", null, mapC)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=_deref_pb(.)z1(.*)", ".*z1.*z1.*|.*=.*aa.*|.*yy.*", "ldy #0\n" + "lda ({z1}),y\n", "$1=vb$2aa$3", null, mapZ)); + + // Convert array indexing with A register to X/Y register by prefixing tax/tay (..._derefidx_vbuaa... -> ..._derefidx_vbuxx... /... _derefidx_vbuyy... ) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuaa(.*)", ".*xx.*", "tax\n", "$1=$2_derefidx_vbuxx$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuaa(.*)", ".*yy.*", "tay\n", "$1=$2_derefidx_vbuyy$3", null, null)); + // Convert array indexing with zero page to x/y register by prefixing ldx z1 / ldy z1 ( ..._derefidx_vbuzn... -> ..._derefidx_vbuxx... / ..._derefidx_vbuyy... ) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz1(.*)", ".*xx.*|.*z1.*z1.*", "ldx {z1}\n", "$1=$2_derefidx_vbuxx$3", null, mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz1(.*)", ".*yy.*|.*z1.*z1.*", "ldy {z1}\n", "$1=$2_derefidx_vbuyy$3", null, mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz2(.*)", ".*xx.*|.*z2.*z2.*", "ldx {z2}\n", "$1=$2_derefidx_vbuxx$3", null, mapZ3)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz2(.*)", ".*yy.*|.*z2.*z2.*", "ldy {z2}\n", "$1=$2_derefidx_vbuyy$3", null, mapZ3)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz3(.*)", ".*yy.*", "ldy {z3}\n", "$1=$2_derefidx_vbuyy$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_derefidx_vbuz3(.*)", ".*xx.*", "ldx {z3}\n", "$1=$2_derefidx_vbuxx$3", null, null)); + // Convert array indexing twice with A/zp1/zp2 to X/Y register with a ldx/ldy prefix ( ..._derefidx_vbunn..._derefidx_vbunn... -> ..._derefidx_vbuxx..._derefidx_vbuxx... ) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuaa(.*)_derefidx_vbuaa(.*)", ".*aa.*aa.*aa.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "tax\n", null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuaa(.*)_derefidx_vbuaa(.*)", ".*aa.*aa.*aa.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "tay\n", null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz1(.*)_derefidx_vbuz1(.*)", ".*z1.*z1.*z1.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "ldx {z1}\n", mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz1(.*)_derefidx_vbuz1(.*)", ".*z1.*z1.*z1.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "ldy {z1}\n", mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz2(.*)_derefidx_vbuz2(.*)", ".*z2.*z2.*z2.*|.*xx.*", null, "$1_derefidx_vbuxx$2_derefidx_vbuxx$3", "ldx {z2}\n", mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz2(.*)_derefidx_vbuz2(.*)", ".*z2.*z2.*z2.*|.*yy.*", null, "$1_derefidx_vbuyy$2_derefidx_vbuyy$3", "ldy {z2}\n", mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuz1=(.*c1.*)", ".*z1.*z1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuz1=(.*z1.*)", ".*c1.*c1.*", null, "vb$1aa=$2", "ldx {z1}\n" + "sta {c1},x\n", mapC)); + + // Convert X/Y-based array indexing of a constant pointer into A-register by prefixing lda cn,x / lda cn,y ( ...pb.c1_derefidx_vbuxx... / ...pb.c1_derefidx_vbuyy... -> ...vb.aa... ) + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c1_derefidx_vbuxx(.*)", ".*=.*aa.*|.*c1.*c1.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, mapC)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*c1.*)pb(.)c1_derefidx_vbuxx(.*)", ".*=.*aa.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c1_derefidx_vbuxx(.*c1.*)", ".*=.*aa.*", "lda {c1},x\n", "$1=$2vb$3aa$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c1_derefidx_vbuyy(.*)", ".*=.*aa.*|.*c1.*c1.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, mapC)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*c1.*)pb(.)c1_derefidx_vbuyy(.*)", ".*=.*aa.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c1_derefidx_vbuyy(.*c1.*)", ".*=.*aa.*", "lda {c1},y\n", "$1=$2vb$3aa$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c2_derefidx_vbuxx(.*)", ".*=.*aa.*|.*c2.*c2.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, mapC3)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*c2.*)pb(.)c2_derefidx_vbuxx(.*)", ".*=.*aa.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c2_derefidx_vbuxx(.*c2.*)", ".*=.*aa.*", "lda {c2},x\n", "$1=$2vb$3aa$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c2_derefidx_vbuyy(.*)", ".*=.*aa.*|.*c2.*c2.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, mapC3)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*c2.*)pb(.)c2_derefidx_vbuyy(.*)", ".*=.*aa.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)pb(.)c2_derefidx_vbuyy(.*c2.*)", ".*=.*aa.*", "lda {c2},y\n", "$1=$2vb$3aa$4", null, null)); + + // Convert zeropage/constants/X/Y in assignments to A-register using LDA/TXA/TYA prefix + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuz1(.*)", ".*z1.*=.*|.*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2vbuaa$3", null, mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsz1(.*)", ".*z1.*=.*|.*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2vbsaa$3", null, mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuz2(.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbuaa$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsz2(.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbsaa$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuz2(.*z3.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbuaa$3", null, mapZ3)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsz2(.*z3.*)", ".*z2.*=.*|.*=.*aa.*|.*z2.*z2.*|.*z3.*", "lda {z2}\n", "$1=$2vbsaa$3", null, mapZ3)); + + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbuxx", ".*=.*[ax][ax].*xx|.*derefidx_vb.xx", "txa\n", "$1=$2_vbuaa", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbsxx", ".*=.*[ax][ax].*xx|.*derefidx_vb.xx", "txa\n", "$1=$2_vbsaa", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbuyy", ".*=.*[ay][ay].*yy|.*derefidx_vb.yy", "tya\n", "$1=$2_vbuaa", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbsyy", ".*=-*[ay][ay].*yy|.*derefidx_vb.yy", "tya\n", "$1=$2_vbsaa", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbuz1", ".*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2_vbuaa", null, mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbsz1", ".*=.*aa.*|.*z1.*z1.*", "lda {z1}\n", "$1=$2_vbsaa", null, mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbuz2", ".*=.*aa.*|.*z2.*z2.*", "lda {z2}\n", "$1=$2_vbuaa", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_vbuz3", ".*=.*aa.*|.*z3.*z3.*", "lda {z3}\n", "$1=$2_vbuaa", null, null)); + + synths.add(new AsmFragmentTemplateSynthesisRule("vbuz1=vbuz1(.*)", ".*=.*vb.aa.*|.*z1.*z1.*z1.*", "lda {z1}\n", "vbuaa=vbuaa$1", "sta {z1}\n", mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("vbsz1=vbsz1(.*)", ".*=.*vb.aa.*|.*z1.*z1.*z1.*", "lda {z1}\n", "vbsaa=vbsaa$1", "sta {z1}\n", mapZ)); + + synths.add(new AsmFragmentTemplateSynthesisRule("vbuz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*|.*z1.*z1.*", "lda {z1}\n", "vbuaa_$1_$2", null, mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("vbsz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*|.*z1.*z1.*", "lda {z1}\n", "vbsaa_$1_$2", null, mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pb(.)c1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*", "lda {c1}\n", "vb$1aa_$2_$3", null, mapC)); + synths.add(new AsmFragmentTemplateSynthesisRule("_deref_pb(.)z1_(lt|gt|le|ge|eq|neq)_(.*)", ".*vb.aa.*|.*vb.yy.*|.*z1.*z1.*", "ldy #0\n" + "lda ({z1}),y\n", "vb$1aa_$2_$3", null, mapZ)); + + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz1_(.*)", ".*z1.*z1.*|.*.yy.*", "ldy {z1}\n", "$1_derefidx_vbuyy_$2", null, mapZ)); + + + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_derefidx_vbuz1_(lt|gt|le|ge|eq|neq)_(.*)", ".*z1.*z1.*|.*vb.xx.*", "ldx {z1}\n", "$1_derefidx_vbuxx_$2_$3", null, mapZ)); + synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuyy_(lt|gt|le|ge|eq|neq)_(.*)", ".*c1.*c1.*|.*aa.*", "lda {c1},y\n", "vb$1aa_$2_$3", null, mapC)); + synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuyy_(lt|gt|le|ge|eq|neq)_(.*c1.*)", ".*aa.*", "lda {c1},y\n", "vb$1aa_$2_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuxx_(lt|gt|le|ge|eq|neq)_(.*)", ".*c1.*c1.*|.*aa.*", "lda {c1},x\n", "vb$1aa_$2_$3", null, mapC)); + synths.add(new AsmFragmentTemplateSynthesisRule("pb(.)c1_derefidx_vbuxx_(lt|gt|le|ge|eq|neq)_(.*c1.*)", ".*aa.*", "lda {c1},x\n", "vb$1aa_$2_$3", null, null)); + + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_ge_(vb.aa)_then_(.*)", ".*vb.aa.*_ge.*", null, "$2_le_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_ge_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_ge.*", null, "$2_le_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_ge_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_ge.*", null, "$2_le_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_lt_(vb.aa)_then_(.*)", ".*vb.aa.*_lt.*", null, "$2_gt_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_lt_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_lt.*", null, "$2_gt_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_lt_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_lt.*", null, "$2_gt_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_gt_(vb.aa)_then_(.*)", ".*vb.aa.*_gt.*", null, "$2_lt_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_gt_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_gt.*", null, "$2_lt_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_gt_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_gt.*", null, "$2_lt_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_le_(vb.aa)_then_(.*)", ".*vb.aa.*_le.*", null, "$2_ge_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_le_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_le.*", null, "$2_ge_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_le_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_le.*", null, "$2_ge_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_neq_(vb.aa)_then_(.*)", ".*vb.aa.*_neq.*", null, "$2_neq_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_neq_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_neq.*", null, "$2_neq_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_neq_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_neq.*", null, "$2_neq_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_eq_(vb.aa)_then_(.*)", ".*vb.aa.*_eq.*", null, "$2_eq_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_eq_(vb.xx)_then_(.*)", ".*vb.[ax][ax].*_eq.*", null, "$2_eq_$1_then_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_eq_(vb.yy)_then_(.*)", ".*vb.[axy][axy].*_eq.*", null, "$2_eq_$1_then_$3", null, null)); + + // Use unsigned ASM to synthesize signed ASM ( ...vbs... -> ...vbu... ) + synths.add(new AsmFragmentTemplateSynthesisRule("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_(eq|neq)_(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_then_(.*)", null, null, "$1_$2_$3_then_$4", null, mapSToU)); + synths.add(new AsmFragmentTemplateSynthesisRule("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)", null, null, "$1=$2", null, mapSToU)); + synths.add(new AsmFragmentTemplateSynthesisRule("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)_(plus|band|bxor|bor)_(vbsz.|csoby.|vbsaa|vbsxx|vbsyy)", null, null, "$1=$2_$3_$4", null, mapSToU)); + synths.add(new AsmFragmentTemplateSynthesisRule("(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)=_(inc|dec)_(vbsz.|vbsc.|vbsaa|vbsxx|vbsyy)", null, null, "$1=_$2_$3", null, mapSToU)); + synths.add(new AsmFragmentTemplateSynthesisRule("(vwsz.|vwsc.)_(eq|neq)_(vwsz.|vwsc.)_then_(.*)", null, null, "$1_$2_$3_then_$4", null, mapSToU)); + synths.add(new AsmFragmentTemplateSynthesisRule("(vwsz.)=(vwsz.|vwsc.)", null, null, "$1=$2", null, mapSToU)); + synths.add(new AsmFragmentTemplateSynthesisRule("(v.sz.)=(v.s..)_(band|bxor|bor)_(v.s..)", null, null, "$1=$2_$3_$4", null, mapSToU)); + synths.add(new AsmFragmentTemplateSynthesisRule("(vbuz.|vbuaa|vbuxx|vbuyy)=_(lo|hi)_vws(z.|c.)", null, null, "$1=_$2_vwu$3", null, mapSToU)); + + // Use constant word ASM to synthesize unsigned constant byte ASM ( ...vb.c... -> vw.c... ) + synths.add(new AsmFragmentTemplateSynthesisRule("(vwuz.)=(vwuz.)_(plus|minus|band|bxor|bor)_vb.c(.)", null, null, "$1=$2_$3_vwuc$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(vwuz.)=vb.c(.)_(plus|minus|band|bxor|bor)_(vwuz.)", null, null, "$1=vwuc$2_$3_$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(vwsz.)=(vwsz.)_(plus|minus|band|bxor|bor)_vb.c(.)", null, null, "$1=$2_$3_vwsc$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(vwsz.)=vb.c(.)_(plus|minus|band|bxor|bor)_(vwsz.)", null, null, "$1=vwsc$2_$3_$4", null, null)); + + // Move constant words to the end of the ASM signature for symmetric operators ( ...vw.c...vw.z... -> ...vw.z...vw.c... ) + synths.add(new AsmFragmentTemplateSynthesisRule("(vwuz.)=(vwuc.)_(plus|band|bxor|bor)_(vwuz.)", null, null, "$1=$4_$3_$2", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(vwsz.)=(vwsc.)_(plus|band|bxor|bor)_(vwsz.)", null, null, "$1=$4_$3_$2", null, null)); + + // Use Z1/Z2 ASM to synthesize Z1-only code ( ...z1...z1... -> ...z1...z2... ) + synths.add(new AsmFragmentTemplateSynthesisRule("(v..)z1=(v..)z1_(plus|minus|band|bxor|bor)_(.*)", ".*z2.*", null, "$1z1=$2z2_$3_$4", null, mapZ, false)); + synths.add(new AsmFragmentTemplateSynthesisRule("(v..)z1=(.*)_(plus|minus|band|bxor|bor)_(v..)z1", ".*z2.*", null, "$1z1=$2_$3_$4z2", null, mapZ, false)); + synths.add(new AsmFragmentTemplateSynthesisRule("(v..)z1=_(neg|lo|hi)_(v..)z1", ".*z2.*", null, "$1z1=_$2_$3z2", null, mapZ, false)); + + // Convert INC/DEC to +1/-1 ( ..._inc_xxx... -> ...xxx_plus_1_... / ..._dec_xxx... -> ...xxx_minus_1_... ) + synths.add(new AsmFragmentTemplateSynthesisRule("vb(.)aa=_inc_(.*)", null, null, "vb$1aa=$2_plus_1", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("vb(.)aa=_dec_(.*)", null, null, "vb$1aa=$2_minus_1", null, null)); + + // Synthesize XX/YY using AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuxx(.*)", ".*=.*aa.*|.*derefidx_vb.xx.*", "txa\n", "$1=$2vbuaa$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsxx(.*)", ".*=.*aa.*|.*derefidx_vb.xx.*", "txa\n", "$1=$2vbsaa$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuyy(.*)", ".*=.*aa.*|.*derefidx_vb.yy.*", "tya\n", "$1=$2vbuaa$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsyy(.*)", ".*=.*aa.*|.*derefidx_vb.yy.*", "tya\n", "$1=$2vbsaa$3", null, null)); + // Synthesize constants using AA + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbuc1(.*)", ".*=.*aa.*|.*c1.*c1.*|.*c1_deref.*", "lda #{c1}\n", "$1=$2vbuaa$3", null, mapC)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)vbsc1(.*)", ".*=.*aa.*|.*c1.*c1.*|.*c1_deref.*", "lda #{c1}\n", "$1=$2vbsaa$3", null, mapC)); + + // Synthesize some constant pointers as constant words + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)_(lt|gt|le|ge|eq|neq)_p..([cz].)_then_(.*)", null, null, "$1_$2_vwu$3_then_$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("p..([cz].)_(lt|gt|le|ge|eq|neq)_(.*)", null, null, "vwu$1_$2_$3", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([zc].)", null, null, "$1=vwu$2", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=(.*)_(plus|minus|bor|bxor)_p..([cz].)", null, null, "$1=$2_$3_vwu$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([cz].)_(plus|minus|bor|bxor)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("p..([cz].)=(.*)_(sethi|setlo|plus|minus)_(.*)", null, null, "vwu$1=$2_$3_$4", null, null)); + synths.add(new AsmFragmentTemplateSynthesisRule("(.*)=p..([cz].)_(sethi|setlo|plus|minus)_(.*)", null, null, "$1=vwu$2_$3_$4", null, null)); + + return synths; + } } diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesizer.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesizer.java new file mode 100644 index 000000000..485d55de9 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesizer.java @@ -0,0 +1,471 @@ +package dk.camelot64.kickc.fragment; + +import dk.camelot64.kickc.CompileLog; +import dk.camelot64.kickc.asm.AsmProgram; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.*; + +/** + * Provides fragment templates from their signature. + *

+ * Fragment templates are created by either: + *

+ *

+ * The actual creation is handled by a full-graph + */ +public class AsmFragmentTemplateSynthesizer { + + /** Resource Folder containing the fragment files. */ + public static final String FRAGMENT_RESOURCE_FOLDER = "dk/camelot64/kickc/fragment/asm/"; + + /** The static instance. */ + static AsmFragmentTemplateSynthesizer SYNTHESIZER = new AsmFragmentTemplateSynthesizer(); + + /** Create synthesizer. */ + private AsmFragmentTemplateSynthesizer() { + this.bestFragmentCache = new LinkedHashMap<>(); + this.synthesisGraph = new LinkedHashMap<>(); + this.bestTemplateUpdate = new ArrayDeque<>(); + } + + /** Cache for the best fragment templates. Maps signature to the best fragment template for the signature. */ + private Map bestFragmentCache; + + /** Special singleton representing that the fragment can not be synthesized or loaded. */ + private AsmFragmentTemplate UNKNOWN = new AsmFragmentTemplate("UNKNOWN", null); + + public static AsmFragmentInstance getFragmentInstance(AsmFragmentInstanceSpec instanceSpec, CompileLog log) { + String signature = instanceSpec.getSignature(); + AsmFragmentTemplate fragmentTemplate = SYNTHESIZER.getFragmentTemplate(signature, log); + // Return the resulting fragment instance + return new AsmFragmentInstance( + instanceSpec.getProgram(), + signature, + instanceSpec.getCodeScope(), + fragmentTemplate, + instanceSpec.getBindings()); + } + + public AsmFragmentTemplate getFragmentTemplate(String signature, CompileLog log) { + AsmFragmentTemplate bestTemplate = bestFragmentCache.get(signature); + if(bestTemplate == UNKNOWN) { + if(log.isVerboseFragmentLog()) { + log.append("Unknown fragment " + signature); + } + throw new UnknownFragmentException(signature); + } + if(bestTemplate == null) { + Collection candidates = getBestTemplates(signature, log); + if(candidates.size() == 0) { + if(log.isVerboseFragmentLog()) { + log.append("Unknown fragment " + signature); + } + bestFragmentCache.put(signature, UNKNOWN); + throw new UnknownFragmentException(signature); + } + double minScore = Double.MAX_VALUE; + for(AsmFragmentTemplate candidateTemplate : candidates) { + double score = candidateTemplate.getCycles(); + if(candidateTemplate.getClobber().isClobberA()) score+=0.5; + if(candidateTemplate.getClobber().isClobberY()) score+=1.0; + if(candidateTemplate.getClobber().isClobberX()) score+=1.5; + if(score < minScore) { + minScore = score; + bestTemplate = candidateTemplate; + } + } + if(log.isVerboseFragmentLog()) { + log.append("Found best fragment " + bestTemplate.getName() + " score: " + minScore); + } + bestFragmentCache.put(signature, bestTemplate); + } + // Count usages + AsmFragmentTemplateUsages.incUsage(bestTemplate); + return bestTemplate; + } + + /** + * Get the best fragment templates for a signature. + * The templates are either loaded or synthesized from other templates. + * + * @param signature The signature of the fragment template to get + * @param log The compile log + * @return The best templates for the passed signature + */ + private Collection getBestTemplates(String signature, CompileLog log) { + getOrCreateSynthesis(signature, log); + updateBestTemplates(log); + AsmFragmentSynthesis synthesis = getSynthesis(signature); + if(synthesis == null) { + throw new RuntimeException("Synthesis Graph Error! synthesis not found in graph " + signature); + } + return synthesis.getBestTemplates(); + } + + /** + * Contains the synthesis for each fragment template signature. + * The synthesis is capable of loading the fragment from disk or synthesizing it from other fragments using synthesis rules. + * The synthesis caches the best fragments for each clobber profile (loaded or synthesized). + * This map is effectively a full-graph where the nodes are synthesis for signatures and edges are the + * synthesis rules capable of synthesizing one fragment temple from another. + */ + private Map synthesisGraph; + + /** + * The synthesis is capable of loading the fragment from disk or synthesizing it from other fragments using synthesis rules. + * The synthesis caches the best fragments for each clobber profile (loaded or synthesized). + * A node in the synthesis graph. + */ + public static class AsmFragmentSynthesis { + + /** The signature of the fragment template being synthesized. */ + private String signature; + + /** The best template loaded/synthesized so far for each clobber profile */ + private Map bestTemplates; + + /** Options for synthesizing the template from sub-fragments using a specific synthesis rule. Forward edges in the synthesis graph. */ + private Set synthesisOptions; + + /** Options for synthesizing the other templates from this template using a specific synthesis rule. Backward edges in the synthesis graph. */ + private Set parentOptions; + + /** The template loaded from a file, if it exists. null if no file exists for the signature. */ + private AsmFragmentTemplate fileTemplate; + + /** + * Create a new synthesis + * + * @param signature The signature of the fragment template to load/synthesize + */ + public AsmFragmentSynthesis(String signature) { + this.signature = signature; + this.bestTemplates = new LinkedHashMap<>(); + this.synthesisOptions = new LinkedHashSet<>(); + this.parentOptions = new LinkedHashSet<>(); + } + + /** + * Add a synthesis option to the template synthesis. + * The synthesis options can be used for synthesizing the template from sub-fragments using a specific synthesis rule. + * + * @param synthesisOption The option to add + */ + public void addSynthesisOption(AsmFragmentSynthesisOption synthesisOption) { + this.synthesisOptions.add(synthesisOption); + } + + /** + * Get the options for synthesizing the template from sub-fragments using a specific synthesis rule. Forward edges in the synthesis graph. + * + * @return The options + */ + public Collection getSynthesisOptions() { + return synthesisOptions; + } + + /** + * Add a parent. The parent is an option for synthesizing another fragment template from this one using a specific synthesis rule. + * + * @param synthesisOption Thew parent option to add + */ + public void addParentOption(AsmFragmentSynthesisOption synthesisOption) { + this.parentOptions.add(synthesisOption); + } + + public void setFileTemplate(AsmFragmentTemplate fileTemplate) { + this.fileTemplate = fileTemplate; + } + + public AsmFragmentTemplate getFileTemplate() { + return fileTemplate; + } + + /** + * Handle a candidate for best template. + * If the candidate is better than the current best for its clobber profile update the best template + * + * @param template The template candidate to examine + * @return true if the best template was updated + */ + public boolean bestTemplateCandidate(AsmFragmentTemplate template) { + AsmFragmentClobber clobber = template.getClobber(); + AsmFragmentTemplate bestTemplate = bestTemplates.get(clobber); + if(bestTemplate == null || bestTemplate.getCycles() > template.getCycles()) { + bestTemplates.put(clobber, template); + return true; + } else { + return false; + } + } + + /** + * Get the parent options. + * These are options for synthesizing other templates + * from this template using a specific synthesis rule. Backward edges in the synthesis graph. + * + * @return The parent options. + */ + public Set getParentOptions() { + return parentOptions; + } + + /** + * Get the best fragment templates of the synthesis. + * Multiple templates are returned if templates with different clobber profiles exist. + * + * @return The best templates of the synthesis. + */ + public Collection getBestTemplates() { + return bestTemplates.values(); + } + + public String getSignature() { + return signature; + } + } + + /** An option for synthesizing a fragment template from a sub-template using a specific synthesis rule. An edge in the synthesis graph. */ + public static class AsmFragmentSynthesisOption { + + /** The signature of the fragment template being synthesized. The from-node in the graph. */ + private String signature; + + /** The signature of the sub-fragment template to synthesize from. The to-node in the graph. */ + private String subSignature; + + /** The synthesis rule capable of synthesizing this template from the sub-fragment. */ + private AsmFragmentTemplateSynthesisRule rule; + + /** + * Create a synthesis option + * + * @param signature he signature of the fragment template being synthesized. + * @param rule The synthesis rule capable of synthesizing this template from the sub-fragment. + */ + public AsmFragmentSynthesisOption(String signature, AsmFragmentTemplateSynthesisRule rule) { + this.signature = signature; + this.rule = rule; + this.subSignature = rule.getSubSignature(signature); + } + + public String getSignature() { + return signature; + } + + public String getSubSignature() { + return subSignature; + } + + public AsmFragmentTemplateSynthesisRule getRule() { + return rule; + } + + @Override + public boolean equals(Object o) { + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + + AsmFragmentSynthesisOption that = (AsmFragmentSynthesisOption) o; + + if(signature != null ? !signature.equals(that.signature) : that.signature != null) return false; + if(subSignature != null ? !subSignature.equals(that.subSignature) : that.subSignature != null) return false; + return rule != null ? rule.equals(that.rule) : that.rule == null; + } + + @Override + public int hashCode() { + int result = signature != null ? signature.hashCode() : 0; + result = 31 * result + (subSignature != null ? subSignature.hashCode() : 0); + result = 31 * result + (rule != null ? rule.hashCode() : 0); + return result; + } + } + + /** Get the entire synthesis graph. Called by the usage statistics. + * + * @return The entire synthesis graph + */ + Map getSynthesisGraph() { + return synthesisGraph; + } + + /** + * Get the synthesis used to synthesize a fragment template. + * If the synthesis does not exist null is returned. + * + * @param signature The signature of the template to synthesize + * @return The synthesis used to synthesize the fragment template. + * null if the synthesis graph does not contain the synthesis. + */ + private AsmFragmentSynthesis getSynthesis(String signature) { + return synthesisGraph.get(signature); + } + + /** + * Get (or create) the synthesis used to synthesize a fragment template. + * If the synthesis does not already exist in the synthesis graph it is created - along with any sub-synthesis recursively usable for creating it. + * If any synthesis are created they are added to the {@link #bestTemplateUpdate} queue. + * + * @param signature The signature of the template to synthesize + * @param log The compile log + * @return The synthesis that is used to load/synthesize the best template + */ + AsmFragmentSynthesis getOrCreateSynthesis(String signature, CompileLog log) { + AsmFragmentSynthesis synthesis = getSynthesis(signature); + if(synthesis != null) { + return synthesis; + } + // Synthesis not found - create it + if(log.isVerboseFragmentLog()) { + log.append("New fragment synthesis " + signature); + } + synthesis = new AsmFragmentSynthesis(signature); + synthesisGraph.put(signature, synthesis); + queueUpdateBestTemplate(synthesis); + // Load the template from file - if it exists + AsmFragmentTemplate fileTemplate = loadFragmentTemplate(signature, log); + if(fileTemplate != null) { + synthesis.setFileTemplate(fileTemplate); + if(log.isVerboseFragmentLog()) { + log.append("New fragment synthesis " + signature + " - Successfully loaded " + signature + ".asm"); + } + } + // Populate with synthesis options + for(AsmFragmentTemplateSynthesisRule rule : AsmFragmentTemplateSynthesisRule.getSynthesisRules()) { + if(rule.matches(signature)) { + AsmFragmentSynthesisOption synthesisOption = new AsmFragmentSynthesisOption(signature, rule); + synthesis.addSynthesisOption(synthesisOption); + if(log.isVerboseFragmentLog()) { + log.append("New fragment synthesis " + signature + " - sub-option " + synthesisOption.getSubSignature()); + } + } + } + // Ensure that all sub-synthesis exist (recursively) - and set their parent options + for(AsmFragmentSynthesisOption synthesisOption : synthesis.getSynthesisOptions()) { + AsmFragmentSynthesis subSynthesis = getOrCreateSynthesis(synthesisOption.getSubSignature(), log); + subSynthesis.addParentOption(synthesisOption); + } + return synthesis; + } + + /** Work queue with synthesis that need to be recalculated to find the best templates. */ + private Deque bestTemplateUpdate; + + /** + * Queue an update of the best templates for a synthesis to the work queue + * + * @param synthesis The synthesis to add to the work queue + */ + private void queueUpdateBestTemplate(AsmFragmentSynthesis synthesis) { + if(!bestTemplateUpdate.contains(synthesis)) { + bestTemplateUpdate.push(synthesis); + } + } + + /** + * Find the best fragment template for all synthesis in the work queue {@link #bestTemplateUpdate} queue. + * During the update more items can be added to the work queue. + * Returns when the work queue is empty. + */ + void updateBestTemplates(CompileLog log) { + while(!bestTemplateUpdate.isEmpty()) { + AsmFragmentSynthesis synthesis = bestTemplateUpdate.pop(); + boolean modified = false; + // Check if the file template is best in class + AsmFragmentTemplate fileTemplate = synthesis.getFileTemplate(); + if(fileTemplate != null) { + modified |= synthesis.bestTemplateCandidate(fileTemplate); + } + Collection synthesisOptions = synthesis.getSynthesisOptions(); + for(AsmFragmentSynthesisOption synthesisOption : synthesisOptions) { + String subSignature = synthesisOption.getSubSignature(); + AsmFragmentTemplateSynthesisRule rule = synthesisOption.getRule(); + AsmFragmentSynthesis subSynthesis = getSynthesis(subSignature); + if(subSynthesis == null) { + throw new RuntimeException("Synthesis Graph Error! Sub-synthesis not found in graph " + subSignature); + } + Collection subTemplates = subSynthesis.getBestTemplates(); + for(AsmFragmentTemplate subTemplate : subTemplates) { + AsmFragmentTemplate synthesized = rule.synthesize(synthesis.getSignature(), subTemplate); + if(log.isVerboseFragmentLog()) { + log.append("Fragment synthesis " + synthesis.getSignature() + " - Successfully synthesized from " + subSignature); + } + modified |= synthesis.bestTemplateCandidate(synthesized); + } + } + if(modified) { + // The synthesis was modified - update all parents later + for(AsmFragmentSynthesisOption parentOption : synthesis.getParentOptions()) { + String parentSignature = parentOption.getSignature(); + AsmFragmentSynthesis parentSynthesis = getSynthesis(parentSignature); + if(parentSynthesis == null) { + throw new RuntimeException("Synthesis Graph Error! Parent synthesis not found in graph " + parentSignature); + } + queueUpdateBestTemplate(parentSynthesis); + if(log.isVerboseFragmentLog()) { + log.append("Fragment synthesis " + synthesis.getSignature() + " - New best, scheduling parent " + parentSignature); + } + } + } + if(synthesis.getBestTemplates().isEmpty() && log.isVerboseFragmentLog()) { + log.append("Fragment synthesis " + synthesis.getSignature() + " - No file or synthesis results!"); + } + } + } + + /** + * Attempt to load a fragment template from disk. + * + * @param signature The signature + * @param log The compile log + * @return The fragment template from a file. null if the template is not found as a file. + */ + private AsmFragmentTemplate loadFragmentTemplate(String signature, CompileLog log) { + try { + ClassLoader classLoader = AsmFragmentTemplateSynthesizer.class.getClassLoader(); + URL fragmentUrl = classLoader.getResource(FRAGMENT_RESOURCE_FOLDER + signature + ".asm"); + if(fragmentUrl == null) return null; + InputStream fragmentStream = fragmentUrl.openStream(); + CharStream fragmentCharStream = CharStreams.fromStream(fragmentStream); + String body = fragmentCharStream.toString(); + return new AsmFragmentTemplate(signature, body); + } catch(IOException e) { + throw new RuntimeException("Error loading fragment file " + signature, e); + } catch(StringIndexOutOfBoundsException e) { + throw new RuntimeException("Problem reading fragment file " + signature, e); + } + } + + File[] allFragmentFiles() { + ClassLoader classLoader = AsmFragmentTemplateSynthesizer.class.getClassLoader(); + String path = classLoader.getResource("dk/camelot64/kickc/fragment/asm/").getPath(); + return new File(path).listFiles((dir, name) -> name.endsWith(".asm")); + + } + + public class UnknownFragmentException extends RuntimeException { + + private String signature; + + UnknownFragmentException(String signature) { + super("Fragment not found " + signature); + this.signature = signature; + } + + public String getFragmentSignature() { + return signature; + } + + } + +} diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateUsages.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateUsages.java index 349ee2c42..d1e8026ab 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateUsages.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateUsages.java @@ -35,12 +35,14 @@ public class AsmFragmentTemplateUsages { */ public static void logUsages(CompileLog log, boolean logRedundantFiles, boolean logUnusedFiles, boolean logFileDetails, boolean logAllDetails) { - Map> fragmentTemplateCache = AsmFragmentTemplateManager.getFragmentTemplateCache(); - ArrayList signatures = new ArrayList<>(fragmentTemplateCache.keySet()); + Map synthesisGraph = + AsmFragmentTemplateSynthesizer.SYNTHESIZER.getSynthesisGraph(); + ArrayList signatures = new ArrayList<>(synthesisGraph.keySet()); Collections.sort(signatures); - File[] files = AsmFragmentTemplateManager.allFragmentFiles(); + File[] files = AsmFragmentTemplateSynthesizer.SYNTHESIZER.allFragmentFiles(); if(logRedundantFiles) { + /* // Find all file fragments that were bested by a synthesized fragment log.append("\nREDUNDANT ASM FRAGMENT FILE ANALYSIS (if found remove them from disk)"); for(String signature : signatures) { @@ -65,34 +67,26 @@ public class AsmFragmentTemplateUsages { log.append("rm " + fileTemplate.getName() + ".asm #synthesized by " + maxTemplate.getName() + " - usages: " + maxUsage); } } + */ Set redundantSignatures = new LinkedHashSet<>(); for(File file : files) { String fileName = file.getName(); String signature = fileName.substring(0, fileName.length() - 4); - // Try to synthesize the fragment - and check if the synthesis is as good as the file body - AsmFragmentTemplateManager.AsmFragmentTemplateSynthesizer synthesizer = new AsmFragmentTemplateManager.AsmFragmentTemplateSynthesizer(signature, log); - List templates = synthesizer.loadOrSynthesizeFragment(signature, new AsmFragmentTemplateManager.AsmSynthesisPath()); - AsmFragmentTemplate fileTemplate = null; - for(AsmFragmentTemplate template : templates) { - if(template.isFile()) { - fileTemplate = template; + // Synthesize the fragment - and check if the synthesis is as good as the file body + AsmFragmentTemplate template = + AsmFragmentTemplateSynthesizer.SYNTHESIZER.getFragmentTemplate(signature, log); + if(!template.isFile()) { + // Check if the synthesis uses a file marked as redundant + AsmFragmentTemplate sourceFileTemplate = template; + while(!sourceFileTemplate.isFile()) { + sourceFileTemplate = sourceFileTemplate.getSubFragment(); } - } - for(AsmFragmentTemplate template : templates) { - if(!template.isFile() && template.getBody().equals(fileTemplate.getBody())) { - // Check if the synthesis uses a file marked as redundant - AsmFragmentTemplate sourceFileTemplate = template; - while(!sourceFileTemplate.isFile()) { - sourceFileTemplate = sourceFileTemplate.getSubFragment(); - } - if(redundantSignatures.contains(sourceFileTemplate.getSignature())) { - throw new RuntimeException("Problem in redundancy analysis! " + sourceFileTemplate.getSignature() + ".asm seems redundant but is needed for synthesis of " + signature); - } - log.append("rm " + fileTemplate.getName() + ".asm #synthesized same ASM by " + template.getName()); - redundantSignatures.add(signature); - break; + if(redundantSignatures.contains(sourceFileTemplate.getSignature())) { + throw new RuntimeException("Problem in redundancy analysis! " + sourceFileTemplate.getSignature() + ".asm seems redundant but is needed for synthesis of " + signature); } + log.append("rm " + template.getName() + ".asm #synthesized better ASM by " + template.getName()); + redundantSignatures.add(signature); } } } @@ -102,7 +96,8 @@ public class AsmFragmentTemplateUsages { for(File file : files) { String fileName = file.getName(); String signature = fileName.substring(0, fileName.length() - 4); - List templates = fragmentTemplateCache.get(signature); + AsmFragmentTemplateSynthesizer.AsmFragmentSynthesis asmFragmentSynthesis = synthesisGraph.get(signature); + Collection templates = asmFragmentSynthesis.getBestTemplates(); if(templates != null && templates.size() > 0) { // The template has been loaded / synthesized - is the usage count zero? boolean allZero = true; @@ -135,7 +130,7 @@ public class AsmFragmentTemplateUsages { // Find all file templates List fileTemplates = new ArrayList<>(); for(String signature : signatures) { - List templates = fragmentTemplateCache.get(signature); + Collection templates = synthesisGraph.get(signature).getBestTemplates(); for(AsmFragmentTemplate template : templates) { if(template.isFile()) { fileTemplates.add(template); @@ -150,10 +145,8 @@ public class AsmFragmentTemplateUsages { log.append("\nDETAILED ASM FRAGMENT USAGES"); List allTemplates = new ArrayList<>(); for(String signature : signatures) { - List templates = fragmentTemplateCache.get(signature); - for(AsmFragmentTemplate template : templates) { - allTemplates.add(template); - } + Collection templates = synthesisGraph.get(signature).getBestTemplates(); + allTemplates.addAll(templates); } logTemplatesByUsage(log, allTemplates); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 6ab23f6a1..319e9f74c 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -221,7 +221,7 @@ public class Pass4CodeGeneration { } StatementAssignment assignment = (StatementAssignment) statement; AsmFragmentInstanceSpec asmFragmentInstanceSpec = new AsmFragmentInstanceSpec(assignment, assignmentAlu, program); - AsmFragmentInstance asmFragmentInstance = AsmFragmentTemplateManager.getFragment(asmFragmentInstanceSpec, program.getLog()); + AsmFragmentInstance asmFragmentInstance = AsmFragmentTemplateSynthesizer.getFragmentInstance(asmFragmentInstanceSpec, program.getLog()); asm.getCurrentSegment().setFragment(asmFragmentInstance.getFragmentName()); asmFragmentInstance.generate(asm); aluState.clear(); @@ -248,14 +248,14 @@ public class Pass4CodeGeneration { asm.addComment(lValue.toString(program) + " = " + assignment.getrValue2().toString(program) + " // register copy " + getRegister(lValue)); } else { AsmFragmentInstanceSpec asmFragmentInstanceSpec = new AsmFragmentInstanceSpec(assignment, program); - AsmFragmentInstance asmFragmentInstance = AsmFragmentTemplateManager.getFragment(asmFragmentInstanceSpec, program.getLog()); + AsmFragmentInstance asmFragmentInstance = AsmFragmentTemplateSynthesizer.getFragmentInstance(asmFragmentInstanceSpec, program.getLog()); asm.getCurrentSegment().setFragment(asmFragmentInstance.getFragmentName()); asmFragmentInstance.generate(asm); } } } else if(statement instanceof StatementConditionalJump) { AsmFragmentInstanceSpec asmFragmentInstanceSpec = new AsmFragmentInstanceSpec((StatementConditionalJump) statement, block, program, getGraph()); - AsmFragmentInstance asmFragmentInstance = AsmFragmentTemplateManager.getFragment(asmFragmentInstanceSpec, program.getLog()); + AsmFragmentInstance asmFragmentInstance = AsmFragmentTemplateSynthesizer.getFragmentInstance(asmFragmentInstanceSpec, program.getLog()); asm.getCurrentSegment().setFragment(asmFragmentInstance.getFragmentName()); asmFragmentInstance.generate(asm); } else if(statement instanceof StatementCall) { @@ -341,7 +341,7 @@ public class Pass4CodeGeneration { asm.getCurrentSegment().setFragment("register_copy"); } else { AsmFragmentInstanceSpec asmFragmentInstanceSpec = new AsmFragmentInstanceSpec(lValue, rValue, program, scope); - AsmFragmentInstance asmFragmentInstance = AsmFragmentTemplateManager.getFragment(asmFragmentInstanceSpec, program.getLog()); + AsmFragmentInstance asmFragmentInstance = AsmFragmentTemplateSynthesizer.getFragmentInstance(asmFragmentInstanceSpec, program.getLog()); asm.getCurrentSegment().setFragment(asmFragmentInstance.getFragmentName()); asmFragmentInstance.generate(asm); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java index db9113441..f7e798e33 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftCombinations.java @@ -4,7 +4,7 @@ import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.asm.AsmSegment; import dk.camelot64.kickc.fragment.AsmFragmentInstance; import dk.camelot64.kickc.fragment.AsmFragmentInstanceSpec; -import dk.camelot64.kickc.fragment.AsmFragmentTemplateManager; +import dk.camelot64.kickc.fragment.AsmFragmentTemplateSynthesizer; import dk.camelot64.kickc.model.*; import java.util.*; @@ -28,7 +28,7 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { */ static void chooseBestUpliftCombination( RegisterCombinationIterator combinationIterator, int maxCombinations, - Set unknownFragments, + Set unknownFragments, ScopeRef scope, Program program ) { @@ -86,7 +86,7 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { public static boolean generateCombinationAsm( RegisterCombination combination, Program program, - Set unknownFragments, + Set unknownFragments, ScopeRef scope) { // Reset register allocation to original zero page allocation new Pass4RegistersFinalize(program).allocate(false); @@ -107,8 +107,8 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { // Generate ASM try { new Pass4CodeGeneration(program, false).generate(); - } catch(AsmFragmentTemplateManager.UnknownFragmentException e) { - unknownFragments.add(e.getFragmentInstanceSpec()); + } catch(AsmFragmentTemplateSynthesizer.UnknownFragmentException e) { + unknownFragments.add(e.getFragmentSignature()); if(program.getLog().isVerboseUplift()) { StringBuilder msg = new StringBuilder(); msg.append("Uplift attempt [" + (scope == null ? "" : scope) + "] "); @@ -235,7 +235,7 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { public void performUplift(int maxCombinations) { // Test uplift combinations to find the best one. - Set unknownFragments = new LinkedHashSet<>(); + Set unknownFragments = new LinkedHashSet<>(); List registerUpliftScopes = getProgram().getRegisterUpliftProgram().getRegisterUpliftScopes(); for(RegisterUpliftScope upliftScope : registerUpliftScopes) { RegisterCombinationIterator combinationIterator = upliftScope.getCombinationIterator(getProgram().getRegisterPotentials()); @@ -249,8 +249,8 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base { if(unknownFragments.size() > 0) { getLog().append("MISSING FRAGMENTS"); - for(AsmFragmentInstanceSpec unknownFragment : unknownFragments) { - getLog().append(" " + unknownFragment.toString()); + for(String unknownFragment : unknownFragments) { + getLog().append(" " + unknownFragment); } } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java index f075db339..831265414 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java @@ -3,7 +3,7 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.asm.AsmClobber; import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.fragment.AsmFragmentInstance; -import dk.camelot64.kickc.fragment.AsmFragmentTemplateManager; +import dk.camelot64.kickc.fragment.AsmFragmentTemplateSynthesizer; import dk.camelot64.kickc.model.*; import java.util.*; @@ -196,8 +196,8 @@ public class Pass4RegisterUpliftPotentialRegisterAnalysis extends Pass2Base { Pass4CodeGeneration.AsmCodegenAluState aluState = new Pass4CodeGeneration.AsmCodegenAluState(); try { (new Pass4CodeGeneration(getProgram(), false)).generateStatementAsm(asm, block, statement, aluState, false); - } catch(AsmFragmentTemplateManager.UnknownFragmentException e) { - unknownFragments.add(e.getFragmentDescription()); + } catch(AsmFragmentTemplateSynthesizer.UnknownFragmentException e) { + unknownFragments.add(e.getFragmentSignature()); StringBuilder msg = new StringBuilder(); msg.append("Potential register analysis " + statement); msg.append(" missing fragment " + e.getFragmentSignature()); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftRemains.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftRemains.java index 718f80f74..e5cafb65c 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftRemains.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftRemains.java @@ -1,6 +1,5 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.fragment.AsmFragmentInstanceSpec; import dk.camelot64.kickc.model.*; import java.util.*; @@ -24,7 +23,7 @@ public class Pass4RegisterUpliftRemains extends Pass2Base { } }); - Set unknownFragments = new LinkedHashSet<>(); + Set unknownFragments = new LinkedHashSet<>(); for(LiveRangeEquivalenceClass equivalenceClass : equivalenceClasses) { if(equivalenceClass.getRegister().getType().equals(Registers.RegisterType.ZP_BYTE)) { @@ -38,8 +37,8 @@ public class Pass4RegisterUpliftRemains extends Pass2Base { if(unknownFragments.size() > 0) { getLog().append("MISSING FRAGMENTS"); - for(AsmFragmentInstanceSpec unknownFragment : unknownFragments) { - getLog().append(" " + unknownFragment.toString()); + for(String unknownFragment : unknownFragments) { + getLog().append(" " + unknownFragment); } } diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index e519b7504..0efee46fb 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -32,7 +32,7 @@ public class TestPrograms { public static void tearDown() throws Exception { CompileLog log = new CompileLog(); log.setSysOut(true); - AsmFragmentTemplateUsages.logUsages(log, false, false, false, false); + AsmFragmentTemplateUsages.logUsages(log, false, false, false, true); } @Test