mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-12-29 08:31:03 +00:00
Working on ASM fragment template interface and implementation.
This commit is contained in:
parent
7ba1e7830d
commit
7234060c41
@ -40,7 +40,6 @@ public interface AsmFragmentTemplate {
|
||||
*/
|
||||
KickCParser.AsmLinesContext getBodyAsm();
|
||||
|
||||
|
||||
/**
|
||||
* Get the registers clobbered by the ASM fragment
|
||||
*
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user