diff --git a/src/dk/camelot64/kickc/TODO.txt b/src/dk/camelot64/kickc/TODO.txt index 706a7674b..801206b66 100644 --- a/src/dk/camelot64/kickc/TODO.txt +++ b/src/dk/camelot64/kickc/TODO.txt @@ -40,4 +40,4 @@ Real Usage - Implement library for fast multiply (mul.asm) - Implement spline library (spline.asm) - Implement polygon filler for complex polygons. -- Implement true type font renderer. \ No newline at end of file +- Implement a true type font renderer. \ No newline at end of file diff --git a/src/dk/camelot64/kickc/icl/Pass1ProcedureCallParameters.java b/src/dk/camelot64/kickc/icl/Pass1ProcedureCallParameters.java index 713add442..89952d6a0 100644 --- a/src/dk/camelot64/kickc/icl/Pass1ProcedureCallParameters.java +++ b/src/dk/camelot64/kickc/icl/Pass1ProcedureCallParameters.java @@ -40,7 +40,14 @@ public class Pass1ProcedureCallParameters extends ControlFlowGraphCopyVisitor { splitCurrentBlock(scope.addLabelIntermediate()); if(!SymbolTypeBasic.VOID.equals(procedure.getReturnType())) { addStatementToCurrentBlock(new StatementAssignment(origCall.getLValue(), procReturnVar)); + } { + // No return type. Remove variable receiving the result. + LValue lValue = origCall.getLValue(); + if(lValue instanceof Variable) { + scope.remove((Variable) lValue); + } } + return null; } diff --git a/src/dk/camelot64/kickc/icl/Pass2AssertSymbols.java b/src/dk/camelot64/kickc/icl/Pass2AssertSymbols.java new file mode 100644 index 000000000..bb15762a1 --- /dev/null +++ b/src/dk/camelot64/kickc/icl/Pass2AssertSymbols.java @@ -0,0 +1,150 @@ +package dk.camelot64.kickc.icl; + +import java.util.HashSet; + +/** Asserts that the symbols in the symbol table match exactly the symbols in the program */ +public class Pass2AssertSymbols extends Pass2SsaAssertion { + + public Pass2AssertSymbols(ControlFlowGraph graph, Scope scope) { + super(graph, scope); + } + + @Override + public void check() throws AssertionFailed { + SymbolFinder symbolFinder = new SymbolFinder(); + symbolFinder.visitGraph(getGraph()); + HashSet codeSymbols = symbolFinder.getSymbols(); + // Check that all symbols found in the code is also oin the symbol tabel + for (Symbol codeSymbol : codeSymbols) { + if(codeSymbol.getFullName().equals("@RETURN")) continue; + Symbol tableSymbol = getSymbols().getSymbol(codeSymbol.getFullName()); + if(tableSymbol==null) { + throw new AssertionFailed("Compile process error. Symbol found in code, but not in symbol table. "+codeSymbol.getFullName()); + } + } + // Check that all symbols in the symbol table is also in the code + HashSet tableSymbols = getAllSymbols(getSymbols()); + for (Symbol tableSymbol : tableSymbols) { + if(tableSymbol instanceof VariableUnversioned) continue; + Symbol codeSymbol = null; + String codeSymbolFullName = tableSymbol.getFullName(); + for (Symbol symbol : codeSymbols) { + if(codeSymbolFullName.equals(symbol.getFullName())) { + codeSymbol = symbol; + break; + } + } + if(codeSymbol==null) { + throw new AssertionFailed("Compile process error. Symbol found in symbol table, but not in code. "+ codeSymbolFullName); + } + } + } + + private HashSet getAllSymbols(Scope symbols) { + HashSet allSymbols = new HashSet<>(); + for (Symbol symbol : symbols.getSymbols()) { + allSymbols.add(symbol); + if(symbol instanceof Scope) { + HashSet subSymbols = getAllSymbols((Scope) symbol); + allSymbols.addAll(subSymbols); + } + } + return allSymbols; + } + + private static class SymbolFinder extends ControlFlowGraphBaseVisitor { + + private HashSet symbols = new HashSet<>(); + + public HashSet getSymbols() { + return symbols; + } + + private void addSymbol(Value symbol) { + if (symbol instanceof Symbol) { + symbols.add((Symbol) symbol); + } else if(symbol instanceof PointerDereferenceIndexed) { + addSymbol(((PointerDereferenceIndexed) symbol).getPointer()); + addSymbol(((PointerDereferenceIndexed) symbol).getIndex()); + } else if(symbol instanceof PointerDereference) { + addSymbol(((PointerDereference) symbol).getPointer()); + } + } + + @Override + public Void visitBlock(ControlFlowBlock block) { + addSymbol(block.getLabel()); + addSymbol(block.getDefaultSuccessor()); + addSymbol(block.getConditionalSuccessor()); + addSymbol(block.getCallSuccessor()); + return super.visitBlock(block); + } + + @Override + public Void visitProcedureBegin(StatementProcedureBegin statement) { + symbols.add(statement.getProcedure()); + return super.visitProcedureBegin(statement); + } + + @Override + public Void visitProcedureEnd(StatementProcedureEnd statement) { + symbols.add(statement.getProcedure()); + return super.visitProcedureEnd(statement); + } + + @Override + public Void visitReturn(StatementReturn aReturn) { + addSymbol(aReturn.getValue()); + return super.visitReturn(aReturn); + } + + @Override + public Void visitConditionalJump(StatementConditionalJump conditionalJump) { + addSymbol(conditionalJump.getRValue1()); + addSymbol(conditionalJump.getRValue2()); + addSymbol(conditionalJump.getDestination()); + return super.visitConditionalJump(conditionalJump); + } + + @Override + public Void visitAssignment(StatementAssignment assignment) { + addSymbol(assignment.getLValue()); + addSymbol(assignment.getRValue1()); + addSymbol(assignment.getRValue2()); + return super.visitAssignment(assignment); + } + + @Override + public Void visitJump(StatementJump jump) { + addSymbol(jump.getDestination()); + return super.visitJump(jump); + } + + @Override + public Void visitJumpTarget(StatementLabel jumpTarget) { + addSymbol(jumpTarget.getLabel()); + return super.visitJumpTarget(jumpTarget); + } + + @Override + public Void visitCall(StatementCall callLValue) { + addSymbol(callLValue.getLValue()); + addSymbol(callLValue.getProcedure()); + if(callLValue.getParameters()!=null) { + for (RValue param : callLValue.getParameters()) { + addSymbol(param); + } + } + return super.visitCall(callLValue); + } + + @Override + public Void visitPhi(StatementPhi phi) { + addSymbol(phi.getLValue()); + for (StatementPhi.PreviousSymbol previousSymbol : phi.getPreviousVersions()) { + addSymbol(previousSymbol.getRValue()); + } + return super.visitPhi(phi); + } + } +} diff --git a/src/dk/camelot64/kickc/icl/Pass2SsaAssertion.java b/src/dk/camelot64/kickc/icl/Pass2SsaAssertion.java new file mode 100644 index 000000000..89fa7534a --- /dev/null +++ b/src/dk/camelot64/kickc/icl/Pass2SsaAssertion.java @@ -0,0 +1,30 @@ +package dk.camelot64.kickc.icl; + +/** Assertion checking that a pass 2 representation of the program is consistent */ +public abstract class Pass2SsaAssertion { + + private ControlFlowGraph graph; + private Scope scope; + + public Pass2SsaAssertion(ControlFlowGraph graph, Scope scope) { + this.graph = graph; + this.scope = scope; + } + + public ControlFlowGraph getGraph() { + return graph; + } + + public Scope getSymbols() { + return scope; + } + + public abstract void check() throws AssertionFailed; + + public static class AssertionFailed extends RuntimeException { + public AssertionFailed(String message) { + super(message); + } + } + +} diff --git a/src/dk/camelot64/kickc/test/Main.java b/src/dk/camelot64/kickc/test/Main.java index bcd4a1adc..add0ec106 100644 --- a/src/dk/camelot64/kickc/test/Main.java +++ b/src/dk/camelot64/kickc/test/Main.java @@ -10,7 +10,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -/** Test my KickC Grammar */ +/** Perform KickC compilation ans optimization*/ public class Main { public static void main(String[] args) throws IOException { final String fileName = "src/dk/camelot64/kickc/test/flipper-rex2.kc"; @@ -30,7 +30,8 @@ public class Main { pass1GenerateStatementSequence.generate(file); StatementSequence statementSequence = pass1GenerateStatementSequence.getSequence(); Scope programScope = pass1GenerateStatementSequence.getProgramScope(); - new Pass1TypeInference().inferTypes(statementSequence, programScope); + Pass1TypeInference pass1TypeInference = new Pass1TypeInference(); + pass1TypeInference.inferTypes(statementSequence, programScope); System.out.println("PROGRAM"); System.out.println(statementSequence.toString()); @@ -42,7 +43,8 @@ public class Main { System.out.println("INITIAL CONTROL FLOW GRAPH"); System.out.println(controlFlowGraph.toString()); - Pass1ProcedureCallParameters pass1ProcedureCallParameters = new Pass1ProcedureCallParameters(programScope, controlFlowGraph); + Pass1ProcedureCallParameters pass1ProcedureCallParameters = + new Pass1ProcedureCallParameters(programScope, controlFlowGraph); controlFlowGraph = pass1ProcedureCallParameters.generate(); System.out.println("CONTROL FLOW GRAPH WITH ASSIGNMENT CALL"); System.out.println(controlFlowGraph.toString()); @@ -54,13 +56,12 @@ public class Main { System.out.println("CONTROL FLOW GRAPH SSA"); System.out.println(controlFlowGraph.toString()); - Pass1ProcedureCallsReturnValue pass1ProcedureCallsReturnValue = new Pass1ProcedureCallsReturnValue(programScope, controlFlowGraph); + Pass1ProcedureCallsReturnValue pass1ProcedureCallsReturnValue = + new Pass1ProcedureCallsReturnValue(programScope, controlFlowGraph); controlFlowGraph = pass1ProcedureCallsReturnValue.generate(); System.out.println("CONTROL FLOW GRAPH WITH ASSIGNMENT CALL & RETURN"); System.out.println(controlFlowGraph.toString()); - //if(1==1) return; - List optimizations = new ArrayList<>(); optimizations.add(new Pass2CullEmptyBlocks(controlFlowGraph, programScope)); optimizations.add(new Pass2ConstantPropagation(controlFlowGraph, programScope)); @@ -70,8 +71,14 @@ public class Main { optimizations.add(new Pass2SelfPhiElimination(controlFlowGraph, programScope)); optimizations.add(new Pass2ConditionalJumpSimplification(controlFlowGraph, programScope)); + List assertions = new ArrayList<>(); + assertions.add(new Pass2AssertSymbols(controlFlowGraph, programScope)); + boolean ssaOptimized = true; while (ssaOptimized) { + for (Pass2SsaAssertion assertion : assertions) { + assertion.check(); + } ssaOptimized = false; for (Pass2SsaOptimization optimization : optimizations) { boolean stepOptimized = optimization.optimize();