diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java index 81b39b69c..6a96eb14c 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplate.java @@ -40,7 +40,6 @@ public interface AsmFragmentTemplate { */ KickCParser.AsmLinesContext getBodyAsm(); - /** * Get the registers clobbered by the ASM fragment * diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java index 6711a56f5..d9884a762 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateCache.java @@ -47,7 +47,7 @@ public class AsmFragmentTemplateCache { /** Special singleton representing that the fragment can not be synthesized or loaded. */ public static AsmFragmentTemplate NO_SYNTHESIS = - AsmFragmentTemplateParser.create(new AsmFragmentSignature.Singleton("NO_SYNTHESIS"), "NO_SYNTHESIS", null); + new AsmFragmentTemplateImpl(new AsmFragmentSignature.Singleton("NO_SYNTHESIS"), "NO_SYNTHESIS", null, null, null, null); /** Prefix for the fragment hash file header. */ public static final String HASH_HEADER = "//KICKC FRAGMENT CACHE "; @@ -187,7 +187,7 @@ public class AsmFragmentTemplateCache { if(bodyString.startsWith(NO_SYNTHESIS.getBody())) { cache.put(signature, NO_SYNTHESIS); } else { - AsmFragmentTemplate template = AsmFragmentTemplateParser.create(signature, AsmFragmentTemplateParser.fixNewlines(bodyString), targetCpu); + AsmFragmentTemplate template = AsmFragmentTemplateParser.parse(signature, AsmFragmentTemplateParser.fixNewlines(bodyString), targetCpu); cache.put(signature, template); } } diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateFactory.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateFactory.java index 6536ec3f3..cbc867839 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateFactory.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateFactory.java @@ -19,9 +19,10 @@ public interface AsmFragmentTemplateFactory { /** * Get ASM templates for a signature. + *

* Returns zero to many templates that can generate ASM for the signature. * If many templates are returned they will all generate valid ASM, but may have different characteristics such as clobber and cycles. - * Return null or zero templates if the factory cannot find a fragment template for the signature. + * Returns null or zero templates if the factory cannot find a fragment template for the signature. * * @param signature The signature describing the ASM fragment * @return ASM fragment templates that can be used for generating ASM diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateImpl.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateImpl.java index 9fedb4e9b..c5a6a8954 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateImpl.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateImpl.java @@ -1,24 +1,9 @@ package dk.camelot64.kickc.fragment; -import dk.camelot64.cpufamily6502.CpuClobber; -import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.fragment.signature.AsmFragmentSignature; -import dk.camelot64.kickc.model.Program; -import dk.camelot64.kickc.model.Registers; import dk.camelot64.kickc.model.TargetCpu; -import dk.camelot64.kickc.model.statements.StatementSource; -import dk.camelot64.kickc.model.symbols.Label; -import dk.camelot64.kickc.model.symbols.ProgramScope; -import dk.camelot64.kickc.model.symbols.Scope; -import dk.camelot64.kickc.model.symbols.Variable; -import dk.camelot64.kickc.model.types.SymbolType; -import dk.camelot64.kickc.model.values.ConstantInteger; -import dk.camelot64.kickc.model.values.ScopeRef; -import dk.camelot64.kickc.model.values.Value; -import dk.camelot64.kickc.parser.AsmParser; import dk.camelot64.kickc.parser.KickCParser; -import java.util.LinkedHashMap; import java.util.Objects; /** @@ -27,181 +12,64 @@ import java.util.Objects; */ public class AsmFragmentTemplateImpl implements AsmFragmentTemplate { - /** The fragment template signature name. */ - private final AsmFragmentSignature signature; - /** The target CPU. */ - private final TargetCpu targetCpu; - /** The fragment template body */ - private String body; + /** The fragment template signature name. */ + private final AsmFragmentSignature signature; + /** The target CPU. */ + private final TargetCpu targetCpu; + /** The fragment template body */ + private final String body; - /** The parsed ASM lines. Initially null. Non-null after the template is used to generate ASM code. */ - private KickCParser.AsmLinesContext bodyAsm; - /** The ASM clobber of the fragment. Initially null. Non-null after the template is used to generate ASM code.*/ - private AsmFragmentClobber clobber; - /** The cycles consumed by the ASM of the fragment. Initially null. Non-null after the template is used to generate ASM code.*/ - private Double cycles; + /** The parsed ASM lines. Initially null. Non-null after the template is used to generate ASM code. */ + private final KickCParser.AsmLinesContext bodyAsm; + /** The ASM clobber of the fragment. Initially null. Non-null after the template is used to generate ASM code. */ + private final AsmFragmentClobber clobber; + /** The cycles consumed by the ASM of the fragment. Initially null. Non-null after the template is used to generate ASM code. */ + private final Double cycles; - AsmFragmentTemplateImpl(AsmFragmentSignature signature, String body, TargetCpu targetCpu) { - this.signature = signature; - this.body = body; - this.targetCpu = targetCpu; - } + AsmFragmentTemplateImpl(AsmFragmentSignature signature, String body, TargetCpu targetCpu, KickCParser.AsmLinesContext bodyAsm, AsmFragmentClobber clobber, Double cycles) { + this.signature = signature; + this.body = body; + this.targetCpu = targetCpu; + this.bodyAsm = bodyAsm; + this.clobber = clobber; + this.cycles = cycles; + } - /** - * Creates an inline ASM fragment template - * - * @param bodyLines Parsed ASM body - */ - public AsmFragmentTemplateImpl(KickCParser.AsmLinesContext bodyLines, TargetCpu targetCpu) { - this.signature = new AsmFragmentSignature.Singleton("INLINE"); - this.bodyAsm = bodyLines; - this.targetCpu = targetCpu; - } + public AsmFragmentSignature getSignature() { + return signature; + } - /** - * Create a load/store variable - * - * @param name The name - * @param type The type - * @param scope The scope - * @param memoryArea The memory area (zeropage/main memory) - * @param dataSegment The data segment (in main memory) - * @return The new PHI-master variable - */ - public static Variable createLoadStore(String name, SymbolType type, Scope scope, Variable.MemoryArea memoryArea, String dataSegment) { - return new Variable(name, Variable.Kind.LOAD_STORE, type, scope, memoryArea, dataSegment, null); - } + public String getBody() { + return body; + } - /** - * Create a PHI master variable - * - * @param name The name - * @param type The type - * @param scope The scope - * @param memoryArea The memory area (zeropage/main memory) - * @param dataSegment The data segment (in main memory) - * @return The new PHI-master variable - */ - public static Variable createPhiMaster(String name, SymbolType type, Scope scope, Variable.MemoryArea memoryArea, String dataSegment) { - return new Variable(name, Variable.Kind.PHI_MASTER, type, scope, memoryArea, dataSegment, null); - } + @Override + public KickCParser.AsmLinesContext getBodyAsm() { + return bodyAsm; + } - /** - * Initialize the fields that require parsing the ASM (bodyAsm, clobber, cycles). - */ - private void initAsm() { - String signatureName = signature.getName(); + public TargetCpu getTargetCpu() { + return targetCpu; + } - // Parse the body ASM - this.bodyAsm = AsmParser.parseAsm(this.body, new StatementSource(signature + ".asm", 1, 1, this.body, -1, -1)); - // Generate a dummy instance to find clobber & cycles - ProgramScope scope = new ProgramScope(); - LinkedHashMap bindings = new LinkedHashMap<>(); - { - Variable master = createPhiMaster("z", SymbolType.BYTE, scope, Variable.MemoryArea.ZEROPAGE_MEMORY, null); - Variable v1 = Variable.createPhiVersion(master, 1); v1.setName("z1"); - Variable v2 = Variable.createPhiVersion(master, 2); v2.setName("z2"); - Variable v3 = Variable.createPhiVersion(master, 3); v3.setName("z3"); - Variable v4 = Variable.createPhiVersion(master, 4); v4.setName("z4"); - Variable v5 = Variable.createPhiVersion(master, 5); v5.setName("z5"); - Variable v6 = Variable.createPhiVersion(master, 6); v6.setName("z6"); - v1.setAllocation(new Registers.RegisterZpMem(2, 1)); - v2.setAllocation(new Registers.RegisterZpMem(4, 1)); - v3.setAllocation(new Registers.RegisterZpMem(6, 1)); - v4.setAllocation(new Registers.RegisterZpMem(8, 1)); - v5.setAllocation(new Registers.RegisterZpMem(9, 1)); - v6.setAllocation(new Registers.RegisterZpMem(10, 1)); - if(signatureName.contains("z1")) bindings.put("z1", v1); - if(signatureName.contains("z2")) bindings.put("z2", v2); - if(signatureName.contains("z3")) bindings.put("z3", v3); - if(signatureName.contains("z4")) bindings.put("z4", v4); - if(signatureName.contains("z5")) bindings.put("z5", v5); - if(signatureName.contains("z6")) bindings.put("z6", v6); - } - { - Variable v1 = createLoadStore("m1", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); - Variable v2 = createLoadStore("m2", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); - Variable v3 = createLoadStore("m3", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); - Variable v4 = createLoadStore("m4", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); - Variable v5 = createLoadStore("m5", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); - Variable v6 = createLoadStore("m6", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); - v1.setAllocation(new Registers.RegisterMainMem(v1.getVariableRef(), 1, null, false)); - v2.setAllocation(new Registers.RegisterMainMem(v2.getVariableRef(), 1, null, false)); - v3.setAllocation(new Registers.RegisterMainMem(v3.getVariableRef(), 1, null, false)); - v4.setAllocation(new Registers.RegisterMainMem(v4.getVariableRef(), 1, null, false)); - v5.setAllocation(new Registers.RegisterMainMem(v5.getVariableRef(), 1, null, false)); - v6.setAllocation(new Registers.RegisterMainMem(v6.getVariableRef(), 1, null, false)); - if(signatureName.contains("m1")) bindings.put("m1", v1); - if(signatureName.contains("m2")) bindings.put("m2", v2); - if(signatureName.contains("m3")) bindings.put("m3", v3); - if(signatureName.contains("m4")) bindings.put("m4", v4); - if(signatureName.contains("m5")) bindings.put("m5", v5); - if(signatureName.contains("m6")) bindings.put("m6", v6); - } - if(signatureName.contains("c1")) bindings.put("c1", new ConstantInteger(310L)); - if(signatureName.contains("c2")) bindings.put("c2", new ConstantInteger(320L)); - if(signatureName.contains("c3")) bindings.put("c3", new ConstantInteger(330L)); - if(signatureName.contains("c4")) bindings.put("c4", new ConstantInteger(340L)); - if(signatureName.contains("c5")) bindings.put("c5", new ConstantInteger(350L)); - if(signatureName.contains("c6")) bindings.put("c6", new ConstantInteger(360L)); - if(signatureName.contains("la1")) bindings.put("la1", new Label("@1", scope, true)); - if(signatureName.startsWith("call_")) bindings.put("la1", new Label("@1", scope, true)); - AsmFragmentInstance fragmentInstance = - new AsmFragmentInstance(new Program(), signature, ScopeRef.ROOT, this, bindings); - AsmProgram asm = new AsmProgram(targetCpu); - asm.startChunk(ScopeRef.ROOT, null, signature.getName()); - fragmentInstance.generate(asm); - CpuClobber cpuClobber = asm.getClobber(); - this.clobber = new AsmFragmentClobber(cpuClobber); - asm.addStash(); - this.cycles = asm.getCycles(); - } + public AsmFragmentClobber getClobber() { + return clobber; + } - public AsmFragmentSignature getSignature() { - return signature; - } + public double getCycles() { + return cycles; + } - public String getBody() { - return body; - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AsmFragmentTemplateImpl that = (AsmFragmentTemplateImpl) o; + return Objects.equals(signature, that.signature) && targetCpu == that.targetCpu && Objects.equals(body, that.body); + } - @Override - public KickCParser.AsmLinesContext getBodyAsm() { - if(bodyAsm == null) { - initAsm(); - } - return bodyAsm; - } - - public TargetCpu getTargetCpu() { - return targetCpu; - } - - public AsmFragmentClobber getClobber() { - if(clobber == null) { - initAsm(); - } - return clobber; - } - - public double getCycles() { - if(cycles == null) { - initAsm(); - } - return cycles; - } - - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - AsmFragmentTemplateImpl that = (AsmFragmentTemplateImpl) o; - return Objects.equals(signature, that.signature) && targetCpu == that.targetCpu && Objects.equals(body, that.body); - } - - @Override - public int hashCode() { - return Objects.hash(signature, targetCpu, body); - } + @Override + public int hashCode() { + return Objects.hash(signature, targetCpu, body); + } } diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateParser.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateParser.java index 5987ee600..4b8a58526 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateParser.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateParser.java @@ -1,10 +1,29 @@ package dk.camelot64.kickc.fragment; +import dk.camelot64.cpufamily6502.CpuClobber; +import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.fragment.signature.AsmFragmentSignature; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.Registers; import dk.camelot64.kickc.model.TargetCpu; +import dk.camelot64.kickc.model.statements.StatementSource; +import dk.camelot64.kickc.model.symbols.Label; +import dk.camelot64.kickc.model.symbols.ProgramScope; +import dk.camelot64.kickc.model.symbols.Scope; +import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.values.ConstantInteger; +import dk.camelot64.kickc.model.values.ScopeRef; +import dk.camelot64.kickc.model.values.Value; +import dk.camelot64.kickc.parser.AsmParser; +import dk.camelot64.kickc.parser.KickCParser; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.TerminalNode; + +import java.util.LinkedHashMap; /** - * + * Creates ASM fragment templates by parsing ASM in string form. */ public class AsmFragmentTemplateParser { @@ -12,12 +31,191 @@ public class AsmFragmentTemplateParser { * Creates an ASM fragment from a body. * * @param signature The ASM fragment signature - * @param body The ASM body + * @param body The ASM body * @param targetCpu The target CPU * @return The ASM fragment template */ - public static AsmFragmentTemplate create(AsmFragmentSignature signature, String body, TargetCpu targetCpu) { - return new AsmFragmentTemplateImpl(signature, body, targetCpu); + public static AsmFragmentTemplate parse(AsmFragmentSignature signature, String body, TargetCpu targetCpu) { + final ParsedAsmBody parsedAsmBody = parseAsm(signature, targetCpu, body); + final AsmFragmentTemplateImpl asmFragmentTemplate = new AsmFragmentTemplateImpl(signature, body, targetCpu, parsedAsmBody.bodyAsm, parsedAsmBody.clobber, parsedAsmBody.cycles); + // assertBodyPrint(signature, body, parsedAsmBody.bodyAsm); + return asmFragmentTemplate; + } + + /** + * Assert that the body can be printed and parsed again - adn result in the same parsed value + * @param signature The signature + * @param body The original string body + * @param parsedBody The parsed body + */ + private static void assertBodyPrint(AsmFragmentSignature signature, String body, KickCParser.AsmLinesContext parsedBody) { + final String printedBody = AsmTemplateBodyPrinter.print(parsedBody); + try { + final KickCParser.AsmLinesContext reparsed = AsmParser.parseAsm(printedBody, new StatementSource(parsedBody)); + if (!isEqual(reparsed, parsedBody)) + throw new RuntimeException("ASM fragment body mismatch for " + signature.getName() + ".\nACTUAL: '" + normalize(printedBody) + "'\nEXPECTED:'" + normalize(body) + "'\n"); + } catch (Throwable e) { + throw new RuntimeException("Error parsing printed ASM fragment body for " + signature.getName() + ".\nPRINTED: '" + normalize(printedBody) + "'\nORIGINAL:'" + normalize(body) + "'\n"); + } + } + + private static boolean isEqual(ParseTree ctx1, ParseTree ctx2) { + if (ctx1 == ctx2) return true; + if (ctx2 == null || ctx1.getClass() != ctx2.getClass()) return false; + if (ctx1 instanceof TerminalNode) + return ctx1.getText().equals(ctx2.getText()); + if (ctx1.getChildCount() != ctx2.getChildCount()) return false; + for (int i = 0; i < ctx1.getChildCount(); i++) { + if (!isEqual(ctx1.getChild(i), ctx2.getChild(i))) return false; + } + return true; + } + + private static String normalize(String body) { + body = body.replace("\r", ""); + body = body.replace("\n", " "); + body = body.replace(" ", " "); + while (body.endsWith(" ")) { + body = body.substring(0, body.length() - 1); + } + return body; + } + + /** + * Create an inline ASM fragment template. + * + * @param signature The ASM fragment signature + * @param asmBody The parsed ASM body + * @param targetCpu The target CPU + * @return The ASM fragment template + */ + public static AsmFragmentTemplate inline(AsmFragmentSignature signature, KickCParser.AsmLinesContext asmBody, TargetCpu targetCpu) { + return new AsmFragmentTemplateImpl(signature, null, targetCpu, asmBody, null, null); + } + + /** + * Create a load/store variable + * + * @param name The name + * @param type The type + * @param scope The scope + * @param memoryArea The memory area (zeropage/main memory) + * @param dataSegment The data segment (in main memory) + * @return The new PHI-master variable + */ + private static Variable createLoadStore(String name, SymbolType type, Scope scope, Variable.MemoryArea memoryArea, String dataSegment) { + return new Variable(name, Variable.Kind.LOAD_STORE, type, scope, memoryArea, dataSegment, null); + } + + /** + * Create a PHI master variable + * + * @param name The name + * @param type The type + * @param scope The scope + * @param memoryArea The memory area (zeropage/main memory) + * @param dataSegment The data segment (in main memory) + * @return The new PHI-master variable + */ + private static Variable createPhiMaster(String name, SymbolType type, Scope scope, Variable.MemoryArea memoryArea, String dataSegment) { + return new Variable(name, Variable.Kind.PHI_MASTER, type, scope, memoryArea, dataSegment, null); + } + + /** + * Result from parsing an ASM string body. + */ + private static class ParsedAsmBody { + final KickCParser.AsmLinesContext bodyAsm; + final AsmFragmentClobber clobber; + final Double cycles; + + public ParsedAsmBody(KickCParser.AsmLinesContext bodyAsm, AsmFragmentClobber clobber, Double cycles) { + this.bodyAsm = bodyAsm; + this.clobber = clobber; + this.cycles = cycles; + } + } + + public static int parseCount = 0; + + /** + * Initialize the fields that require parsing the ASM (bodyAsm, clobber, cycles). + */ + private static ParsedAsmBody parseAsm(AsmFragmentSignature signature, TargetCpu targetCpu, String body) { + String signatureName = signature.getName(); + + parseCount++; + if ((parseCount % 1000) == 0) System.out.println("Parsed " + parseCount + " ASM fragment bodies."); + + // Parse the body ASM + final KickCParser.AsmLinesContext bodyAsm = AsmParser.parseAsm(body, new StatementSource(signature + ".asm", 1, 1, body, -1, -1)); + // Generate a dummy instance to find clobber & cycles + ProgramScope scope = new ProgramScope(); + LinkedHashMap bindings = new LinkedHashMap<>(); + { + Variable master = createPhiMaster("z", SymbolType.BYTE, scope, Variable.MemoryArea.ZEROPAGE_MEMORY, null); + Variable v1 = Variable.createPhiVersion(master, 1); + v1.setName("z1"); + Variable v2 = Variable.createPhiVersion(master, 2); + v2.setName("z2"); + Variable v3 = Variable.createPhiVersion(master, 3); + v3.setName("z3"); + Variable v4 = Variable.createPhiVersion(master, 4); + v4.setName("z4"); + Variable v5 = Variable.createPhiVersion(master, 5); + v5.setName("z5"); + Variable v6 = Variable.createPhiVersion(master, 6); + v6.setName("z6"); + v1.setAllocation(new Registers.RegisterZpMem(2, 1)); + v2.setAllocation(new Registers.RegisterZpMem(4, 1)); + v3.setAllocation(new Registers.RegisterZpMem(6, 1)); + v4.setAllocation(new Registers.RegisterZpMem(8, 1)); + v5.setAllocation(new Registers.RegisterZpMem(9, 1)); + v6.setAllocation(new Registers.RegisterZpMem(10, 1)); + if (signatureName.contains("z1")) bindings.put("z1", v1); + if (signatureName.contains("z2")) bindings.put("z2", v2); + if (signatureName.contains("z3")) bindings.put("z3", v3); + if (signatureName.contains("z4")) bindings.put("z4", v4); + if (signatureName.contains("z5")) bindings.put("z5", v5); + if (signatureName.contains("z6")) bindings.put("z6", v6); + } + { + Variable v1 = createLoadStore("m1", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); + Variable v2 = createLoadStore("m2", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); + Variable v3 = createLoadStore("m3", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); + Variable v4 = createLoadStore("m4", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); + Variable v5 = createLoadStore("m5", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); + Variable v6 = createLoadStore("m6", SymbolType.BYTE, scope, Variable.MemoryArea.MAIN_MEMORY, null); + v1.setAllocation(new Registers.RegisterMainMem(v1.getVariableRef(), 1, null, false)); + v2.setAllocation(new Registers.RegisterMainMem(v2.getVariableRef(), 1, null, false)); + v3.setAllocation(new Registers.RegisterMainMem(v3.getVariableRef(), 1, null, false)); + v4.setAllocation(new Registers.RegisterMainMem(v4.getVariableRef(), 1, null, false)); + v5.setAllocation(new Registers.RegisterMainMem(v5.getVariableRef(), 1, null, false)); + v6.setAllocation(new Registers.RegisterMainMem(v6.getVariableRef(), 1, null, false)); + if (signatureName.contains("m1")) bindings.put("m1", v1); + if (signatureName.contains("m2")) bindings.put("m2", v2); + if (signatureName.contains("m3")) bindings.put("m3", v3); + if (signatureName.contains("m4")) bindings.put("m4", v4); + if (signatureName.contains("m5")) bindings.put("m5", v5); + if (signatureName.contains("m6")) bindings.put("m6", v6); + } + if (signatureName.contains("c1")) bindings.put("c1", new ConstantInteger(310L)); + if (signatureName.contains("c2")) bindings.put("c2", new ConstantInteger(320L)); + if (signatureName.contains("c3")) bindings.put("c3", new ConstantInteger(330L)); + if (signatureName.contains("c4")) bindings.put("c4", new ConstantInteger(340L)); + if (signatureName.contains("c5")) bindings.put("c5", new ConstantInteger(350L)); + if (signatureName.contains("c6")) bindings.put("c6", new ConstantInteger(360L)); + if (signatureName.contains("la1")) bindings.put("la1", new Label("@1", scope, true)); + if (signatureName.startsWith("call_")) bindings.put("la1", new Label("@1", scope, true)); + final AsmFragmentTemplateImpl fragmentTemplate = new AsmFragmentTemplateImpl(signature, body, targetCpu, bodyAsm, null, null); + AsmFragmentInstance fragmentInstance = + new AsmFragmentInstance(new Program(), signature, ScopeRef.ROOT, fragmentTemplate, bindings); + AsmProgram asm = new AsmProgram(targetCpu); + asm.startChunk(ScopeRef.ROOT, null, signature.getName()); + fragmentInstance.generate(asm); + CpuClobber cpuClobber = asm.getClobber(); + asm.addStash(); + return new ParsedAsmBody(bodyAsm, new AsmFragmentClobber(cpuClobber), asm.getCycles()); } /** @@ -29,10 +227,10 @@ public class AsmFragmentTemplateParser { * @return The body with fixed newlines */ public static String fixNewlines(String body) { - body = body.replace("\r", ""); - while (body.length() > 0 && body.charAt(body.length() - 1) == '\n') { - body = body.substring(0, body.length() - 1); - } - return body; + body = body.replace("\r", ""); + while (body.length() > 0 && body.charAt(body.length() - 1) == '\n') { + body = body.substring(0, body.length() - 1); + } + return body; } } diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmTemplateBodyPrinter.java b/src/main/java/dk/camelot64/kickc/fragment/AsmTemplateBodyPrinter.java new file mode 100644 index 000000000..bbbee9939 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmTemplateBodyPrinter.java @@ -0,0 +1,167 @@ +package dk.camelot64.kickc.fragment; + +import dk.camelot64.kickc.parser.KickCParser; +import dk.camelot64.kickc.parser.KickCParserBaseVisitor; +import org.antlr.v4.runtime.tree.TerminalNode; + +import java.util.List; + +/** + * Prints ASM template body as string. + */ +public class AsmTemplateBodyPrinter extends KickCParserBaseVisitor { + + private StringBuffer out; + + private AsmTemplateBodyPrinter(StringBuffer out) { + this.out = out; + } + + public static String print(KickCParser.AsmLinesContext asmBody) { + final StringBuffer stringBuffer = new StringBuffer(); + final AsmTemplateBodyPrinter bodyPrinter = new AsmTemplateBodyPrinter(stringBuffer); + bodyPrinter.visit(asmBody); + return stringBuffer.toString(); + } + + private void printTags(List tags) { + tags.stream().forEach(tag -> out.append(" ").append(tag)); + } + + @Override + public Object visitAsmLabelName(KickCParser.AsmLabelNameContext ctx) { + out.append(ctx.ASM_NAME()).append(ctx.ASM_COLON()); + final List tags = ctx.ASM_TAG(); + printTags(tags); + out.append("\n"); + return null; + } + + + @Override + public Object visitAsmLabelReplace(KickCParser.AsmLabelReplaceContext ctx) { + out.append(ctx.ASM_CURLY_BEGIN()); + out.append(ctx.ASM_NAME()); + out.append(ctx.ASM_CURLY_END()); + out.append(ctx.ASM_COLON()); + printTags(ctx.ASM_TAG()); + out.append("\n"); + return null; + } + + @Override + public Object visitAsmLabelMulti(KickCParser.AsmLabelMultiContext ctx) { + out.append(ctx.ASM_MULTI_NAME()); + out.append(ctx.ASM_COLON()); + printTags(ctx.ASM_TAG()); + out.append("\n"); + return null; + } + + @Override + public Object visitAsmBytes(KickCParser.AsmBytesContext ctx) { + printTags(ctx.ASM_TAG()); + return null; + } + + @Override + public Object visitAsmInstruction(KickCParser.AsmInstructionContext ctx) { + out.append(ctx.ASM_MNEMONIC()); + if (ctx.asmParamMode() != null) { + out.append(" "); + this.visit(ctx.asmParamMode()); + } + printTags(ctx.ASM_TAG()); + out.append("\n"); + return null; + } + + @Override + public Object visitAsmModeImm(KickCParser.AsmModeImmContext ctx) { + out.append(ctx.ASM_IMM()); + this.visit(ctx.asmExpr()); + return null; + } + + @Override + public Object visitAsmModeAbs(KickCParser.AsmModeAbsContext ctx) { + this.visit(ctx.asmExpr()); + return null; + } + + @Override + public Object visitAsmModeAbs2(KickCParser.AsmModeAbs2Context ctx) { + this.visit(ctx.asmExpr(0)); + out.append(ctx.ASM_COMMA()); + this.visit(ctx.asmExpr(1)); + return null; + } + + @Override + public Object visitAsmModeInd(KickCParser.AsmModeIndContext ctx) { + out.append(ctx.ASM_PAR_BEGIN()); + this.visit(ctx.asmExpr()); + out.append(ctx.ASM_PAR_END()); + return null; + } + + @Override + public Object visitAsmModeIndIdxXY(KickCParser.AsmModeIndIdxXYContext ctx) { + out.append(ctx.ASM_PAR_BEGIN()); + this.visit(ctx.asmExpr()); + out.append(ctx.ASM_PAR_END()); + out.append(ctx.ASM_COMMA()); + out.append(ctx.ASM_NAME()); + return null; + } + + @Override + public Object visitAsmExprPar(KickCParser.AsmExprParContext ctx) { + out.append(ctx.ASM_BRACKET_BEGIN()); + this.visit(ctx.asmExpr()); + out.append(ctx.ASM_BRACKET_END()); + return null; + } + + @Override + public Object visitAsmExprInt(KickCParser.AsmExprIntContext ctx) { + out.append(ctx.ASM_NUMBER()); + return null; + } + + @Override + public Object visitAsmExprLabel(KickCParser.AsmExprLabelContext ctx) { + out.append(ctx.ASM_NAME()); + return null; + } + + @Override + public Object visitAsmExprLabelRel(KickCParser.AsmExprLabelRelContext ctx) { + out.append(ctx.ASM_MULTI_REL()); + out.append(" "); + return null; + } + + @Override + public Object visitAsmExprUnary(KickCParser.AsmExprUnaryContext ctx) { + out.append((TerminalNode) ctx.getChild(0)); + this.visit(ctx.asmExpr()); + return null; + } + + @Override + public Object visitAsmExprBinary(KickCParser.AsmExprBinaryContext ctx) { + this.visit(ctx.asmExpr(0)); + out.append((TerminalNode) ctx.getChild(1)); + this.visit(ctx.asmExpr(1)); + return null; + } + + @Override + public Object visitAsmExprReplace(KickCParser.AsmExprReplaceContext ctx) { + out.append(ctx.ASM_CURLY_BEGIN()); + out.append(ctx.ASM_NAME()); + out.append(ctx.ASM_CURLY_END()); + return null; + } +} diff --git a/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesisRuleRegex.java b/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesisRuleRegex.java index bb1328c6a..a22be95e2 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesisRuleRegex.java +++ b/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesisRuleRegex.java @@ -1,7 +1,6 @@ package dk.camelot64.kickc.fragment.synthesis; import dk.camelot64.kickc.fragment.AsmFragmentTemplate; -import dk.camelot64.kickc.fragment.AsmFragmentTemplateImpl; import dk.camelot64.kickc.fragment.AsmFragmentTemplateParser; import dk.camelot64.kickc.fragment.signature.AsmFragmentSignature; @@ -154,7 +153,7 @@ class AsmFragmentTemplateSynthesisRuleRegex implements AsmFragmentTemplateSynthe if(newFragment.length() > 0 && newFragment.charAt(newFragment.length() - 1) == '\n') { newFragment = new StringBuilder(newFragment.substring(0, newFragment.length() - 1)); } - return AsmFragmentTemplateParser.create(signature, newFragment.toString(), subTemplate.getTargetCpu()); + return AsmFragmentTemplateParser.parse(signature, newFragment.toString(), subTemplate.getTargetCpu()); } static String regexpRewriteSignature(String signature, String match, String replace) { diff --git a/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesizer.java b/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesizer.java index 81e9fd94e..f692fe9d0 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesizer.java +++ b/src/main/java/dk/camelot64/kickc/fragment/synthesis/AsmFragmentTemplateSynthesizer.java @@ -365,7 +365,7 @@ public class AsmFragmentTemplateSynthesizer { CharStream fragmentCharStream = CharStreams.fromStream(fragmentStream); body = AsmFragmentTemplateParser.fixNewlines(fragmentCharStream.toString()); } - final AsmFragmentTemplate fragment = AsmFragmentTemplateParser.create(signature, body, targetCpu); + final AsmFragmentTemplate fragment = AsmFragmentTemplateParser.parse(signature, body, targetCpu); return new AsmFragmentSynthesisResult(fragment, true, false, null, new ArrayList<>()); } catch (IOException e) { throw new RuntimeException("Error loading fragment file " + signature, e); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 8b3a48298..b538367eb 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -894,12 +894,13 @@ public class Pass4CodeGeneration { } else if (statement instanceof StatementAsm) { StatementAsm statementAsm = (StatementAsm) statement; HashMap bindings = new HashMap<>(); + final AsmFragmentSignature.Singleton signature = new AsmFragmentSignature.Singleton("INLINE"); AsmFragmentInstance asmFragmentInstance = new AsmFragmentInstance( program, - new AsmFragmentSignature.Singleton("INLINE"), + signature, block.getScope(), - new AsmFragmentTemplateImpl(statementAsm.getAsmLines(), program.getTargetCpu()), + AsmFragmentTemplateParser.inline(signature, statementAsm.getAsmLines(), program.getTargetCpu()), bindings); asmFragmentInstance.generate(asm); AsmChunk currentChunk = asm.getCurrentChunk(); diff --git a/src/test/java/dk/camelot64/kickc/test/TestFragments.java b/src/test/java/dk/camelot64/kickc/test/TestFragments.java index 850a99c80..5444d83f1 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestFragments.java +++ b/src/test/java/dk/camelot64/kickc/test/TestFragments.java @@ -33,6 +33,11 @@ public class TestFragments { log.setSysOut(true); } + // @Test + // public void testEvilSynthesis() { + // testFragmentExists("vbuz1=pbuz2_derefidx_(pbuz3_derefidx_vbuz4)_plus_pbuz5_derefidx_(pbuz6_derefidx_vbuz4)"); + // } + @Test public void testSynthesis() { testFragmentExists("pbuz1=pbuz2_plus_pwuc1_derefidx_vbuxx");