diff --git a/src/dk/camelot64/kickc/asm/fragment/aby=xby_plus_coby1.asm b/src/dk/camelot64/kickc/asm/fragment/aby=xby_plus_coby1.asm new file mode 100644 index 000000000..11b2a5a61 --- /dev/null +++ b/src/dk/camelot64/kickc/asm/fragment/aby=xby_plus_coby1.asm @@ -0,0 +1,3 @@ +txa +clc +adc #{coby1} \ No newline at end of file diff --git a/src/dk/camelot64/kickc/asm/fragment/aby=xby_plus_xby.asm b/src/dk/camelot64/kickc/asm/fragment/aby=xby_plus_xby.asm new file mode 100644 index 000000000..5cfa44dde --- /dev/null +++ b/src/dk/camelot64/kickc/asm/fragment/aby=xby_plus_xby.asm @@ -0,0 +1,2 @@ +txa +asl \ No newline at end of file diff --git a/src/dk/camelot64/kickc/icl/ControlFlowGraph.java b/src/dk/camelot64/kickc/icl/ControlFlowGraph.java index 6ea6c0777..37f36cf66 100644 --- a/src/dk/camelot64/kickc/icl/ControlFlowGraph.java +++ b/src/dk/camelot64/kickc/icl/ControlFlowGraph.java @@ -36,4 +36,27 @@ public class ControlFlowGraph { public Collection getAllBlocks() { return blocks.values(); } + + /** Get the assignment of the passed variable. + * + * @param variable The variable to find the assignment for + * @return The assignment. null if the variable is not assigned. The variable is assigned by a Phi-statement instead. + */ + public StatementAssignment getAssignment(Variable variable) { + if(variable instanceof VariableUnversioned) { + throw new RuntimeException("Error attempting to get assignment of unversioned variable, which is not guaranteed to be unique "+variable); + } + for (ControlFlowBlock block : getAllBlocks()) { + for (Statement statement : block.getStatements()) { + if(statement instanceof StatementAssignment) { + StatementAssignment assignment = (StatementAssignment) statement; + if(assignment.getLValue().equals(variable)) { + return assignment; + } + } + } + } + return null; + } + } diff --git a/src/dk/camelot64/kickc/icl/Pass1GenerateStatementSequence.java b/src/dk/camelot64/kickc/icl/Pass1GenerateStatementSequence.java index 6d4699c21..5ee3149a0 100644 --- a/src/dk/camelot64/kickc/icl/Pass1GenerateStatementSequence.java +++ b/src/dk/camelot64/kickc/icl/Pass1GenerateStatementSequence.java @@ -166,7 +166,7 @@ public class Pass1GenerateStatementSequence extends KickCBaseVisitor { public LValue visitLvalueArray(KickCParser.LvalueArrayContext ctx) { LValue lval = (LValue) visit(ctx.lvalue()); RValue index = (RValue) visit(ctx.expr()); - return new PointerDereferenceIndexed((Variable) lval, (Variable)index); + return new PointerDereferenceIndexed(lval, index); } @Override diff --git a/src/dk/camelot64/kickc/icl/Pass2AliasElimination.java b/src/dk/camelot64/kickc/icl/Pass2AliasElimination.java index 8b58f86a0..eaff96526 100644 --- a/src/dk/camelot64/kickc/icl/Pass2AliasElimination.java +++ b/src/dk/camelot64/kickc/icl/Pass2AliasElimination.java @@ -1,8 +1,6 @@ package dk.camelot64.kickc.icl; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; +import java.util.*; /** Compiler Pass eliminating alias assignments */ public class Pass2AliasElimination extends Pass2SsaOptimization { @@ -11,55 +9,136 @@ public class Pass2AliasElimination extends Pass2SsaOptimization { super(graph, symbolTable); } + /** * Eliminate alias assignments replacing them with the aliassed variable. */ @Override public boolean optimize() { - final Map 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); + final Aliases aliases = findAliases(); + removeAssignments(aliases.getAssignmentsToRemove()); + replaceVariables(aliases.getReplacements()); + deleteSymbols(aliases.getSymbolsToRemove()); + for (Alias alias : aliases.getAliases()) { + System.out.println("Alias " + alias.getKeep() + " " + alias.getEliminate()); } return (aliases.size()>0); } - private Map findAliases() { - Map candidates = findAliasesCandidates(); + private static class Aliases { + private List aliases; + + public Aliases() { + this.aliases = new ArrayList<>(); + } + + public List getAliases() { + return aliases; + } + + public List getAssignmentsToRemove() { + ArrayList eliminates = new ArrayList<>(); + for (Alias alias : aliases) { + if(alias.isKeepHasDefinition()) { + eliminates.add(alias.getEliminate()); + } else { + eliminates.add(alias.getKeep()); + } + } + return eliminates; + } + + public List getSymbolsToRemove() { + ArrayList eliminates = new ArrayList<>(); + for (Alias alias : aliases) { + eliminates.add(alias.getEliminate()); + } + return eliminates; + } + + public Map getReplacements() { + HashMap replacements = new HashMap<>(); + for (Alias alias : aliases) { + replacements.put(alias.getEliminate(), alias.getKeep()); + } + return replacements; + } + + public int size() { + return aliases.size(); + } + + public void add(Variable lValue, Variable rValue) { + if(lValue instanceof VariableVersion && rValue instanceof VariableIntermediate) { + aliases.add(new Alias(lValue, rValue, false)); + } else { + aliases.add(new Alias(rValue, lValue, true)); + } + } + + } + + private static class Alias { + private Variable keep; + private Variable eliminate; + // true if the symbol to keep has the proper definition of the value. + private boolean keepHasDefinition; + + public Alias(Variable keep, Variable eliminate, boolean keepHasDefinition) { + this.keep = keep; + this.eliminate = eliminate; + this.keepHasDefinition = keepHasDefinition; + } + + public Variable getEliminate() { + return eliminate; + } + + public Variable getKeep() { + return keep; + } + + public boolean isKeepHasDefinition() { + return keepHasDefinition; + } + } + + + + private Aliases findAliases() { + Aliases candidates = findAliasesCandidates(); cleanupCandidates(candidates); return candidates; } // Remove all candidates that are used after assignment in phi blocks - private void cleanupCandidates(Map candidates) { - Iterator aliasIt = candidates.keySet().iterator(); - while (aliasIt.hasNext()) { - final Variable alias = aliasIt.next(); - final Variable variable = candidates.get(alias); + private void cleanupCandidates(Aliases candidates) { + Iterator candidateIt = candidates.getAliases().iterator(); + while (candidateIt.hasNext()) { + Alias candidate = candidateIt.next(); + final Variable varEliminate = candidate.getEliminate(); + final Variable varKeep = candidate.getKeep(); final Boolean[] rMatch = {false}; final Boolean[] lMatch = {false}; ControlFlowGraphBaseVisitor candidateEliminator = new ControlFlowGraphBaseVisitor() { @Override public Void visitPhi(StatementPhi phi) { for (StatementPhi.PreviousSymbol previousSymbol : phi.getPreviousVersions()) { - if(previousSymbol.getRValue().equals(variable)) { + if (previousSymbol.getRValue().equals(varKeep)) { rMatch[0] = true; break; } } - if(phi.getLValue().equals(alias)) { + if (phi.getLValue().equals(varEliminate)) { lMatch[0] = true; } return null; } }; candidateEliminator.visitGraph(getGraph()); - if(rMatch[0] && lMatch[0]) { - System.out.println("Alias candidate removed " + alias + " " + variable); - aliasIt.remove(); + if (rMatch[0] && lMatch[0]) { + System.out.println("Alias candidate removed " + varEliminate + " " + varKeep); + candidateIt.remove(); } } } @@ -68,8 +147,8 @@ public class Pass2AliasElimination extends Pass2SsaOptimization { * Find variables that have constant values. * @return Map from Variable to the Constant value */ - private Map findAliasesCandidates() { - final Map aliases = new HashMap<>(); + private Aliases findAliasesCandidates() { + final Aliases aliases = new Aliases(); ControlFlowGraphBaseVisitor visitor = new ControlFlowGraphBaseVisitor() { @Override public Void visitAssignment(StatementAssignment assignment) { @@ -78,7 +157,7 @@ public class Pass2AliasElimination extends Pass2SsaOptimization { if (assignment.getRValue1() == null && assignment.getOperator() == null && assignment.getRValue2() instanceof Variable) { // Alias assignment Variable alias = (Variable) assignment.getRValue2(); - aliases.put(alias, variable); + aliases.add(variable, alias); } } return null; @@ -91,7 +170,7 @@ public class Pass2AliasElimination extends Pass2SsaOptimization { if(previousSymbol.getRValue() instanceof Variable) { VariableVersion variable = phi.getLValue(); Variable alias = (Variable) previousSymbol.getRValue(); - aliases.put(alias, variable); + aliases.add(variable, alias); } } return null; diff --git a/src/dk/camelot64/kickc/icl/Pass2ConstantAdditionElimination.java b/src/dk/camelot64/kickc/icl/Pass2ConstantAdditionElimination.java new file mode 100644 index 000000000..1685ad11a --- /dev/null +++ b/src/dk/camelot64/kickc/icl/Pass2ConstantAdditionElimination.java @@ -0,0 +1,93 @@ +package dk.camelot64.kickc.icl; + +/** Compiler Pass eliminating several additions of constants by consolidating them to a single (compile time) constant c1+v+c2 => (c1+c2)+v */ +public class Pass2ConstantAdditionElimination extends Pass2SsaOptimization { + + public Pass2ConstantAdditionElimination(ControlFlowGraph graph, SymbolTable symbolTable) { + super(graph, symbolTable); + } + + /** + * For assignments with a constant part the variable part is examined looking for constants to consolidate into the constant. + * @return true optimization was performed. false if no optimization was possible. + */ + @Override + public boolean optimize() { + boolean optimized = false; + // Examine all assigments - performing constant consolidation + for (ControlFlowBlock block : getGraph().getAllBlocks()) { + for (Statement statement : block.getStatements()) { + if(statement instanceof StatementAssignment) { + StatementAssignment assignment = (StatementAssignment) statement; + if(assignment.getOperator()!=null && "+".equals(assignment.getOperator().getOperator())) { + if(assignment.getRValue1() instanceof ConstantInteger && assignment.getRValue2() instanceof Variable) { + Variable variable = (Variable) assignment.getRValue2(); + ConstantInteger consolidated = consolidateSubConstants(variable); + if(consolidated!=null) { + ConstantInteger const1 = (ConstantInteger) assignment.getRValue1(); + assignment.setRValue1(new ConstantInteger(const1.getNumber()+consolidated.getNumber())); + optimized = true; + System.out.println("Consolidated constant in assignment "+assignment.getLValue()); + } + } else if(assignment.getRValue1() instanceof Variable && assignment.getRValue2() instanceof ConstantInteger) { + Variable variable = (Variable) assignment.getRValue1(); + ConstantInteger consolidated = consolidateSubConstants(variable); + if(consolidated!=null) { + ConstantInteger const2 = (ConstantInteger) assignment.getRValue2(); + assignment.setRValue2(new ConstantInteger(const2.getNumber()+consolidated.getNumber())); + optimized = true; + System.out.println("Consolidated constant in assignment "+assignment.getLValue()); + } + } + } + } + } + } + return optimized; + } + + /** + * Gather up constants from sub addition expressions of a variable, remove them there, and return the aggregated sum. + * @param variable The variable to examine + * @return The consolidated constant. Null if no sub-constants were found. + */ + private ConstantInteger consolidateSubConstants(Variable variable) { + StatementAssignment assignment = getGraph().getAssignment(variable); + if(assignment!=null && assignment.getOperator()!=null && "+".equals(assignment.getOperator().getOperator())) { + if(assignment.getRValue1() instanceof ConstantInteger) { + ConstantInteger constant = (ConstantInteger) assignment.getRValue1(); + assignment.setRValue1(null); + assignment.setOperator(null); + return constant; + } else if(assignment.getRValue2() instanceof ConstantInteger) { + ConstantInteger constant = (ConstantInteger) assignment.getRValue2(); + assignment.setRValue2(assignment.getRValue1()); + assignment.setOperator(null); + assignment.setRValue1(null); + return constant; + } else { + ConstantInteger const1 = null; + if(assignment.getRValue1() instanceof Variable) { + const1 = consolidateSubConstants((Variable) assignment.getRValue1()); + } + ConstantInteger const2 = null; + if(assignment.getRValue2() instanceof Variable) { + const2 = consolidateSubConstants((Variable) assignment.getRValue2()); + } + ConstantInteger result = null; + if(const1!=null) { + result = const1; + if(const2!=null) { + result = new ConstantInteger(const1.getNumber()+const2.getNumber()); + } + } else if(const2!=null) { + result = const2; + } + return result; + } + } + return null; + } + + +} diff --git a/src/dk/camelot64/kickc/icl/Pass2ConstantPropagation.java b/src/dk/camelot64/kickc/icl/Pass2ConstantPropagation.java index f19b15fdc..f0dfdb358 100644 --- a/src/dk/camelot64/kickc/icl/Pass2ConstantPropagation.java +++ b/src/dk/camelot64/kickc/icl/Pass2ConstantPropagation.java @@ -12,8 +12,7 @@ public class Pass2ConstantPropagation extends Pass2SsaOptimization { /** * Propagate constants, replacing variables with constants where possible. - * @return true if no more constant propagation is possible. (no constant propagation was performed) - * false if more constant propagation might be possible. (signalling another call) + * @return true optimization was performed. false if no optimization was possible. */ @Override public boolean optimize() { diff --git a/src/dk/camelot64/kickc/icl/Pass3RegisterAllocation.java b/src/dk/camelot64/kickc/icl/Pass3RegisterAllocation.java index 2e1315299..0e5caaff9 100644 --- a/src/dk/camelot64/kickc/icl/Pass3RegisterAllocation.java +++ b/src/dk/camelot64/kickc/icl/Pass3RegisterAllocation.java @@ -63,8 +63,10 @@ public class Pass3RegisterAllocation { allocation.allocate(symbols.getVariable("v#3"), new RegisterAllocation.RegisterAByte()); allocation.allocate(symbols.getVariable("v#4"), new RegisterAllocation.RegisterAByte()); allocation.allocate(symbols.getVariable("v#5"), new RegisterAllocation.RegisterAByte()); - allocation.allocate(symbols.getVariable("$0"), new RegisterAllocation.RegisterAByte()); - + //allocation.allocate(symbols.getVariable("$0"), new RegisterAllocation.RegisterAByte()); + //allocation.allocate(symbols.getVariable("$1"), new RegisterAllocation.RegisterAByte()); + //allocation.allocate(symbols.getVariable("$2"), new RegisterAllocation.RegisterAByte()); + //allocation.allocate(symbols.getVariable("$3"), new RegisterAllocation.RegisterAByte()); symbols.setAllocation(allocation); } diff --git a/src/dk/camelot64/kickc/icl/StatementAssignment.java b/src/dk/camelot64/kickc/icl/StatementAssignment.java index 96c102284..95a4e5b65 100644 --- a/src/dk/camelot64/kickc/icl/StatementAssignment.java +++ b/src/dk/camelot64/kickc/icl/StatementAssignment.java @@ -76,4 +76,7 @@ public class StatementAssignment implements Statement { rValue2 ; } + public void setOperator(Operator operator) { + this.operator = operator; + } } diff --git a/src/dk/camelot64/kickc/icl/StatementPhi.java b/src/dk/camelot64/kickc/icl/StatementPhi.java index abcf4678a..8875024cc 100644 --- a/src/dk/camelot64/kickc/icl/StatementPhi.java +++ b/src/dk/camelot64/kickc/icl/StatementPhi.java @@ -58,6 +58,9 @@ public class StatementPhi implements Statement { } public void setLValue(Variable lValue) { + if(!(lValue instanceof VariableVersion)) { + throw new RuntimeException("Error modifying phi-statement lValue "+this.lValue+". Attempt to set to non-versioned variable "+lValue); + } this.lValue = (VariableVersion) lValue; } diff --git a/src/dk/camelot64/kickc/icl/VariableVersion.java b/src/dk/camelot64/kickc/icl/VariableVersion.java index 560e69e2a..7cd3c49a0 100644 --- a/src/dk/camelot64/kickc/icl/VariableVersion.java +++ b/src/dk/camelot64/kickc/icl/VariableVersion.java @@ -23,4 +23,22 @@ public class VariableVersion extends Variable { public VariableUnversioned getVersionOf() { return versionOf; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + VariableVersion that = (VariableVersion) o; + + return versionOf != null ? versionOf.equals(that.versionOf) : that.versionOf == null; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (versionOf != null ? versionOf.hashCode() : 0); + return result; + } } diff --git a/src/dk/camelot64/kickc/test/Main.java b/src/dk/camelot64/kickc/test/Main.java index 28eea3a9a..fdcd1bcbe 100644 --- a/src/dk/camelot64/kickc/test/Main.java +++ b/src/dk/camelot64/kickc/test/Main.java @@ -13,7 +13,7 @@ import java.util.List; /** Test my KickC Grammar */ public class Main { public static void main(String[] args) throws IOException { - final String fileName = "src/dk/camelot64/kickc/test/bresenhamarr.kc"; + final String fileName = "src/dk/camelot64/kickc/test/fibmem.kc"; final CharStream input = CharStreams.fromFileName(fileName); System.out.println(input.toString()); KickCLexer lexer = new KickCLexer(input); @@ -45,6 +45,7 @@ public class Main { List optimizations = new ArrayList<>(); optimizations.add(new Pass2CullEmptyBlocks(controlFlowGraph, symbolTable)); optimizations.add(new Pass2ConstantPropagation(controlFlowGraph, symbolTable)); + optimizations.add(new Pass2ConstantAdditionElimination(controlFlowGraph, symbolTable)); optimizations.add(new Pass2AliasElimination(controlFlowGraph, symbolTable)); optimizations.add(new Pass2RedundantPhiElimination(controlFlowGraph, symbolTable)); optimizations.add(new Pass2SelfPhiElimination(controlFlowGraph, symbolTable)); diff --git a/src/dk/camelot64/kickc/test/alias.kc b/src/dk/camelot64/kickc/test/alias.kc new file mode 100644 index 000000000..280c66140 --- /dev/null +++ b/src/dk/camelot64/kickc/test/alias.kc @@ -0,0 +1,6 @@ +byte b = 5; +while(b<10) { + byte c=b; + byte d=c+2; + b = b+1; +} diff --git a/src/dk/camelot64/kickc/test/minus.kc b/src/dk/camelot64/kickc/test/minus.kc new file mode 100644 index 000000000..131e8bdbe --- /dev/null +++ b/src/dk/camelot64/kickc/test/minus.kc @@ -0,0 +1,7 @@ +byte[16] p = $1100; +byte i = 5; +do { + byte c=2; + p[i] = 15+i+c+i+2; + i = i+1; +} while(i<10)