diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ReplaceableValue.java b/src/main/java/dk/camelot64/kickc/model/iterator/ReplaceableValue.java index ff7064391..fc08cb3fb 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ReplaceableValue.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ReplaceableValue.java @@ -1,10 +1,10 @@ package dk.camelot64.kickc.model.iterator; import dk.camelot64.kickc.model.statements.*; +import dk.camelot64.kickc.model.symbols.ConstantVar; +import dk.camelot64.kickc.model.types.SymbolTypeArray; import dk.camelot64.kickc.model.values.*; -import java.util.Collection; - /** * Interface representing an RValue that can be replaced. * The value may have sub-values that can also be replaced. @@ -15,6 +15,45 @@ public abstract class ReplaceableValue { public abstract void set(RValue value); + public static class ConstantVariableValue extends ReplaceableValue { + private final ConstantVar constantVar; + + public ConstantVariableValue(ConstantVar constantVar) { + this.constantVar = constantVar; + } + + @Override + public RValue get() { + return constantVar.getValue(); + } + + @Override + public void set(RValue val) { + constantVar.setValue((ConstantValue) val); + } + + } + + /** Replaceable size inside a fixed size array. */ + public static class TypeArraySize extends ReplaceableValue { + private final SymbolTypeArray array; + + public TypeArraySize(SymbolTypeArray array) { + this.array = array; + } + + @Override + public RValue get() { + return array.getSize(); + } + + @Override + public void set(RValue val) { + array.setSize(val); + } + + } + /** Replaceable value inside a array filled expression. */ public static class ArrayFilledSize extends ReplaceableValue { private final ArrayFilled array; @@ -272,6 +311,26 @@ public abstract class ReplaceableValue { } + public static class ConstantArrayElement extends ReplaceableValue { + private final ConstantArrayList arrayList; + private final int idx; + + public ConstantArrayElement(ConstantArrayList arrayList, int idx) { + this.arrayList = arrayList; + this.idx = idx; + } + + @Override + public RValue get() { + return arrayList.getElements().get(idx); + } + + @Override + public void set(RValue value) { + arrayList.getElements().set(idx, (ConstantValue) value); + } + } + public static class ListElement extends ReplaceableValue { private ValueList list; private int idx; @@ -466,4 +525,23 @@ public abstract class ReplaceableValue { } } + + /** A generic replaceable value. */ + public static class GenericValue extends ReplaceableValue { + private ConstantValue constantValue; + + public GenericValue(ConstantValue constantValue) { + this.constantValue = constantValue; + } + + @Override + public RValue get() { + return constantValue; + } + + @Override + public void set(RValue value) { + this.constantValue = (ConstantValue) value; + } + } } diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ValueReplacer.java b/src/main/java/dk/camelot64/kickc/model/iterator/ValueReplacer.java index 312cf7f3b..dc6c999fd 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ValueReplacer.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ValueReplacer.java @@ -21,11 +21,21 @@ public class ValueReplacer { */ public static void executeAll(ControlFlowGraph graph, Replacer replacer) { for(ControlFlowBlock block : graph.getAllBlocks()) { - ListIterator statementsIt = block.getStatements().listIterator(); - while(statementsIt.hasNext()) { - Statement statement = statementsIt.next(); - executeAll(statement, replacer, statementsIt, block); - } + executeAll(block, replacer); + } + } + + /** + * Execute a replacer on all replaceable values in a block of the control flow graph + * + * @param block The control flow graph block + * @param replacer The replacer to execute + */ + public static void executeAll(ControlFlowBlock block, Replacer replacer) { + ListIterator statementsIt = block.getStatements().listIterator(); + while(statementsIt.hasNext()) { + Statement statement = statementsIt.next(); + executeAll(statement, replacer, statementsIt, block); } } @@ -37,11 +47,11 @@ public class ValueReplacer { */ public static void executeAll(Statement statement, Replacer replacer, ListIterator statementsIt, ControlFlowBlock block) { if(statement instanceof StatementAssignment) { - executeAll(new ReplaceableValue.LValue((StatementLValue) statement), replacer, statement, statementsIt, block); + // The sequence RValue1, RValue2, LValue is important - as it is essential for {@link dk.camelot64.kickc.passes.Pass1GenerateSingleStaticAssignmentForm} to create the correct SSA executeAll(new ReplaceableValue.RValue1((StatementAssignment) statement), replacer, statement, statementsIt, block); executeAll(new ReplaceableValue.RValue2((StatementAssignment) statement), replacer, statement, statementsIt, block); - } else if(statement instanceof StatementCall) { executeAll(new ReplaceableValue.LValue((StatementLValue) statement), replacer, statement, statementsIt, block); + } else if(statement instanceof StatementCall) { StatementCall call = (StatementCall) statement; if(call.getParameters() != null) { int size = call.getParameters().size(); @@ -49,6 +59,7 @@ public class ValueReplacer { executeAll(new ReplaceableValue.CallParameter(call, i), replacer, statement, statementsIt, block); } } + executeAll(new ReplaceableValue.LValue((StatementLValue) statement), replacer, statement, statementsIt, block); } else if(statement instanceof StatementConditionalJump) { executeAll(new ReplaceableValue.CondRValue1((StatementConditionalJump) statement), replacer, statement, statementsIt, block); executeAll(new ReplaceableValue.CondRValue2((StatementConditionalJump) statement), replacer, statement, statementsIt, block); @@ -56,11 +67,11 @@ public class ValueReplacer { executeAll(new ReplaceableValue.Return((StatementReturn) statement), replacer, statement, statementsIt, block); } else if(statement instanceof StatementPhiBlock) { for(StatementPhiBlock.PhiVariable phiVariable : ((StatementPhiBlock) statement).getPhiVariables()) { - executeAll(new ReplaceableValue.PhiVariable(phiVariable), replacer, statement, statementsIt, block); int size = phiVariable.getValues().size(); for(int i = 0; i < size; i++) { executeAll(new ReplaceableValue.PhiValue(phiVariable, i), replacer, statement, statementsIt, block); } + executeAll(new ReplaceableValue.PhiVariable(phiVariable), replacer, statement, statementsIt, block); } } } @@ -78,7 +89,12 @@ public class ValueReplacer { } } - public static Collection getSubValues(RValue value) { + /** + * Get the sub values for an RValue. + * @param value The RValue + * @return The sub-values of the RValue (only one level down, recursion is needed to get all contained sub-values) + */ + private static Collection getSubValues(RValue value) { ArrayList subValues = new ArrayList<>(); if(value instanceof PointerDereferenceIndexed) { subValues.add(new ReplaceableValue.Pointer((PointerDereference) value)); @@ -91,6 +107,12 @@ public class ValueReplacer { for(int i = 0; i < size; i++) { subValues.add(new ReplaceableValue.ListElement(valueList, i)); } + } else if(value instanceof ConstantArrayList) { + ConstantArrayList constantArrayList = (ConstantArrayList) value; + int size = constantArrayList.getElements().size(); + for(int i=0;i aliases; public AliasReplacer(Map aliases) { this.aliases = aliases; + this.replaced = false; } - /** - * Get the alias to use for an RValue. - * Also looks inside any constant values for replacements. - * If a replacement is found the replacement is performed and the new value is returned. If no replacement is found null is returned. - * - * @param rValue The RValue to find an alias for - * @return The alias to use. Null if no alias exists. - */ - public static RValue getReplacement(RValue rValue, Map aliases) { - if(rValue instanceof SymbolRef) { - RValue alias = aliases.get(rValue); - if(alias != null) { - if(alias.equals(rValue)) { - return alias; - } - RValue replacement = getReplacement(alias, aliases); - if(replacement != null) { - return replacement; - } else { - return alias; - } - } - } else if(rValue instanceof ConstantUnary) { - ConstantUnary constantUnary = (ConstantUnary) rValue; - ConstantValue alias = (ConstantValue) getReplacement(constantUnary.getOperand(), aliases); - if(alias != null) { - return new ConstantUnary(constantUnary.getOperator(), alias); - } - } else if(rValue instanceof ConstantCastValue) { - ConstantCastValue constantCastValue = (ConstantCastValue) rValue; - ConstantValue alias = (ConstantValue) getReplacement(constantCastValue.getValue(), aliases); - if(alias != null) { - return new ConstantCastValue(constantCastValue.getToType(), alias); - } - } else if(rValue instanceof ConstantArrayFilled) { - ConstantArrayFilled arrayFilled = (ConstantArrayFilled) rValue; - ConstantValue alias = (ConstantValue) getReplacement(arrayFilled.getSize(), aliases); - if(alias != null) { - return new ConstantArrayFilled(arrayFilled.getElementType(), alias); - } - } else if(rValue instanceof ConstantBinary) { - ConstantBinary constantBinary = (ConstantBinary) rValue; - ConstantValue aliasLeft = (ConstantValue) getReplacement(constantBinary.getLeft(), aliases); - ConstantValue aliasRight = (ConstantValue) getReplacement(constantBinary.getRight(), aliases); - if(aliasLeft != null || aliasRight != null) { - if(aliasLeft == null) { - aliasLeft = constantBinary.getLeft(); - } - if(aliasRight == null) { - aliasRight = constantBinary.getRight(); - } - return new ConstantBinary(aliasLeft, constantBinary.getOperator(), aliasRight); - } - } else if(rValue instanceof ConstantArrayList) { - ConstantArrayList constantArrayList = (ConstantArrayList) rValue; - ArrayList replacementList = new ArrayList<>(); - boolean any = false; - for(ConstantValue elemValue : constantArrayList.getElements()) { - RValue elemReplacement = getReplacement(elemValue, aliases); - if(elemReplacement != null) { - replacementList.add((ConstantValue) elemReplacement); - any = true; - } else { - replacementList.add(elemValue); - } - } - if(any) { - return new ConstantArrayList(replacementList, constantArrayList.getElementType()); - } - } - - // No replacement found - return null - return null; - + public boolean isReplaced() { + return replaced; } /** @@ -107,8 +41,32 @@ public class AliasReplacer implements Replacer { RValue replacement = getReplacement(replaceable.get(), aliases); if(replacement != null) { replaceable.set(replacement); + this.replaced = true; } } } + /** + * Get the alias to use for an RValue. + * Also looks inside any constant values for replacements. + * If a replacement is found the replacement is performed and the new value is returned. If no replacement is found null is returned. + * + * @param rValue The RValue to find an alias for + * @return The alias to use. Null if no alias exists. + */ + private static RValue getReplacement(RValue rValue, Map aliases) { + if(rValue instanceof SymbolRef) { + RValue alias = aliases.get(rValue); + if(alias != null) { + RValue replacement = getReplacement(alias, aliases); + if(replacement != null) { + return replacement; + } else { + return alias; + } + } + } + return null; + } + } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1GenerateSingleStaticAssignmentForm.java b/src/main/java/dk/camelot64/kickc/passes/Pass1GenerateSingleStaticAssignmentForm.java index c66222836..84e61bdd1 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1GenerateSingleStaticAssignmentForm.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1GenerateSingleStaticAssignmentForm.java @@ -1,16 +1,22 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.*; +import dk.camelot64.kickc.model.CompileError; +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.iterator.ReplaceableValue; import dk.camelot64.kickc.model.iterator.ValueReplacer; -import dk.camelot64.kickc.model.values.LabelRef; -import dk.camelot64.kickc.model.values.RValue; -import dk.camelot64.kickc.model.values.VariableRef; -import dk.camelot64.kickc.model.statements.*; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementAssignment; +import dk.camelot64.kickc.model.statements.StatementLValue; +import dk.camelot64.kickc.model.statements.StatementPhiBlock; import dk.camelot64.kickc.model.symbols.Scope; import dk.camelot64.kickc.model.symbols.Variable; import dk.camelot64.kickc.model.symbols.VariableUnversioned; import dk.camelot64.kickc.model.symbols.VariableVersion; +import dk.camelot64.kickc.model.values.LValue; +import dk.camelot64.kickc.model.values.LabelRef; +import dk.camelot64.kickc.model.values.RValue; +import dk.camelot64.kickc.model.values.VariableRef; import java.util.Collection; import java.util.LinkedHashMap; @@ -74,27 +80,6 @@ public class Pass1GenerateSingleStaticAssignmentForm extends Pass1Base { } } - /** - * Version all variable uses in the replaceable value - * - * @param replaceableValue The value to version variable usages in - * @param blockVersions Newest version of variables in the block. - * @param blockNewPhis New phi functions introduced in the block to create versions of variables. - */ - private void execute( - ReplaceableValue replaceableValue, - Map blockVersions, - Map blockNewPhis) { - RValue value = replaceableValue.get(); - VariableVersion version = findOrCreateVersion(value, blockVersions, blockNewPhis); - if(version != null) { - replaceableValue.set(version.getRef()); - } - for(ReplaceableValue subValue : ValueReplacer.getSubValues(replaceableValue.get())) { - execute(subValue, blockVersions, blockNewPhis); - } - } - /** * Version all uses of non-versioned non-intermediary variables */ @@ -104,29 +89,26 @@ public class Pass1GenerateSingleStaticAssignmentForm extends Pass1Base { Map blockVersions = new LinkedHashMap<>(); // New phi functions introduced in the block to create versions of variables. Map blockNewPhis = new LinkedHashMap<>(); - for(Statement statement : block.getStatements()) { - if(statement instanceof StatementReturn) { - execute(new ReplaceableValue.Return((StatementReturn) statement), blockVersions, blockNewPhis); - } else if(statement instanceof StatementConditionalJump) { - execute(new ReplaceableValue.CondRValue2((StatementConditionalJump) statement), blockVersions, blockNewPhis); - } else if(statement instanceof StatementAssignment) { - StatementAssignment assignment = (StatementAssignment) statement; - execute(new ReplaceableValue.RValue1(assignment), blockVersions, blockNewPhis); - execute(new ReplaceableValue.RValue2(assignment), blockVersions, blockNewPhis); - execute(new ReplaceableValue.LValue(assignment), blockVersions, blockNewPhis); - - // Update map of versions encountered in the block - dk.camelot64.kickc.model.values.LValue lValue = assignment.getlValue(); + ValueReplacer.executeAll(block, (replaceable, currentStmt, stmtIt, currentBlock) -> { + RValue value = replaceable.get(); + VariableVersion version = findOrCreateVersion(value, blockVersions, blockNewPhis); + if(version != null) { + replaceable.set(version.getRef()); + } + // Update map of versions encountered in the block + if(currentStmt instanceof StatementAssignment && replaceable instanceof ReplaceableValue.LValue) { + StatementAssignment assignment = (StatementAssignment) currentStmt; + LValue lValue = assignment.getlValue(); if(lValue instanceof VariableRef) { VariableRef lValueRef = (VariableRef) lValue; - Variable variable = getScope().getVariable(lValueRef); + Variable variable = Pass1GenerateSingleStaticAssignmentForm.this.getScope().getVariable(lValueRef); if(variable instanceof VariableVersion) { VariableVersion versioned = (VariableVersion) variable; blockVersions.put(versioned.getVersionOf(), versioned); } } } - } + }); // Add new Phi functions to block for(VariableUnversioned symbol : blockNewPhis.keySet()) { block.getPhiBlock().addPhiVariable(blockNewPhis.get(symbol).getRef()); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java index edb946cf9..9fbd3a8dd 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java @@ -1,6 +1,8 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.model.*; +import dk.camelot64.kickc.model.iterator.ReplaceableValue; +import dk.camelot64.kickc.model.iterator.ValueReplacer; import dk.camelot64.kickc.model.types.SymbolTypeArray; import dk.camelot64.kickc.model.values.*; import dk.camelot64.kickc.model.symbols.ConstantVar; @@ -35,7 +37,7 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization { inline.putAll(findUnnamedConstants()); inline.putAll(findAliasConstants()); inline.putAll(findConstVarVersions()); - + // Remove all string constants List refs = new ArrayList(inline.keySet()); for(ConstantRef constantRef : refs) { @@ -75,18 +77,12 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization { SymbolType constantType = constantVar.getType(); if(constantType instanceof SymbolTypeArray) { SymbolTypeArray arrayType = (SymbolTypeArray) constantType; - RValue arraySize = arrayType.getSize(); - RValue sizeReplacement = AliasReplacer.getReplacement(arraySize, inline); - if(sizeReplacement != null) { - arrayType.setSize(sizeReplacement); - } + ReplaceableValue.TypeArraySize replaceableArrayType = new ReplaceableValue.TypeArraySize(arrayType); + ValueReplacer.executeAll(replaceableArrayType, new AliasReplacer(inline), null, null, null); } - ConstantValue constantValue = constantVar.getValue(); - RValue replacement = AliasReplacer.getReplacement(constantValue, inline); - if(replacement != null) { - constantVar.setValue((ConstantValue) replacement); - } + ReplaceableValue.ConstantVariableValue replaceableConstantVal = new ReplaceableValue.ConstantVariableValue(constantVar); + ValueReplacer.executeAll(replaceableConstantVal, new AliasReplacer(inline), null, null, null); } } @@ -101,10 +97,11 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization { replaced = false; for(ConstantRef constantRef : inline.keySet()) { ConstantValue constantValue = inline.get(constantRef); - ConstantValue replacement = (ConstantValue) AliasReplacer.getReplacement(constantValue, inline); - if(replacement != null) { - replaced = true; - inline.put(constantRef, replacement); + ReplaceableValue.GenericValue replaceable = new ReplaceableValue.GenericValue(constantValue); + AliasReplacer replacer = new AliasReplacer(inline); + ValueReplacer.executeAll(replaceable, replacer, null, null, null); + if(replacer.isReplaced()) { + inline.put(constantRef, (ConstantValue) replaceable.get()); } } } @@ -190,5 +187,4 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization { return aliases; } - } diff --git a/src/test/java/dk/camelot64/kickc/test/ref/var-forward-problem.log b/src/test/java/dk/camelot64/kickc/test/ref/var-forward-problem.log index dc9439183..69aaa7c33 100644 --- a/src/test/java/dk/camelot64/kickc/test/ref/var-forward-problem.log +++ b/src/test/java/dk/camelot64/kickc/test/ref/var-forward-problem.log @@ -7,8 +7,8 @@ void main() { const byte* screen = $400; const byte b = 'a'; -Resolved forward reference screen to (byte*) screen Resolved forward reference b to (byte) b +Resolved forward reference screen to (byte*) screen SYMBOLS (label) @1 (label) @begin