1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-08-02 09:29:35 +00:00

Iplemented graph based synthesis. Working on the kinks - eg. issue #75

This commit is contained in:
jespergravgaard 2018-01-03 01:00:55 +01:00
parent b29ed11a81
commit e9965580a4
12 changed files with 1028 additions and 696 deletions

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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<String, Value> 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;
}

View File

@ -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.
* <p>
* 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<String, AsmFragmentTemplate> bestFragmentCache = new HashMap<>();
/** Caches all asm fragment templates for all encountered signatures. */
private static Map<String, List<AsmFragmentTemplate>> 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<AsmFragmentTemplateSynthesisRule> fragmentSyntheses;
static Map<String, List<AsmFragmentTemplate>> 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<AsmFragmentTemplate> 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<AsmFragmentTemplateSynthesisRule> getFragmentSyntheses() {
if(fragmentSyntheses == null) {
fragmentSyntheses = initFragmentSyntheses();
}
return fragmentSyntheses;
}
private static List<AsmFragmentTemplateSynthesisRule> initFragmentSyntheses() {
Map<String, String> mapZ = new LinkedHashMap<>();
mapZ.put("z2", "z1");
mapZ.put("z3", "z2");
Map<String, String> mapZ2 = new LinkedHashMap<>();
mapZ2.put("z3", "z1");
Map<String, String> mapZ3 = new LinkedHashMap<>();
mapZ3.put("z3", "z2");
Map<String, String> mapC = new LinkedHashMap<>();
mapC.put("c2", "c1");
mapC.put("c3", "c2");
Map<String, String> mapC3 = new LinkedHashMap<>();
mapC3.put("c3", "c2");
Map<String, String> mapZC = new LinkedHashMap<>();
mapZC.putAll(mapZ);
mapZC.putAll(mapC);
Map<String, String> 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<AsmFragmentTemplateSynthesisRule> 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<String> signatures;
public AsmSynthesisPath() {
this.signatures = new ArrayDeque<>();
}
private AsmSynthesisPath(ArrayDeque<String> signatures) {
this.signatures = signatures;
}
AsmSynthesisPath add(String signature) {
ArrayDeque<String> 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.
* <p>
* 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<AsmFragmentTemplate> 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<AsmFragmentTemplate> candidates = new ArrayList<>();
// Synthesize the fragment from other fragments
List<AsmFragmentTemplateSynthesisRule> synths = getFragmentSyntheses();
for(AsmFragmentTemplateSynthesisRule synth : synths) {
List<AsmFragmentTemplate> 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;
}
}
}

View File

@ -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<String, String> 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<String, String> bindMappings;
final private boolean mapSignature;
AsmFragmentTemplateSynthesisRule(String sigMatch, String sigAvoid, String asmPrefix, String sigReplace, String asmPostfix, Map<String, String> 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<String, String> bindMappings) {
AsmFragmentTemplateSynthesisRule(String sigMatch, String sigAvoid, String asmPrefix, String sigReplace, String asmPostfix, Map<String, String> 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<String> 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<AsmFragmentTemplate> synthesize(String signature, AsmFragmentTemplateManager.AsmSynthesisPath path, AsmFragmentTemplateManager.AsmFragmentTemplateSynthesizer synthesizer) {
ArrayList<AsmFragmentTemplate> 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<AsmFragmentTemplate> 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<String> 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<AsmFragmentTemplateSynthesisRule> fragmentSyntheses;
static List<AsmFragmentTemplateSynthesisRule> getSynthesisRules() {
if(fragmentSyntheses == null) {
fragmentSyntheses = initFragmentSyntheses();
}
return candidates;
return fragmentSyntheses;
}
public String getSubSignature() {
return subSignature;
}
private static List<AsmFragmentTemplateSynthesisRule> initFragmentSyntheses() {
Map<String, String> mapZ = new LinkedHashMap<>();
mapZ.put("z2", "z1");
mapZ.put("z3", "z2");
Map<String, String> mapZ2 = new LinkedHashMap<>();
mapZ2.put("z3", "z1");
Map<String, String> mapZ3 = new LinkedHashMap<>();
mapZ3.put("z3", "z2");
Map<String, String> mapC = new LinkedHashMap<>();
mapC.put("c2", "c1");
mapC.put("c3", "c2");
Map<String, String> mapC3 = new LinkedHashMap<>();
mapC3.put("c3", "c2");
Map<String, String> mapZC = new LinkedHashMap<>();
mapZC.putAll(mapZ);
mapZC.putAll(mapC);
Map<String, String> 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<AsmFragmentTemplateSynthesisRule> 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;
}
}

View File

@ -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.
* <p>
* Fragment templates are created by either:
* <ul>
* <li>Loading a fragment file from the fragment-folder.</li>
* <li>synthesising from another fragment using one of the {@link AsmFragmentTemplateSynthesisRule}s.</li>
* </ul>
* <p>
* 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<String, AsmFragmentTemplate> 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<AsmFragmentTemplate> 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<AsmFragmentTemplate> 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<String, AsmFragmentSynthesis> 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<AsmFragmentClobber, AsmFragmentTemplate> bestTemplates;
/** Options for synthesizing the template from sub-fragments using a specific synthesis rule. Forward edges in the synthesis graph. */
private Set<AsmFragmentSynthesisOption> synthesisOptions;
/** Options for synthesizing the other templates from this template using a specific synthesis rule. Backward edges in the synthesis graph. */
private Set<AsmFragmentSynthesisOption> 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<AsmFragmentSynthesisOption> 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<AsmFragmentSynthesisOption> 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<AsmFragmentTemplate> 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<String, AsmFragmentSynthesis> 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<AsmFragmentSynthesis> 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<AsmFragmentSynthesisOption> 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<AsmFragmentTemplate> 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;
}
}
}

