diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 10e7b3480..b9d0437ae 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -223,6 +223,7 @@ public class Compiler { program.setGraph(new Pass1ProcedureCallsReturnValue(program).generate()); new PassNUnwindLValueLists(program).execute(); + new Pass1UnwindStructVersions(program).execute(); getLog().append("\nCONTROL FLOW GRAPH SSA"); getLog().append(program.getGraph().toString(program)); diff --git a/src/main/java/dk/camelot64/kickc/model/Program.java b/src/main/java/dk/camelot64/kickc/model/Program.java index a2b95ae66..cccbfaf21 100644 --- a/src/main/java/dk/camelot64/kickc/model/Program.java +++ b/src/main/java/dk/camelot64/kickc/model/Program.java @@ -6,7 +6,6 @@ import dk.camelot64.kickc.model.statements.StatementInfos; import dk.camelot64.kickc.model.symbols.ProgramScope; import dk.camelot64.kickc.model.values.LabelRef; import dk.camelot64.kickc.model.values.VariableRef; -import dk.camelot64.kickc.passes.Pass1UnwindStructValues; import java.nio.file.Path; import java.util.ArrayList; @@ -75,7 +74,7 @@ public class Program { /** Cached phi transitions into each block. */ private Map phiTransitions; /** Struct values unwound to individual variables. */ - private Pass1UnwindStructValues.StructUnwinding structUnwinding; + private StructUnwinding structUnwinding; public Program() { this.scope = new ProgramScope(); @@ -86,11 +85,11 @@ public class Program { this.reservedZps = new ArrayList<>(); } - public Pass1UnwindStructValues.StructUnwinding getStructUnwinding() { + public StructUnwinding getStructUnwinding() { return structUnwinding; } - public void setStructUnwinding(Pass1UnwindStructValues.StructUnwinding structUnwinding) { + public void setStructUnwinding(StructUnwinding structUnwinding) { this.structUnwinding = structUnwinding; } diff --git a/src/main/java/dk/camelot64/kickc/model/StructUnwinding.java b/src/main/java/dk/camelot64/kickc/model/StructUnwinding.java new file mode 100644 index 000000000..ccaa826bb --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/StructUnwinding.java @@ -0,0 +1,113 @@ +package dk.camelot64.kickc.model; + +import dk.camelot64.kickc.model.values.LValue; +import dk.camelot64.kickc.model.values.SymbolRef; +import dk.camelot64.kickc.model.values.VariableRef; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Keeps track of all structs that have been unwound into member variables. + */ +public class StructUnwinding { + + /** Maps struct variables to unwinding of each member. */ + Map structVariables = new LinkedHashMap<>(); + + /** + * Get information about how a struct variable was unwound into member variables + * + * @param ref The variable to look for + * @return Information about the unwinding. Null if not unwound + */ + public VariableUnwinding getVariableUnwinding(VariableRef ref) { + return structVariables.get(ref); + } + + /** + * Add information about how a struct variable was unwound into member variables + * + * @param ref The variable to add information for + * @return The new information about the unwinding. + */ + public VariableUnwinding createVariableUnwinding(VariableRef ref) { + VariableUnwinding existing = structVariables.put(ref, new VariableUnwinding()); + if(existing != null) { + throw new InternalError("ERROR! Struct unwinding was already created once! " + ref.toString()); + } + return structVariables.get(ref); + } + + /** + * Find the struct variable that the passed symbol was unwound from. + * + * @param symbolRef The symbol to look for + * @return The struct variable containing it. null if the passed symbol is not an unwound variable. + */ + public VariableRef getContainingStructVariable(SymbolRef symbolRef) { + for(VariableRef structVarRef : structVariables.keySet()) { + VariableUnwinding variableUnwinding = getVariableUnwinding(structVarRef); + for(String memberName : variableUnwinding.getMemberNames()) { + LValue memberUnwinding = variableUnwinding.getMemberUnwinding(memberName); + if(memberUnwinding instanceof VariableRef && memberUnwinding.equals(symbolRef)) { + return structVarRef; + } + } + } + return null; + } + + + /** Information about how a single struct variable was unwound. */ + public static class VariableUnwinding implements StructMemberUnwinding { + + /** Maps member names to the unwound variables. */ + Map memberUnwinding = new LinkedHashMap<>(); + + /** Set how a member variable was unwound to a specific (new) variable. */ + public void setMemberUnwinding(String memberName, VariableRef memberVariableUnwound) { + this.memberUnwinding.put(memberName, memberVariableUnwound); + } + + /** + * Get the names of the members of the struct + * + * @return the names + */ + public List getMemberNames() { + return new ArrayList<>(memberUnwinding.keySet()); + } + + /** + * Get the (new) variable that a specific member was unwound to + * + * @param memberName The member name + * @return The new variable + */ + public LValue getMemberUnwinding(String memberName) { + return this.memberUnwinding.get(memberName); + } + } + + /** Information about how members of an struct Lvalue is unwound. */ + public interface StructMemberUnwinding { + + /** + * Get the names of the members of the struct + * + * @return the names + */ + List getMemberNames(); + + /** + * Get the LValue that a specific member was unwound to + * + * @param memberName The member name + * @return The unwinding of the member + */ + LValue getMemberUnwinding(String memberName); + } +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java index 24d48a63c..1e78f07b2 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java @@ -68,7 +68,7 @@ public class Pass1ProcedureCallParameters extends ControlFlowGraphCopyVisitor { // Special handing of struct value returns if(procReturnVar.getType() instanceof SymbolTypeStruct) { - Pass1UnwindStructValues.StructUnwinding.VariableUnwinding returnVarUnwinding = program.getStructUnwinding().getVariableUnwinding((VariableRef) procReturnVarRef); + StructUnwinding.VariableUnwinding returnVarUnwinding = program.getStructUnwinding().getVariableUnwinding((VariableRef) procReturnVarRef); if(returnVarUnwinding!=null) { ArrayList unwoundReturnVars = new ArrayList<>(); for(String memberName : returnVarUnwinding.getMemberNames()) { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java index 7d4cc515d..f9cc39d98 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java @@ -269,7 +269,7 @@ public class Pass1UnwindStructValues extends Pass1Base { private boolean unwindAssignment(StatementAssignment assignment, SymbolTypeStruct structType, ListIterator stmtIt, ControlFlowBlock currentBlock, StructUnwinding structUnwinding) { boolean modified = false; - StructMemberUnwinding memberUnwinding = getStructMemberUnwinding(assignment.getlValue(), structType, structUnwinding, assignment, stmtIt, currentBlock); + StructUnwinding.StructMemberUnwinding memberUnwinding = getStructMemberUnwinding(assignment.getlValue(), structType, structUnwinding, assignment, stmtIt, currentBlock); if(memberUnwinding == null) { throw new CompileError("Cannot unwind struct assignment " + assignment.toString(getProgram(), false), assignment); } @@ -322,7 +322,7 @@ public class Pass1UnwindStructValues extends Pass1Base { SymbolType sourceType = SymbolTypeInference.inferType(getScope(), assignment.getrValue2()); if(sourceType.equals(structType)) { // Copying a struct - unwind to assigning each member! - StructMemberUnwinding sourceMemberUnwinding = getStructMemberUnwinding((LValue) assignment.getrValue2(), structType, structUnwinding, assignment, stmtIt, currentBlock); + StructUnwinding.StructMemberUnwinding sourceMemberUnwinding = getStructMemberUnwinding((LValue) assignment.getrValue2(), structType, structUnwinding, assignment, stmtIt, currentBlock); if(sourceMemberUnwinding != null) { List membersUnwound = new ArrayList<>(); stmtIt.previous(); @@ -351,7 +351,7 @@ public class Pass1UnwindStructValues extends Pass1Base { return modified; } - private StructMemberUnwinding getStructMemberUnwinding(LValue lValue, SymbolTypeStruct lValueType, StructUnwinding structUnwinding, Statement currentStmt, ListIterator stmtIt, ControlFlowBlock currentBlock) { + private StructUnwinding.StructMemberUnwinding getStructMemberUnwinding(LValue lValue, SymbolTypeStruct lValueType, StructUnwinding structUnwinding, Statement currentStmt, ListIterator stmtIt, ControlFlowBlock currentBlock) { if(lValue instanceof VariableRef) { return structUnwinding.getVariableUnwinding((VariableRef) lValue); } else if(lValue instanceof PointerDereferenceSimple) { @@ -364,120 +364,15 @@ public class Pass1UnwindStructValues extends Pass1Base { } - /** Information about how members of an struct Lvalue is unwound. */ - interface StructMemberUnwinding { - - /** - * Get the names of the members of the struct - * - * @return the names - */ - List getMemberNames(); - - /** - * Get the LValue that a specific member was unwound to - * - * @param memberName The member name - * @return The unwinding of the member - */ - LValue getMemberUnwinding(String memberName); - } - - - /** - * Keeps track of all structs that have been unwound into member variables. - */ - public static class StructUnwinding { - - /** Maps struct variables to unwinding of each member. */ - Map structVariables = new LinkedHashMap<>(); - - /** - * Get information about how a struct variable was unwound into member variables - * - * @param ref The variable to look for - * @return Information about the unwinding. Null if not unwound - */ - VariableUnwinding getVariableUnwinding(VariableRef ref) { - return structVariables.get(ref); - } - - /** - * Add information about how a struct variable was unwound into member variables - * - * @param ref The variable to add information for - * @return The new information about the unwinding. - */ - VariableUnwinding createVariableUnwinding(VariableRef ref) { - VariableUnwinding existing = structVariables.put(ref, new VariableUnwinding()); - if(existing != null) { - throw new InternalError("ERROR! Struct unwinding was already created once! " + ref.toString()); - } - return structVariables.get(ref); - } - - /** - * Find the struct variable that the passed symbol was unwound from. - * - * @param symbolRef The symbol to look for - * @return The struct variable containing it. null if the passed symbol is not an unwound variable. - */ - public VariableRef getContainingStructVariable(SymbolRef symbolRef) { - for(VariableRef structVarRef : structVariables.keySet()) { - VariableUnwinding variableUnwinding = getVariableUnwinding(structVarRef); - for(String memberName : variableUnwinding.getMemberNames()) { - LValue memberUnwinding = variableUnwinding.getMemberUnwinding(memberName); - if(memberUnwinding instanceof VariableRef && memberUnwinding.equals(symbolRef)) { - return structVarRef; - } - } - } - return null; - } - - - /** Information about how a single struct variable was unwound. */ - static class VariableUnwinding implements StructMemberUnwinding { - - /** Maps member names to the unwound variables. */ - Map memberUnwinding = new LinkedHashMap<>(); - - /** Set how a member variable was unwound to a specific (new) variable. */ - void setMemberUnwinding(String memberName, VariableRef memberVariableUnwound) { - this.memberUnwinding.put(memberName, memberVariableUnwound); - } - - /** - * Get the names of the members of the struct - * - * @return the names - */ - public List getMemberNames() { - return new ArrayList<>(memberUnwinding.keySet()); - } - - /** - * Get the (new) variable that a specific member was unwound to - * - * @param memberName The member name - * @return The new variable - */ - public LValue getMemberUnwinding(String memberName) { - return this.memberUnwinding.get(memberName); - } - } - - } - /** Unwinding for a simple pointer deref to a struct. */ - private class StructMemberUnwindingPointerDerefSimple implements StructMemberUnwinding { + private class StructMemberUnwindingPointerDerefSimple implements StructUnwinding.StructMemberUnwinding { private final StructDefinition structDefinition; private final ControlFlowBlock currentBlock; private final ListIterator stmtIt; private final PointerDereferenceSimple pointerDeref; private final Statement currentStmt; - public StructMemberUnwindingPointerDerefSimple(PointerDereferenceSimple pointerDeref, StructDefinition structDefinition, ListIterator stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) { + StructMemberUnwindingPointerDerefSimple(PointerDereferenceSimple pointerDeref, StructDefinition structDefinition, ListIterator stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) { this.structDefinition = structDefinition; this.currentBlock = currentBlock; this.stmtIt = stmtIt; @@ -511,14 +406,14 @@ public class Pass1UnwindStructValues extends Pass1Base { } /** Unwinding for a indexed pointer deref to a struct. */ - private class StructMemberUnwindingPointerDerefIndexed implements StructMemberUnwinding { + private class StructMemberUnwindingPointerDerefIndexed implements StructUnwinding.StructMemberUnwinding { private final StructDefinition structDefinition; private final ControlFlowBlock currentBlock; private final ListIterator stmtIt; private final PointerDereferenceIndexed pointerDeref; private final Statement currentStmt; - public StructMemberUnwindingPointerDerefIndexed(PointerDereferenceIndexed pointerDeref, StructDefinition structDefinition, ListIterator stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) { + StructMemberUnwindingPointerDerefIndexed(PointerDereferenceIndexed pointerDeref, StructDefinition structDefinition, ListIterator stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) { this.structDefinition = structDefinition; this.currentBlock = currentBlock; this.stmtIt = stmtIt; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructVersions.java b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructVersions.java new file mode 100644 index 000000000..374e9dd04 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructVersions.java @@ -0,0 +1,60 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.ControlFlowBlock; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.StructUnwinding; +import dk.camelot64.kickc.model.statements.Statement; +import dk.camelot64.kickc.model.statements.StatementAssignment; +import dk.camelot64.kickc.model.symbols.StructDefinition; +import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.types.SymbolTypeStruct; +import dk.camelot64.kickc.model.values.RValue; +import dk.camelot64.kickc.model.values.StructUnwoundPlaceholder; +import dk.camelot64.kickc.model.values.VariableRef; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** Find the versioned unwound structs - and update the StructUnwinding data structure */ +public class Pass1UnwindStructVersions extends Pass1Base { + + public Pass1UnwindStructVersions(Program program) { + super(program); + } + + @Override + public boolean step() { + boolean modified = false; + StructUnwinding structUnwinding = getProgram().getStructUnwinding(); + for(ControlFlowBlock block : getGraph().getAllBlocks()) { + for(Statement statement : block.getStatements()) { + if(statement instanceof StatementAssignment) { + StatementAssignment assignment = (StatementAssignment) statement; + if(assignment.getOperator() == null && assignment.getlValue() instanceof VariableRef && assignment.getrValue2() instanceof StructUnwoundPlaceholder) { + VariableRef structVarRef = (VariableRef) assignment.getlValue(); + if(structUnwinding.getVariableUnwinding(structVarRef) == null) { + StructUnwinding.VariableUnwinding versionedUnwinding = structUnwinding.createVariableUnwinding(structVarRef); + StructUnwoundPlaceholder placeholder = (StructUnwoundPlaceholder) assignment.getrValue2(); + SymbolTypeStruct typeStruct = placeholder.getTypeStruct(); + StructDefinition structDefinition = typeStruct.getStructDefinition(getProgram().getScope()); + Collection members = structDefinition.getAllVariables(false); + Iterator memberDefIt = members.iterator(); + List unwoundMembers = placeholder.getUnwoundMembers(); + Iterator memberUnwoundIt = unwoundMembers.iterator(); + while(memberDefIt.hasNext()) { + Variable memberVar = memberDefIt.next(); + RValue memberVal = memberUnwoundIt.next(); + versionedUnwinding.setMemberUnwinding(memberVar.getLocalName(), (VariableRef) memberVal); + } + getLog().append("Adding versioned struct unwinding for "+assignment.getlValue().toString(getProgram())); + modified = true; + } + } + } + } + } + return modified; + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java index 325e78275..85784a04d 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java @@ -1,9 +1,6 @@ package dk.camelot64.kickc.passes; -import dk.camelot64.kickc.model.CompileError; -import dk.camelot64.kickc.model.ConstantNotLiteral; -import dk.camelot64.kickc.model.ControlFlowBlock; -import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.*; import dk.camelot64.kickc.model.iterator.ProgramValueIterator; import dk.camelot64.kickc.model.operators.OperatorBinary; import dk.camelot64.kickc.model.operators.OperatorUnary; @@ -330,7 +327,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization { } // If the symbol is part of an unwound struct - look at the struct itself - Pass1UnwindStructValues.StructUnwinding structUnwinding = program.getStructUnwinding(); + StructUnwinding structUnwinding = program.getStructUnwinding(); VariableRef structVarRef = structUnwinding.getContainingStructVariable(symbolRef); if(structVarRef != null) { return isAddressOfUsed(structVarRef, program); diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java index e71c36a2f..7c40bf578 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java @@ -2,6 +2,7 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.model.ControlFlowBlock; import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.StructUnwinding; import dk.camelot64.kickc.model.VariableReferenceInfos; import dk.camelot64.kickc.model.statements.*; import dk.camelot64.kickc.model.symbols.ConstantVar; @@ -50,13 +51,9 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization { eliminate = true; } else if(variable.isDeclaredVolatile() && variable.getType() instanceof SymbolTypeStruct) { // If an unwound volatile struct - eliminate it - if(variable.getRef().isVersion()) { - String fullNameUnversioned = variable.getRef().getFullNameUnversioned(); - VariableRef unversionedRef = new VariableRef(fullNameUnversioned); - Pass1UnwindStructValues.StructUnwinding.VariableUnwinding variableUnwinding = getProgram().getStructUnwinding().getVariableUnwinding(unversionedRef); - if(variableUnwinding != null) { - eliminate = true; - } + StructUnwinding.VariableUnwinding variableUnwinding = getProgram().getStructUnwinding().getVariableUnwinding(variable.getRef()); + if(variableUnwinding != null) { + eliminate = true; } } if(eliminate) { diff --git a/src/test/ref/complex/clearscreen/clearscreen.log b/src/test/ref/complex/clearscreen/clearscreen.log index 58a5babf8..0c63c901a 100644 --- a/src/test/ref/complex/clearscreen/clearscreen.log +++ b/src/test/ref/complex/clearscreen/clearscreen.log @@ -189,6 +189,8 @@ Culled Empty Block (label) @15 Culled Empty Block (label) setupRasterIrq::@4 Unwinding list assignment { (byte) main::$9_x, (byte) main::$9_y, (word) main::$9_dist } ← { (byte) getCharToProcess::return_x, (byte) getCharToProcess::return_y, (word) getCharToProcess::return_dist } Unwinding list assignment { (byte) getCharToProcess::return_x#0, (byte) getCharToProcess::return_y#0, (word) getCharToProcess::return_dist#0 } ← { (byte) getCharToProcess::return_x#2, (byte) getCharToProcess::return_y#2, (word) getCharToProcess::return_dist#2 } +Adding versioned struct unwinding for (struct ProcessingChar) getCharToProcess::return#0 +Adding versioned struct unwinding for (struct ProcessingChar) getCharToProcess::return#1 CONTROL FLOW GRAPH SSA @begin: scope:[] from diff --git a/src/test/ref/struct-5.log b/src/test/ref/struct-5.log index bcc58221a..f4a855835 100644 --- a/src/test/ref/struct-5.log +++ b/src/test/ref/struct-5.log @@ -31,6 +31,8 @@ Culled Empty Block (label) @1 Culled Empty Block (label) point::@1 Unwinding list assignment { (byte) main::$0_x, (byte) main::$0_y } ← { (byte) point::return_x, (byte) point::return_y } Unwinding list assignment { (byte) point::return_x#0, (byte) point::return_y#0 } ← { (byte) point::return_x#2, (byte) point::return_y#2 } +Adding versioned struct unwinding for (struct Point) point::return#0 +Adding versioned struct unwinding for (struct Point) point::return#1 CONTROL FLOW GRAPH SSA @begin: scope:[] from diff --git a/src/test/ref/struct-ptr-12.log b/src/test/ref/struct-ptr-12.log index fd305fe89..be7c9b001 100644 --- a/src/test/ref/struct-ptr-12.log +++ b/src/test/ref/struct-ptr-12.log @@ -6,6 +6,7 @@ Adding struct value list initializer (byte) main::p_y ← (number) 3 Rewriting struct pointer member access *((struct Point*) main::q).x Rewriting struct pointer member access *((struct Point*) main::q).y Adding pointer type conversion cast (byte*) main::SCREEN in (byte*) main::SCREEN ← (number) $400 +Adding versioned struct unwinding for (struct Point) main::p#0 CONTROL FLOW GRAPH SSA @begin: scope:[] from diff --git a/src/test/ref/struct-ptr-14.log b/src/test/ref/struct-ptr-14.log index c8c06ff01..0c4f159e9 100644 --- a/src/test/ref/struct-ptr-14.log +++ b/src/test/ref/struct-ptr-14.log @@ -9,6 +9,7 @@ Rewriting struct pointer member access *((struct Point*) set::ptr).x Rewriting struct pointer member access *((struct Point*) set::ptr).y Adding pointer type conversion cast (byte*) main::SCREEN in (byte*) main::SCREEN ← (number) $400 Culled Empty Block (label) @1 +Adding versioned struct unwinding for (struct Point) main::p#0 CONTROL FLOW GRAPH SSA @begin: scope:[] from