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

Implmented asm fragment generator

This commit is contained in:
jespergravgaard 2017-05-15 17:01:11 +02:00
parent 4ca3fe0787
commit 4e4fee9035
18 changed files with 428 additions and 21 deletions

View File

@ -0,0 +1,83 @@
package dk.camelot64.kickc.icl;
import java.util.HashMap;
import java.util.Map;
/** Signature and Binding for a Code Generation Fragment */
public abstract class AsmFragmentSignature {
/** The symbol table. */
private SymbolTable symbols;
/** Binding of named values in the fragment to values (constants, variables, ...) .*/
private Map<String, RValue> bindings;
/** The string signature/name of the asm fragment. */
private String signature;
public AsmFragmentSignature(SymbolTable symbols) {
this.symbols = symbols;
this.bindings = new HashMap<>();
}
public RValue getBinding(String name) {
return bindings.get(name);
}
public String getSignature() {
return signature;
}
public void setSignature(String signature) {
this.signature = signature;
}
/** Zero page byte register name indexing. */
private int nextZpByteIdx = 1;
/** Zero page bool register name indexing. */
private int nextZpBoolIdx = 1;
/** Constant byte indexing. */
private int nextConstByteIdx = 1;
/**
* Add bindings of a value.
* @param value The value to bind.
* @return The bound name of the value. If the value has already been bound the existing bound name is returned.
*/
public String bind(RValue value) {
// Find value if it is already bound
for (String name : bindings.keySet()) {
if (value.equals(bindings.get(name))) {
return name;
}
}
if (value instanceof Variable) {
RegisterAllocation.Register register = symbols.getRegister((Variable) value);
if (RegisterAllocation.RegisterType.ZP_BYTE.equals(register.getType())) {
String name = "zpby" + nextZpByteIdx++;
bindings.put(name, value);
return name;
} else if (RegisterAllocation.RegisterType.ZP_BOOL.equals(register.getType())) {
String name = "zpbo" + nextZpBoolIdx++;
bindings.put(name, value);
return name;
} else {
throw new RuntimeException("Binding of register type not supported " + register.getType());
}
} else if (value instanceof ConstantInteger) {
ConstantInteger intValue = (ConstantInteger) value;
if(intValue.getType().equals(SymbolType.BYTE)) {
String name = "coby"+ nextConstByteIdx++;
bindings.put(name, value);
return name;
} else {
throw new RuntimeException("Binding of word integers not supported " + intValue);
}
} else {
throw new RuntimeException("Binding of value type not supported " + value);
}
}
}

View File

@ -0,0 +1,31 @@
package dk.camelot64.kickc.icl;
/** Assembler Fragemnt Signature for an assignment */
public class AsmFragmentSignatureAssignment extends AsmFragmentSignature {
public AsmFragmentSignatureAssignment(StatementAssignment assignment, SymbolTable symbols) {
super(symbols);
setSignature(assignment.getLValue(), assignment.getRValue1(), assignment.getOperator(), assignment.getRValue2());
}
public AsmFragmentSignatureAssignment(LValue lValue, RValue rValue, SymbolTable symbols) {
super(symbols);
setSignature(lValue, null, null, rValue);
}
private void setSignature(LValue lValue, RValue rValue1, Operator operator, RValue rValue2) {
StringBuilder signature = new StringBuilder();
signature.append(bind(lValue));
signature.append("=");
if (rValue1 != null) {
signature.append(bind(rValue1));
}
if (operator != null) {
signature.append(operator.getOperator());
}
signature.append(bind(rValue2));
setSignature(signature.toString());
}
}

View File

@ -0,0 +1,48 @@
package dk.camelot64.kickc.icl;
import java.util.ArrayList;
import java.util.List;
/** A sequence of assembler code. */
public class AsmSequence {
private List<AsmStatement> sequence;
public AsmSequence() {
this.sequence = new ArrayList<>();
}
public List<AsmStatement> getSequence() {
return sequence;
}
public void addAsm(String asm) {
sequence.add(new AsmStatement(asm));
}
public class AsmStatement {
private String asm;
public AsmStatement(String asm) {
this.asm = asm;
}
public String getAsm() {
return asm;
}
@Override
public String toString() {
return asm ;
}
}
@Override
public String toString() {
StringBuffer out = new StringBuffer();
for (AsmStatement asmStatement : sequence) {
out.append(asmStatement+"\n");
}
return out.toString();
}
}

View File

@ -23,4 +23,5 @@ public class ConstantInteger implements Constant {
public String toString() {
return "("+getType().getTypeName()+") "+Integer.toString(number);
}
}

View File

