diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index cc3040549..5d51ad09a 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -136,6 +136,7 @@ public class Compiler { List optimizations = new ArrayList<>(); optimizations.add(new Pass2CullEmptyBlocks(program)); optimizations.add(new Pass2ConstantIdentification(program)); + optimizations.add(new Pass2ConstantInlining(program)); //optimizations.add(new Pass2ConstantPropagation(program)); //optimizations.add(new Pass2ConstantAdditionElimination(program)); diff --git a/src/main/java/dk/camelot64/kickc/asm/AsmFragment.java b/src/main/java/dk/camelot64/kickc/asm/AsmFragment.java index bda1d1be2..8b6033ce4 100644 --- a/src/main/java/dk/camelot64/kickc/asm/AsmFragment.java +++ b/src/main/java/dk/camelot64/kickc/asm/AsmFragment.java @@ -4,6 +4,7 @@ import dk.camelot64.kickc.NumberParser; import dk.camelot64.kickc.asm.parser.Asm6502BaseVisitor; import dk.camelot64.kickc.asm.parser.Asm6502Parser; import dk.camelot64.kickc.icl.*; +import dk.camelot64.kickc.passes.Pass1TypeInference; import java.util.LinkedHashMap; import java.util.Map; @@ -331,6 +332,19 @@ public class AsmFragment { bindings.put(name, value); return name; } + } else if(value instanceof ConstantValue) { + SymbolType type = Pass1TypeInference.inferType(program.getScope(), (ConstantValue) value); + if (SymbolTypeBasic.BYTE.equals(type)) { + String name = "coby" + nextConstByteIdx++; + bindings.put(name, value); + return name; + } else if (SymbolTypeBasic.WORD.equals(type)) { + String name = "cowo" + nextConstByteIdx++; + bindings.put(name, value); + return name; + } else { + throw new RuntimeException("Unhandled constant type " + type); + } } else if (value instanceof Label) { String name = "la" + nextLabelIdx++; bindings.put(name, value); @@ -439,6 +453,9 @@ public class AsmFragment { } else if (constant instanceof ConstantVar) { ConstantVar constantVar = (ConstantVar) constant; return getAsmParameter(constantVar); + } else if (constant instanceof ConstantRef) { + ConstantVar constantVar = program.getScope().getConstant((ConstantRef) constant); + return getAsmParameter(constantVar); } else if (constant instanceof ConstantUnary) { ConstantUnary unary = (ConstantUnary) constant; return unary.getOperator().toString() + toAsm(unary.getOperand()); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1TypeInference.java b/src/main/java/dk/camelot64/kickc/passes/Pass1TypeInference.java index a6941cd56..f5682cfd9 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1TypeInference.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1TypeInference.java @@ -42,13 +42,13 @@ public class Pass1TypeInference { if (operator == null || assignment.getrValue1() == null) { // Copy operation or Unary operation RValue rValue = assignment.getrValue2(); - SymbolType subType = inferType(rValue); + SymbolType subType = inferType(programScope, rValue); SymbolType type = inferType(operator, subType); symbol.setTypeInferred(type); } else { // Binary operation - SymbolType type1 = inferType(assignment.getrValue1()); - SymbolType type2 = inferType(assignment.getrValue2()); + SymbolType type1 = inferType(programScope, assignment.getrValue1()); + SymbolType type2 = inferType(programScope, assignment.getrValue2()); SymbolType type = inferType(type1, operator, type2); symbol.setTypeInferred(type); } @@ -156,11 +156,14 @@ public class Pass1TypeInference { } } - public SymbolType inferType(RValue rValue) { + public static SymbolType inferType(ProgramScope programScope, RValue rValue) { SymbolType type = null; if (rValue instanceof VariableRef) { Variable variable = programScope.getVariable((VariableRef) rValue); type = variable.getType(); + } else if (rValue instanceof ConstantRef) { + ConstantVar constVar = programScope.getConstant((ConstantRef) rValue); + type = constVar.getType(); } else if (rValue instanceof Symbol) { Symbol rSymbol = (Symbol) rValue; type = rSymbol.getType(); @@ -171,6 +174,15 @@ public class Pass1TypeInference { type = SymbolTypeBasic.STRING; } else if (rValue instanceof ConstantBool) { type = SymbolTypeBasic.BOOLEAN; + } else if (rValue instanceof ConstantUnary) { + ConstantUnary constUnary = (ConstantUnary) rValue; + SymbolType subType = inferType(programScope, constUnary.getOperand()); + return inferType(constUnary.getOperator(), subType); + } else if (rValue instanceof ConstantBinary) { + ConstantBinary constBin = (ConstantBinary) rValue; + SymbolType leftType = inferType(programScope, constBin.getLeft()); + SymbolType rightType = inferType(programScope, constBin.getRight()); + return inferType(leftType, constBin.getOperator(), rightType); } if (type == null) { throw new RuntimeException("Cannot infer type for " + rValue); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2AliasElimination.java b/src/main/java/dk/camelot64/kickc/passes/Pass2AliasElimination.java index d18e96635..be71d9b3a 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2AliasElimination.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2AliasElimination.java @@ -1,6 +1,5 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.CompileLog; import dk.camelot64.kickc.icl.*; import java.util.*; @@ -32,7 +31,7 @@ public class Pass2AliasElimination extends Pass2SsaOptimization { } getLog().append("Alias " + str); } - deleteVariables(aliases.getSymbolsToRemove()); + deleteSymbols(aliases.getSymbolsToRemove()); return (aliases.size() > 0); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalJumpSimplification.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalJumpSimplification.java index 697b3f0e5..0fdfc4386 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalJumpSimplification.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalJumpSimplification.java @@ -1,6 +1,5 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.CompileLog; import dk.camelot64.kickc.icl.*; import java.util.ArrayList; @@ -25,7 +24,7 @@ public class Pass2ConditionalJumpSimplification extends Pass2SsaOptimization { final Map> usages = getAllUsages(); final List simpleConditionVars = getSimpleConditions(assignments, usages); removeAssignments(simpleConditionVars); - deleteVariables(simpleConditionVars); + deleteSymbols(simpleConditionVars); return (simpleConditionVars.size()>0); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java new file mode 100644 index 000000000..e8fc4596e --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java @@ -0,0 +1,52 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.icl.ConstantRef; +import dk.camelot64.kickc.icl.ConstantValue; +import dk.camelot64.kickc.icl.ConstantVar; +import dk.camelot64.kickc.icl.Program; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** Compiler Pass consolidating unnamed constants into the place using them (instructions or the definition of another constant value) */ +public class Pass2ConstantInlining extends Pass2SsaOptimization { + + public Pass2ConstantInlining(Program program) { + super(program); + } + + /** + * Consolidate unnamed constants into other constants value + * @return true optimization was performed. false if no optimization was possible. + */ + @Override + public boolean optimize() { + Map unnamedConstants = findUnnamedConstants(); + + // Replace all usages of the unnamed constants + replaceVariables(unnamedConstants); + // Remove from symbol table + deleteSymbols(unnamedConstants.keySet()); + + for (ConstantRef constantRef : unnamedConstants.keySet()) { + getLog().append("Constant inlined " + constantRef.toString()+" = "+unnamedConstants.get(constantRef).toString(getProgram())); + } + + return unnamedConstants.size()>0; + + } + + private Map findUnnamedConstants() { + Map unnamed = new HashMap<>(); + Collection allConstants = getProgram().getScope().getAllConstants(true); + for (ConstantVar constant : allConstants) { + if(constant.getRef().isIntermediate()) { + unnamed.put(constant.getRef(), constant.getValue()); + } + } + return unnamed; + } + + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2RedundantPhiElimination.java b/src/main/java/dk/camelot64/kickc/passes/Pass2RedundantPhiElimination.java index 89c5bab16..370001058 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2RedundantPhiElimination.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2RedundantPhiElimination.java @@ -1,6 +1,5 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.CompileLog; import dk.camelot64.kickc.icl.*; import java.util.LinkedHashMap; @@ -25,7 +24,7 @@ public class Pass2RedundantPhiElimination extends Pass2SsaOptimization { RValue alias = aliases.get(var); getLog().append("Redundant Phi " + var.toString(getProgram()) + " " + alias.toString(getProgram())); } - deleteVariables(aliases.keySet()); + deleteSymbols(aliases.keySet()); return aliases.size()>0; } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java b/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java index 09ee9799e..2b19bfc05 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java @@ -61,7 +61,7 @@ public abstract class Pass2SsaOptimization { * * @param aliases Variables that have alias values. */ - public void replaceVariables(final Map aliases) { + public void replaceVariables(final Map aliases) { ControlFlowGraphBaseVisitor visitor = new ControlFlowGraphBaseVisitor() { @Override public Void visitAssignment(StatementAssignment assignment) { @@ -177,7 +177,7 @@ public abstract class Pass2SsaOptimization { * @param rValue The RValue to find an alias for * @return The alias to use. Null if no alias exists. */ - private static RValue getAlias(Map aliases, RValue rValue) { + private static RValue getAlias(Map aliases, RValue rValue) { RValue alias = aliases.get(rValue); while (aliases.get(alias) != null) { alias = aliases.get(alias); @@ -276,20 +276,9 @@ public abstract class Pass2SsaOptimization { } } - /** - * Remove symbols from the symbol table - * - * @param symbols The symbols to remove - */ - public void deleteSymbols(Collection symbols) { - for (Symbol symbol : symbols) { - symbol.getScope().remove(symbol); - } - } - - public void deleteVariables(Collection symbols) { - for (VariableRef variableRef : symbols) { - Symbol symbol = getSymbols().getSymbol(variableRef.getFullName()); + public void deleteSymbols(Collection symbols) { + for (SymbolRef symbolRef : symbols) { + Symbol symbol = getSymbols().getSymbol(symbolRef.getFullName()); symbol.getScope().remove(symbol); } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2UnaryNotSimplification.java b/src/main/java/dk/camelot64/kickc/passes/Pass2UnaryNotSimplification.java index 3cac59680..b20d9f650 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2UnaryNotSimplification.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2UnaryNotSimplification.java @@ -24,7 +24,7 @@ public class Pass2UnaryNotSimplification extends Pass2SsaOptimization { final Map assignments = getAllAssignments(); final List unusedComparisons = optimizeUnaryNots(assignments, usages); removeAssignments(unusedComparisons); - deleteVariables(unusedComparisons); + deleteSymbols(unusedComparisons); return (unusedComparisons.size() > 0); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass3PhiMemCoalesce.java b/src/main/java/dk/camelot64/kickc/passes/Pass3PhiMemCoalesce.java index 0b31aa1de..4ed71a970 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass3PhiMemCoalesce.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass3PhiMemCoalesce.java @@ -1,6 +1,5 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.CompileLog; import dk.camelot64.kickc.icl.*; import java.util.ArrayList; @@ -37,7 +36,7 @@ public class Pass3PhiMemCoalesce extends Pass2SsaOptimization { phiMemCoalescer.visitGraph(getGraph()); removeAssignments(phiMemCoalescer.getRemove()); replaceVariables(phiMemCoalescer.getReplace()); - deleteVariables(phiMemCoalescer.getRemove()); + deleteSymbols(phiMemCoalescer.getRemove()); getLog().append("Coalesced down to " + phiEquivalenceClasses.size() + " phi equivalence classes"); return false; } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 2e097440b..216ff3c8f 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -81,21 +81,60 @@ public class Pass4CodeGeneration { /** * Add constant declarations for all scope constants * - * @param asm The ASM program - * @param scope The scope + * @param asm The ASM program + * @param scopeRef The scope */ - private void addConstants(AsmProgram asm, ScopeRef currentScope) { - Collection scopeConstants = program.getScope().getScope(currentScope).getAllConstants(false); + private void addConstants(AsmProgram asm, ScopeRef scopeRef) { + Scope scope = program.getScope().getScope(scopeRef); + Collection scopeConstants = scope.getAllConstants(false); Set added = new LinkedHashSet<>(); for (ConstantVar scopeConstant : scopeConstants) { - String asmName = scopeConstant.getLocalName(); // scopeConstant.getAsmName() - if (asmName != null && !added.contains(asmName)) { - asm.addConstant(asmName.replace("#","_").replace("$","_"), scopeConstant.getValue().toString(program)); - added.add(asmName); - } + String asmName = scopeConstant.getLocalName(); // scopeConstant.getAsmName() + if (asmName != null && !added.contains(asmName)) { + asm.addConstant(asmName.replace("#", "_").replace("$", "_"), getConstantValueAsm(scopeConstant.getValue(), false)); + added.add(asmName); + } } } + /** + * Get ASM code for a constant value + * + * @param value The constant value + * @param subOperator is this generated inside another operator (needing a parenthesis) + * + * @return The ASM string representing the constant value + */ + private String getConstantValueAsm(Constant value, boolean subOperator) { + if (value instanceof ConstantRef) { + value = program.getScope().getConstant((ConstantRef) value); + } + if (value instanceof ConstantVar) { + String asmName = ((ConstantVar) value).getLocalName(); // xxx.getAsmName() + return asmName.replace("#", "_").replace("$", "_"); + } else if (value instanceof ConstantInteger) { + return String.format("$%x", ((ConstantInteger) value).getNumber()); + } else if (value instanceof ConstantUnary) { + ConstantUnary unary = (ConstantUnary) value; + return + (subOperator ? "(" : "") + + unary.getOperator().getOperator() + + getConstantValueAsm(unary.getOperand(), true) + + (subOperator ? ")" : ""); + } else if (value instanceof ConstantBinary) { + ConstantBinary binary = (ConstantBinary) value; + return + (subOperator ? "(" : "") + + getConstantValueAsm(binary.getLeft(), true) + + binary.getOperator().getOperator() + + getConstantValueAsm(binary.getRight(), true) + + (subOperator ? ")" : ""); + } else { + throw new RuntimeException("Constant type not supported " + value); + } + } + + /** * Add label declarations for all scope variables assigned to ZP registers * @@ -111,7 +150,7 @@ public class Pass4CodeGeneration { Registers.RegisterZp registerZp = (Registers.RegisterZp) register; String asmName = scopeVar.getAsmName(); if (asmName != null && !added.contains(asmName)) { - asm.addLabelDecl(asmName.replace("#","_").replace("$","_"), registerZp.getZp()); + asm.addLabelDecl(asmName.replace("#", "_").replace("$", "_"), registerZp.getZp()); added.add(asmName); } } @@ -280,7 +319,7 @@ public class Pass4CodeGeneration { } transition.setGenerated(true); } else { - program.getLog().append("Already generated transition from "+fromBlock.getLabel()+" to "+toBlock.getLabel()+ " - not generating it again!"); + program.getLog().append("Already generated transition from " + fromBlock.getLabel() + " to " + toBlock.getLabel() + " - not generating it again!"); } } @@ -357,7 +396,7 @@ public class Pass4CodeGeneration { private PhiTransition findTransition(ControlFlowBlock fromBlock) { PhiTransition transition = new PhiTransition(fromBlock); boolean isCallTransition = toBlock.getLabel().equals(fromBlock.getCallSuccessor()); - if(!isCallTransition) { + if (!isCallTransition) { // If the transition is not a call - then attempt to join with other equal transition(s) for (PhiTransition candidate : transitions.values()) { if (candidate.equalAssignments(transition)) { diff --git a/src/main/java/dk/camelot64/kickc/test/constantmin.kc b/src/main/java/dk/camelot64/kickc/test/constantmin.kc index 9409ac550..c8de10d22 100644 --- a/src/main/java/dk/camelot64/kickc/test/constantmin.kc +++ b/src/main/java/dk/camelot64/kickc/test/constantmin.kc @@ -2,7 +2,7 @@ const byte* SCREEN = $0400; const byte STAR = 81; byte* VIC = $d000; -byte* BGCOL = VIC+$21; +byte* BGCOL = VIC+$10*2+1; byte RED = 2; main();