From 37708748601a37bc15569403504222be136345f6 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Fri, 14 Feb 2020 15:55:04 +0100 Subject: [PATCH] Unrolled condition variables are now converted to SSA automatically in pass 1. --- .../java/dk/camelot64/kickc/Compiler.java | 2 + .../fragment/AsmFragmentInstanceSpec.java | 15 ++-- .../AsmFragmentTemplateSynthesisRule.java | 26 +++--- .../kickc/model/symbols/Variable.java | 4 + .../Pass1UnrollConditionVariableSsa.java | 80 +++++++++++++++++++ .../passes/Pass2LoopUnrollAssertComplete.java | 2 +- .../PassNCalcVariableReferenceInfos.java | 17 ++-- .../kickc/passes/utils/Unroller.java | 7 +- src/test/kc/unroll-for-min.kc | 2 +- 9 files changed, 116 insertions(+), 39 deletions(-) create mode 100644 src/main/java/dk/camelot64/kickc/passes/Pass1UnrollConditionVariableSsa.java diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index d0deb39e8..74e36ab48 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -225,6 +225,8 @@ public class Compiler { new Pass1ExtractInlineStrings(program).execute(); new PassNCullEmptyBlocks(program).execute(); + new Pass1UnrollConditionVariableSsa(program).step(); + new Pass1ModifiedVarsAnalysis(program).execute(); if(getLog().isVerbosePass1CreateSsa()) { getLog().append("PROCEDURE MODIFY VARIABLE ANALYSIS"); diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpec.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpec.java index 0e9c9f779..458ef99c5 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpec.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpec.java @@ -182,20 +182,15 @@ public class AsmFragmentInstanceSpec { public boolean equals(Object o) { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; - AsmFragmentInstanceSpec that = (AsmFragmentInstanceSpec) o; - - if(signature != null ? !signature.equals(that.signature) : that.signature != null) return false; - if(bindings != null ? !bindings.equals(that.bindings) : that.bindings != null) return false; - return codeScopeRef != null ? codeScopeRef.equals(that.codeScopeRef) : that.codeScopeRef == null; + return + Objects.equals(signature, that.signature) && + Objects.equals(bindings, that.bindings) && + Objects.equals(codeScopeRef, that.codeScopeRef); } @Override public int hashCode() { - int result = signature != null ? signature.hashCode() : 0; - result = 31 * result + (bindings != null ? bindings.hashCode() : 0); - result = 31 * result + (codeScopeRef != null ? codeScopeRef.hashCode() : 0); - return result; + return Objects.hash(signature, bindings, codeScopeRef); } - } diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java index 523787a0a..3f977ade2 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentTemplateSynthesisRule.java @@ -165,28 +165,20 @@ class AsmFragmentTemplateSynthesisRule { public boolean equals(Object o) { if(this == o) return true; if(o == null || getClass() != o.getClass()) return false; - AsmFragmentTemplateSynthesisRule that = (AsmFragmentTemplateSynthesisRule) o; - - if(mapSignature != that.mapSignature) return false; - if(sigMatch != null ? !sigMatch.equals(that.sigMatch) : that.sigMatch != null) return false; - if(sigAvoid != null ? !sigAvoid.equals(that.sigAvoid) : that.sigAvoid != null) return false; - if(asmPrefix != null ? !asmPrefix.equals(that.asmPrefix) : that.asmPrefix != null) return false; - if(sigReplace != null ? !sigReplace.equals(that.sigReplace) : that.sigReplace != null) return false; - if(asmPostfix != null ? !asmPostfix.equals(that.asmPostfix) : that.asmPostfix != null) return false; - return bindMappings != null ? bindMappings.equals(that.bindMappings) : that.bindMappings == null; + return mapSignature == that.mapSignature && + Objects.equals(sigMatch, that.sigMatch) && + Objects.equals(sigAvoid, that.sigAvoid) && + Objects.equals(asmPrefix, that.asmPrefix) && + Objects.equals(sigReplace, that.sigReplace) && + Objects.equals(asmPostfix, that.asmPostfix) && + Objects.equals(bindMappings, that.bindMappings) && + Objects.equals(subDontClobber, that.subDontClobber); } @Override public int hashCode() { - int result = sigMatch != null ? sigMatch.hashCode() : 0; - result = 31 * result + (sigAvoid != null ? sigAvoid.hashCode() : 0); - result = 31 * result + (asmPrefix != null ? asmPrefix.hashCode() : 0); - result = 31 * result + (sigReplace != null ? sigReplace.hashCode() : 0); - result = 31 * result + (asmPostfix != null ? asmPostfix.hashCode() : 0); - result = 31 * result + (bindMappings != null ? bindMappings.hashCode() : 0); - result = 31 * result + (mapSignature ? 1 : 0); - return result; + return Objects.hash(sigMatch, sigAvoid, asmPrefix, sigReplace, asmPostfix, bindMappings, mapSignature, subDontClobber); } /** All the synthesize rules available. */ diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java b/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java index 8cca84fa9..383e61f70 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java @@ -306,6 +306,10 @@ public class Variable implements Symbol { public void setKind(Kind kind) { this.kind = kind; + if(kind.equals(Kind.PHI_MASTER)) { + this.nextPhiVersionNumber = 0; + } else + this.nextPhiVersionNumber = null; } public boolean isKindConstant() { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1UnrollConditionVariableSsa.java b/src/main/java/dk/camelot64/kickc/passes/Pass1UnrollConditionVariableSsa.java new file mode 100644 index 000000000..c6903804f --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1UnrollConditionVariableSsa.java @@ -0,0 +1,80 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.VariableReferenceInfos; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementAssignment; +import dk.camelot64.kickc.model.statements.StatementConditionalJump; +import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.values.RValue; +import dk.camelot64.kickc.model.values.VariableRef; +import dk.camelot64.kickc.passes.utils.VarAssignments; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; + +/** + * Convert condition variables in unrolled jumps to single-static-assignment + */ +public class Pass1UnrollConditionVariableSsa extends Pass2SsaOptimization { + + public Pass1UnrollConditionVariableSsa(Program program) { + super(program); + } + + @Override + public boolean step() { + for(ControlFlowBlock block : getGraph().getAllBlocks()) { + for(Statement statement : block.getStatements()) { + if(statement instanceof StatementConditionalJump) { + final StatementConditionalJump conditionalJump = (StatementConditionalJump) statement; + if(conditionalJump.isDeclaredUnroll()) { + Collection referencedVars = new LinkedHashSet<>(); + findAllReferencedVars(referencedVars, conditionalJump.getrValue1()); + findAllReferencedVars(referencedVars, conditionalJump.getrValue2()); + for(VariableRef referencedVar : referencedVars) { + final Variable variable = getScope().getVariable(referencedVar); + if(variable.isKindLoadStore()) { + // Convert the variable to versioned if it is load/store + getLog().append("Converting unrolled condition variable to single-static-assignment "+variable); + variable.setKind(Variable.Kind.PHI_MASTER); + } + } + } + } + } + } + return false; + } + + /** + * Find all referenced variables in a value. If an intermediate variable is referenced this recurses to the definition of the intermediate. + * @param referencedVars The collection to fill + * @param rValue The value to find references in + */ + private void findAllReferencedVars(Collection referencedVars, RValue rValue) { + Collection directReferenced = VariableReferenceInfos.getReferencedVars(rValue); + referencedVars.addAll(directReferenced); + for(VariableRef varRef : directReferenced) { + if(varRef.isIntermediate()) { + // Found an intermediate variable - recurse to the definition + final List varAssignments = VarAssignments.get(varRef, getGraph(), getScope()); + for(VarAssignments.VarAssignment varAssignment : varAssignments) { + if(VarAssignments.VarAssignment.Type.STATEMENT_LVALUE.equals(varAssignment.type)) { + if(varAssignment.statementLValue instanceof StatementAssignment) { + findAllReferencedVars(referencedVars, ((StatementAssignment) varAssignment.statementLValue).getrValue1()); + findAllReferencedVars(referencedVars, ((StatementAssignment) varAssignment.statementLValue).getrValue2()); + } else { + throw new InternalError("Not implemented!"); + } + } else { + throw new InternalError("Not implemented!"); + } + } + } + } + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2LoopUnrollAssertComplete.java b/src/main/java/dk/camelot64/kickc/passes/Pass2LoopUnrollAssertComplete.java index 6fc496e75..73febb59b 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2LoopUnrollAssertComplete.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2LoopUnrollAssertComplete.java @@ -23,7 +23,7 @@ public class Pass2LoopUnrollAssertComplete extends Pass2SsaOptimization { for(Statement statement : block.getStatements()) { if(statement instanceof StatementConditionalJump) { if(((StatementConditionalJump) statement).isWasUnrolled()) { - throw new CompileError("Loop cannot be unrolled. Condition not resolvable to a constant true/false. "+statement.toString(getProgram(), false)); + throw new CompileError("Loop cannot be unrolled. Condition not resolvable to a constant true/false. ", statement); } } } diff --git a/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcVariableReferenceInfos.java b/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcVariableReferenceInfos.java index 5340833c9..7c9cb147f 100644 --- a/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcVariableReferenceInfos.java +++ b/src/main/java/dk/camelot64/kickc/passes/calcs/PassNCalcVariableReferenceInfos.java @@ -106,9 +106,9 @@ public class PassNCalcVariableReferenceInfos extends PassNCalcBase getReferenced(RValue rValue) { Collection referenced = new LinkedHashSet<>(); - ProgramValueIterator.execute(new ProgramValue.GenericValue(rValue), (programValue, currentStmt, stmtIt, currentBlock) -> { - if(programValue.get() instanceof SymbolVariableRef) { - referenced.add((SymbolVariableRef) programValue.get()); - } - }, null, null, null); + if(rValue != null) + ProgramValueIterator.execute(new ProgramValue.GenericValue(rValue), (programValue, currentStmt, stmtIt, currentBlock) -> { + if(programValue.get() instanceof SymbolVariableRef) { + referenced.add((SymbolVariableRef) programValue.get()); + } + }, null, null, null); return referenced; } diff --git a/src/main/java/dk/camelot64/kickc/passes/utils/Unroller.java b/src/main/java/dk/camelot64/kickc/passes/utils/Unroller.java index e3c47f301..816929ad9 100644 --- a/src/main/java/dk/camelot64/kickc/passes/utils/Unroller.java +++ b/src/main/java/dk/camelot64/kickc/passes/utils/Unroller.java @@ -218,7 +218,7 @@ public class Unroller { } /** - * Create new versions of all symbols assigned inside some blocks to be unrolled + * Create new versions of all symbols defined inside some blocks to be unrolled * * @param unrollBlocks The blocks being unrolled * @return A map from variables assigned inside the unroll blocks to the new copy of the variable @@ -233,12 +233,15 @@ public class Unroller { String name = scope.allocateIntermediateVariableName(); newVar = Variable.createCopy(name, scope, definedVar); scope.add(newVar); + definedToNewVar.put(definedVarRef, newVar.getRef()); } else if(definedVarRef.isVersion()) { newVar = (definedVar).getPhiMaster().createVersion(); + definedToNewVar.put(definedVarRef, newVar.getRef()); + } else if(definedVar.isKindLoadStore()) { + // New version not needed for load/store } else { throw new RuntimeException("Error! Variable is not versioned or intermediate " + definedVar.toString(program)); } - definedToNewVar.put(definedVarRef, newVar.getRef()); //getLog().append("Defined in loop: " + definedVarRef.getFullName() + " -> " + newVar.getRef().getFullName()); } return definedToNewVar; diff --git a/src/test/kc/unroll-for-min.kc b/src/test/kc/unroll-for-min.kc index 132d3370c..e764eaee9 100644 --- a/src/test/kc/unroll-for-min.kc +++ b/src/test/kc/unroll-for-min.kc @@ -1,6 +1,6 @@ // Minimal unrolled ranged for() loop void main() { - char* SCREEN = $400; + char* SCREEN = 0x0400; inline for(char i : 0..2) { SCREEN[i] = 'a'; }