@ -18,6 +18,7 @@ public class Pass2AliasElimination extends Pass2Optimization {
final Map<Variable, Variable> aliases = findAliases();
removeAssignments(aliases.values());
replaceVariables(aliases);
deleteSymbols(aliases.keySet());
for (Variable var : aliases.keySet()) {
Variable alias = aliases.get(var);
System.out.println("Alias " + var + " " + alias);

View File

@ -23,6 +23,7 @@ public class Pass2ConstantPropagation extends Pass2Optimization {
System.out.println("Constant " + constantVar + " " + constantValue);
}
removeAssignments(constants.keySet());
deleteSymbols(constants.keySet());
replaceVariables(constants);
return constants.size() > 0;
}

View File

@ -46,9 +46,9 @@ public abstract class Pass2Optimization {
* @param aliases Variables that have alias values.
*/
public void replaceVariables(final Map<Variable, ? extends RValue> aliases) {
ControlFlowGraphBaseVisitor<Void> visitor = new ControlFlowGraphBaseVisitor() {
ControlFlowGraphBaseVisitor<Void> visitor = new ControlFlowGraphBaseVisitor<Void>() {
@Override
public Object visitAssignment(StatementAssignment assignment) {
public Void visitAssignment(StatementAssignment assignment) {
if(getAlias(aliases, assignment.getLValue()) !=null) {
RValue alias = getAlias(aliases, assignment.getLValue());
if(alias instanceof LValue) {
@ -65,7 +65,7 @@ public abstract class Pass2Optimization {
}
@Override
public Object visitConditionalJump(StatementConditionalJump conditionalJump) {
public Void visitConditionalJump(StatementConditionalJump conditionalJump) {
if(getAlias(aliases, conditionalJump.getCondition())!=null) {
conditionalJump.setCondition(getAlias(aliases, conditionalJump.getCondition()));
}
@ -73,7 +73,7 @@ public abstract class Pass2Optimization {
}
@Override
public Object visitPhi(StatementPhi phi) {
public Void visitPhi(StatementPhi phi) {
if(getAlias(aliases, phi.getLValue())!=null) {
RValue alias = getAlias(aliases, phi.getLValue());
if(alias instanceof LValue) {
@ -164,7 +164,7 @@ public abstract class Pass2Optimization {
/**
* Remove all assignments to specific LValues from the control frlo graph (as they are no longer needed).
* Remove all assignments to specific LValues from the control flow graph (as they are no longer needed).
* @param variables The variables to eliminate
*/
public void removeAssignments(Collection<? extends LValue> variables) {
@ -175,17 +175,26 @@ public abstract class Pass2Optimization {
StatementAssignment assignment = (StatementAssignment) statement;
if (variables.contains(assignment.getLValue())) {
iterator.remove();
symbolTable.remove((Symbol) assignment.getLValue());
}
} else if(statement instanceof StatementPhi) {
StatementPhi phi = (StatementPhi) statement;
if (variables.contains(phi.getLValue())) {
iterator.remove();
symbolTable.remove(phi.getLValue());
}
}
}
}
}
/**
* Remove variables from the symbol table
* @param variables The variables to remove
*/
public void deleteSymbols(Collection<? extends LValue> variables) {
for (LValue variable : variables) {
symbolTable.remove((Symbol) variable);
}
}
}

View File

@ -17,6 +17,7 @@ public class Pass2RedundantPhiElimination extends Pass2Optimization{
public boolean optimize() {
final Map<Variable, RValue> aliases = findRedundantPhis();
removeAssignments(aliases.keySet());
deleteSymbols(aliases.keySet());
replaceVariables(aliases);
for (Variable var : aliases.keySet()) {
RValue alias = aliases.get(var);

View File

@ -16,7 +16,11 @@ public class Pass3RegisterAllocation {
int currentZp = 2;
for (Variable var : symbols.getAllVariables()) {
if(var instanceof VariableIntermediate || var instanceof VariableVersion)
allocation.allocate(var, new RegisterAllocation.RegisterZp(currentZp++));
if(var.getType().equals(SymbolType.BYTE)) {
allocation.allocate(var, new RegisterAllocation.RegisterZpByte(currentZp++));
} else if(var.getType().equals(SymbolType.BOOLEAN)) {
allocation.allocate(var, new RegisterAllocation.RegisterZpBool(currentZp++));
}
}
symbols.setAllocation(allocation);
}

View File

@ -0,0 +1,187 @@
package dk.camelot64.kickc.icl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Code Generation of 6502 Assembler from ICL/SSA Control Flow Graph
*/
public class Pass4CodeGeneration {
private ControlFlowGraph graph;
private SymbolTable symbols;
public Pass4CodeGeneration(ControlFlowGraph graph, SymbolTable symbols) {
this.graph = graph;
this.symbols = symbols;
}
public AsmSequence generate() {
AsmSequence asm = new AsmSequence();
for (ControlFlowBlock block : graph.getAllBlocks()) {
// Generate entry points (if needed)
genBlockEntryPoints(asm, block);
// Generate label
genAsmLabel(asm, block.getLabel());
// Generate statements
genStatements(asm, block);
// Generate exit
ControlFlowBlock defaultSuccessor = block.getDefaultSuccessor();
if (defaultSuccessor != null) {
if(defaultSuccessor.getPredecessors().size()>1) {
String label = defaultSuccessor.getLabel().getName() + "_from_" + block.getLabel().getName();
genAsmJump(asm, label);
} else {
genAsmJump(asm, defaultSuccessor.getLabel().getName());
}
}
}
return asm;
}
private void genStatements(AsmSequence asm, ControlFlowBlock block) {
for (Statement statement : block.getStatements()) {
if (!(statement instanceof StatementPhi)) {
genStatement(asm, statement);
}
}
}
private void genStatement(AsmSequence asm, Statement statement) {
if (statement instanceof StatementAssignment) {
AsmFragmentSignature asmFragmentSignature = new AsmFragmentSignatureAssignment((StatementAssignment) statement, symbols);
asm.addAsm(" // " + statement + " // "+asmFragmentSignature.getSignature());
genAsmFragment(asm, asmFragmentSignature);
} else {
asm.addAsm(" // TODO: " + statement);
}
}
private void genBlockEntryPoints(AsmSequence asm, ControlFlowBlock block) {
List<Statement> statements = block.getStatements();
if (statements.size() > 0 && (statements.get(0) instanceof StatementPhi)) {
for (ControlFlowBlock predecessor : block.getPredecessors()) {
genBlockEntryPoint(asm, block, predecessor);
}
}
}
private void genBlockEntryPoint(AsmSequence asm, ControlFlowBlock block, ControlFlowBlock predecessor) {
genAsmLabel(asm, block.getLabel().getName() + "_from_" + predecessor.getLabel().getName());
for (Statement statement : block.getStatements()) {
if (!(statement instanceof StatementPhi)) {
// No more phi statements to handle
break;
}
StatementPhi phi = (StatementPhi) statement;
for (StatementPhi.PreviousSymbol previousSymbol : phi.getPreviousVersions()) {
if (previousSymbol.getBlock().equals(predecessor)) {
genAsmMove(asm, previousSymbol.getRValue(), phi.getLValue());
break;
}
}
}
genAsmJump(asm, block.getLabel().getName());
}
/**
* Generate an assembler move from an Rvalue to an LValue
*
* @param asm The assembler sequence
* @param rValue The rValue
* @param lValue The lValue
*/
private void genAsmMove(AsmSequence asm, RValue rValue, LValue lValue) {
AsmFragmentSignatureAssignment signature = new AsmFragmentSignatureAssignment(lValue, rValue, symbols);
asm.addAsm(" // " + rValue + " = " + lValue + " // "+signature.getSignature());
genAsmFragment(asm, signature);
}
private void genAsmLabel(AsmSequence asm, Label label) {
genAsmLabel(asm, label.getName());
}
private void genAsmLabel(AsmSequence asm, String label) {
asm.addAsm(label.replace('@', 'B') + ":");
}
private void genAsmJump(AsmSequence asm, String label) {
asm.addAsm(" jmp " + label.replace('B', '_'));
}
/**
* Generate assembler code for an assembler fragment.
*
* @param asm The assembler sequence to generate into.
* @param fragmentSignature Signature of the code fragment to generate
*/
private void genAsmFragment(AsmSequence asm, AsmFragmentSignature fragmentSignature) {
String signature = fragmentSignature.getSignature();
ClassLoader classLoader = this.getClass().getClassLoader();
URL fragmentResource = classLoader.getResource("dk/camelot64/kickc/icl/asm/" + signature + ".asm");
if (fragmentResource == null) {
System.out.println("Fragment not found " + fragmentResource);
asm.addAsm(" // Fragment not found: " + signature);
return;
}
Pattern bindPattern = Pattern.compile(".*\\{([^}]*)}.*");
try {
InputStream fragmentStream = fragmentResource.openStream();
BufferedReader fragmentReader = new BufferedReader(new InputStreamReader(fragmentStream));
String line;
while ((line = fragmentReader.readLine()) != null) {
Matcher matcher = bindPattern.matcher(line);
if(matcher.matches()) {
String name = matcher.group(1);
String bound = getFragmentBoundValue(name, fragmentSignature);
line = line.replaceFirst("\\{[^}]*}", bound);
}
asm.addAsm(" " + line);
}
fragmentReader.close();
fragmentStream.close();
} catch (IOException e) {
throw new RuntimeException("Error reading code fragment " + fragmentResource);
}
}
/**
* Get the value to replace a bound name with from the fragment signature
* @param boundName The name of the bound value in the fragment
* @param fragmentSignature The fragment signature containing the bindings
* @return The bound value to use in the generated ASM code
*/
private String getFragmentBoundValue(String boundName, AsmFragmentSignature fragmentSignature) {
RValue boundValue = fragmentSignature.getBinding(boundName);
String bound;
if(boundValue instanceof Variable) {
RegisterAllocation.Register register = symbols.getRegister((Variable) boundValue);
if(register instanceof RegisterAllocation.RegisterZpByte) {
bound = Integer.toString(((RegisterAllocation.RegisterZpByte) register).getZp());
} else if(register instanceof RegisterAllocation.RegisterZpBool) {
bound = Integer.toString(((RegisterAllocation.RegisterZpBool) register).getZp());
} else {
throw new RuntimeException("Register Type not implemented "+register);
}
} else if(boundValue instanceof ConstantInteger) {
ConstantInteger boundInt = (ConstantInteger) boundValue;
if(boundInt.getType().equals(SymbolType.BYTE)) {
bound = Integer.toString(boundInt.getNumber());
} else {
throw new RuntimeException("Bound Value Type not implemented " + boundValue);
}
} else {
throw new RuntimeException("Bound Value Type not implemented " + boundValue);
}
return bound;
}
}

View File

@ -35,14 +35,16 @@ public class RegisterAllocation {
}
/** The register type. */
public enum RegisterType { ZP };
public enum RegisterType {
ZP_BYTE, ZP_BOOL
};
/** A zero page address used as a register for a single byte variable. */
public static class RegisterZp implements Register {
public static class RegisterZpByte implements Register {
private int zp;
public RegisterZp(int zp) {
public RegisterZpByte(int zp) {
this.zp = zp;
}
@ -52,12 +54,35 @@ public class RegisterAllocation {
@Override
public RegisterType getType() {
return RegisterType.ZP;
return RegisterType.ZP_BYTE;
}
@Override
public String toString() {
return "zp:"+zp;
return "zp byte:"+zp;
}
}
/** A zero page address used as a register for a boolean variable. */
public static class RegisterZpBool implements Register {
private int zp;
public RegisterZpBool(int zp) {
this.zp = zp;
}
public int getZp() {
return zp;
}
@Override
public RegisterType getType() {
return RegisterType.ZP_BOOL;
}
@Override
public String toString() {
return "zp bool:"+zp;
}
}
@ -70,4 +95,5 @@ public class RegisterAllocation {
}
return out.toString();
}
}

View File

@ -0,0 +1,2 @@
lda #{coby1}
sta {zpby1}

View File

@ -0,0 +1,4 @@
lda {zpby2}
clc
adc #{coby1}
sta {zpby1}

View File

@ -0,0 +1,4 @@
lda {zpby2}
clc
adc {zpby3}
sta {zpby1}

View File

@ -0,0 +1,4 @@
lda {zpby2}
sec
sbc #{coby1}
sta {zpby1}

View File

@ -0,0 +1,2 @@
lda {zpby2}
sta {zpby1}

View File

@ -8,7 +8,6 @@ import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.List;
@ -33,12 +32,6 @@ public class Main {
Pass1GenerateControlFlowGraph pass1GenerateControlFlowGraph = new Pass1GenerateControlFlowGraph(symbolTable);
ControlFlowGraph controlFlowGraph = pass1GenerateControlFlowGraph.generate(statementSequence);
System.out.println("SYMBOLS");
System.out.println(symbolTable.toString());
System.out.println("CONTROL FLOW GRAPH");
System.out.println(controlFlowGraph.toString());
Pass1GenerateSingleStaticAssignmentForm pass1GenerateSingleStaticAssignmentForm =
new Pass1GenerateSingleStaticAssignmentForm(symbolTable, controlFlowGraph);
pass1GenerateSingleStaticAssignmentForm.generate();
@ -65,10 +58,16 @@ public class Main {
Pass3RegisterAllocation pass3RegisterAllocation = new Pass3RegisterAllocation(controlFlowGraph, symbolTable);
pass3RegisterAllocation.allocate();
Pass4CodeGeneration pass4CodeGeneration = new Pass4CodeGeneration(controlFlowGraph, symbolTable);
AsmSequence asmSequence = pass4CodeGeneration.generate();
System.out.println("SYMBOLS");
System.out.println(symbolTable.toString());
System.out.println("CONTROL FLOW GRAPH");
System.out.println(controlFlowGraph.toString());
System.out.println("ASSEMBLER");
System.out.println(asmSequence.toString());
}
}

View File

@ -1,5 +1,5 @@
byte a = 0;
word b = 0;
byte b = 0;
byte d=a+b+1;
while(a<10) {
b=b+a;