View File

@ -35,12 +35,14 @@ public class AsmFragmentTemplateUsages {
*/
public static void logUsages(CompileLog log, boolean logRedundantFiles, boolean logUnusedFiles, boolean logFileDetails, boolean logAllDetails) {
Map<String, List<AsmFragmentTemplate>> fragmentTemplateCache = AsmFragmentTemplateManager.getFragmentTemplateCache();
ArrayList<String> signatures = new ArrayList<>(fragmentTemplateCache.keySet());
Map<String, AsmFragmentTemplateSynthesizer.AsmFragmentSynthesis> synthesisGraph =
AsmFragmentTemplateSynthesizer.SYNTHESIZER.getSynthesisGraph();
ArrayList<String> 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<String> 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<AsmFragmentTemplate> 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<AsmFragmentTemplate> templates = fragmentTemplateCache.get(signature);
AsmFragmentTemplateSynthesizer.AsmFragmentSynthesis asmFragmentSynthesis = synthesisGraph.get(signature);
Collection<AsmFragmentTemplate> 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<AsmFragmentTemplate> fileTemplates = new ArrayList<>();
for(String signature : signatures) {
List<AsmFragmentTemplate> templates = fragmentTemplateCache.get(signature);
Collection<AsmFragmentTemplate> 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<AsmFragmentTemplate> allTemplates = new ArrayList<>();
for(String signature : signatures) {
List<AsmFragmentTemplate> templates = fragmentTemplateCache.get(signature);
for(AsmFragmentTemplate template : templates) {
allTemplates.add(template);
}
Collection<AsmFragmentTemplate> templates = synthesisGraph.get(signature).getBestTemplates();
allTemplates.addAll(templates);
}
logTemplatesByUsage(log, allTemplates);
}

View File

@ -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);
}

View File

@ -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<AsmFragmentInstanceSpec> unknownFragments,
Set<String> unknownFragments,
ScopeRef scope,
Program program
) {
@ -86,7 +86,7 @@ public class Pass4RegisterUpliftCombinations extends Pass2Base {
public static boolean generateCombinationAsm(
RegisterCombination combination,
Program program,
Set<AsmFragmentInstanceSpec> unknownFragments,
Set<String> 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<AsmFragmentInstanceSpec> unknownFragments = new LinkedHashSet<>();
Set<String> unknownFragments = new LinkedHashSet<>();
List<RegisterUpliftScope> 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);
}
}
}

View File

@ -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());

View File

@ -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<AsmFragmentInstanceSpec> unknownFragments = new LinkedHashSet<>();
Set<String> 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);
}
}

View File

@ -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