1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-06-03 07:29:37 +00:00

Working on ASM fragment template interface and implementation.

This commit is contained in:
jespergravgaard 2022-03-29 01:32:47 +02:00
parent 7ba1e7830d
commit 7234060c41
10 changed files with 438 additions and 200 deletions

View File

@ -40,7 +40,6 @@ public interface AsmFragmentTemplate {
*/
KickCParser.AsmLinesContext getBodyAsm();
/**
* Get the registers clobbered by the ASM fragment
*

View File

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

View File

@ -19,9 +19,10 @@ public interface AsmFragmentTemplateFactory {
/**
* Get ASM templates for a signature.
* <p>
* 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 <code>null</code> 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -894,12 +894,13 @@ public class Pass4CodeGeneration {
} else if (statement instanceof StatementAsm) {
StatementAsm statementAsm = (StatementAsm) statement;
HashMap<String, Value> 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();

View File

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