diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 39375caa2..6e0230899 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -203,18 +203,21 @@ public class Compiler { getLog().append(program.getProcedureModifiedVars().toString(program)); } + getLog().append("CONTROL FLOW GRAPH (CLEANED)"); + getLog().append(program.getGraph().toString(program)); + new Pass1ProcedureCallParameters(program).generate(); - //getLog().append("CONTROL FLOW GRAPH WITH ASSIGNMENT CALL"); - //getLog().append(program.getGraph().toString(program)); + getLog().append("CONTROL FLOW GRAPH (CALL PARAMETERS)"); + getLog().append(program.getGraph().toString(program)); new Pass1GenerateSingleStaticAssignmentForm(program).execute(); - //getLog().append("CONTROL FLOW GRAPH SSA"); - //getLog().append(program.getGraph().toString(program)); + getLog().append("CONTROL FLOW GRAPH (SSA)"); + getLog().append(program.getGraph().toString(program)); program.setGraph(new Pass1ProcedureCallsReturnValue(program).generate()); - getLog().append("\nCONTROL FLOW GRAPH SSA"); + getLog().append("\nCONTROL FLOW GRAPH SSA (RETURN VALUES)"); getLog().append(program.getGraph().toString(program)); getLog().append("SYMBOL TABLE SSA"); diff --git a/src/main/java/dk/camelot64/kickc/model/values/ValueList.java b/src/main/java/dk/camelot64/kickc/model/values/ValueList.java index a81adb58a..8669bf12b 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/ValueList.java +++ b/src/main/java/dk/camelot64/kickc/model/values/ValueList.java @@ -5,12 +5,10 @@ import dk.camelot64.kickc.model.Program; import java.util.List; /** - * A list of sub-values. Used for array variable initializers and word from byte constructors - * (in the future also usable for dword from byte, dword from double etc.). - * Compilation execution will resolve into a constant array, - * constant word or word constructor operator before ASM-generation. + * A list of sub-values. + * Used for array variable initializers, struct value initilizers, word/dword initializers. */ -public class ValueList implements RValue { +public class ValueList implements LValue { private List list; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java index 3cd877fc6..24d48a63c 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallParameters.java @@ -1,18 +1,16 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.model.*; -import dk.camelot64.kickc.model.statements.StatementPhiBlock; -import dk.camelot64.kickc.model.values.*; import dk.camelot64.kickc.model.statements.StatementAssignment; import dk.camelot64.kickc.model.statements.StatementCall; +import dk.camelot64.kickc.model.statements.StatementPhiBlock; import dk.camelot64.kickc.model.statements.StatementReturn; import dk.camelot64.kickc.model.symbols.*; import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.types.SymbolTypeStruct; +import dk.camelot64.kickc.model.values.*; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** Pass that modifies a control flow graph to call procedures by passing parameters through registers */ public class Pass1ProcedureCallParameters extends ControlFlowGraphCopyVisitor { @@ -34,7 +32,7 @@ public class Pass1ProcedureCallParameters extends ControlFlowGraphCopyVisitor { for(StatementPhiBlock.PhiVariable phiVariable : block.getPhiBlock().getPhiVariables()) { for(StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) { LabelRef splitBlockNew = splitBlockMap.get(phiRValue.getPredecessor()); - if(splitBlockNew !=null) { + if(splitBlockNew != null) { phiRValue.setPredecessor(splitBlockNew); } } @@ -64,10 +62,23 @@ public class Pass1ProcedureCallParameters extends ControlFlowGraphCopyVisitor { } String procedureName = origCall.getProcedureName(); Variable procReturnVar = procedure.getVariable("return"); - VariableRef procReturnVarRef = null; + LValue procReturnVarRef = null; if(procReturnVar != null) { procReturnVarRef = procReturnVar.getRef(); + + // Special handing of struct value returns + if(procReturnVar.getType() instanceof SymbolTypeStruct) { + Pass1UnwindStructValues.StructUnwinding.VariableUnwinding returnVarUnwinding = program.getStructUnwinding().getVariableUnwinding((VariableRef) procReturnVarRef); + if(returnVarUnwinding!=null) { + ArrayList unwoundReturnVars = new ArrayList<>(); + for(String memberName : returnVarUnwinding.getMemberNames()) { + unwoundReturnVars.add(returnVarUnwinding.getMemberUnwinding(memberName)); + } + procReturnVarRef = new ValueList(unwoundReturnVars); + } + } } + StatementCall copyCall = new StatementCall(procReturnVarRef, procedureName, null, origCall.getSource(), Comment.NO_COMMENTS); copyCall.setProcedure(procedureRef); addStatementToCurrentBlock(copyCall); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallsReturnValue.java b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallsReturnValue.java index 1a2ee6208..b92efb999 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallsReturnValue.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1ProcedureCallsReturnValue.java @@ -42,13 +42,15 @@ public class Pass1ProcedureCallsReturnValue extends ControlFlowGraphCopyVisitor // Find return variable final version Label returnBlockLabel = procedure.getLabel(SymbolRef.PROCEXIT_BLOCK_NAME); ControlFlowBlock returnBlock = program.getGraph().getBlock(returnBlockLabel.getRef()); - VariableRef returnVarFinal = null; + RValue returnVarFinal = null; for(Statement statement : returnBlock.getStatements()) { if(statement instanceof StatementReturn) { StatementReturn statementReturn = (StatementReturn) statement; RValue returnValue = statementReturn.getValue(); if(returnValue instanceof VariableRef) { - returnVarFinal = (VariableRef) returnValue; + returnVarFinal = returnValue; + } else if(returnValue instanceof ValueList) { + returnVarFinal = returnValue; } } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java index a288c013a..a33bf7c4b 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java @@ -6,6 +6,7 @@ import dk.camelot64.kickc.model.iterator.ProgramValueIterator; import dk.camelot64.kickc.model.statements.Statement; import dk.camelot64.kickc.model.statements.StatementAssignment; import dk.camelot64.kickc.model.statements.StatementCall; +import dk.camelot64.kickc.model.statements.StatementReturn; import dk.camelot64.kickc.model.symbols.*; import dk.camelot64.kickc.model.types.SymbolTypeStruct; import dk.camelot64.kickc.model.values.*; @@ -30,13 +31,13 @@ public class Pass1UnwindStructValues extends Pass1Base { } // Iterate through all scopes generating member-variables for each struct - modified |= unwindAllStructVariables(structUnwinding); + modified |= unwindStructVariables(structUnwinding); // Unwind all procedure declaration parameters - modified |= unwindProcedureParameters(structUnwinding); + modified |= unwindStructParameters(structUnwinding); // Unwind all usages of struct values modified |= unwindStructReferences(structUnwinding); // Change all usages of members of struct values - modified |= unwindMemberReferences(structUnwinding); + modified |= unwindStructMemberReferences(structUnwinding); return modified; } @@ -61,6 +62,8 @@ public class Pass1UnwindStructValues extends Pass1Base { } } else if(statement instanceof StatementCall) { modified |= unwindCall((StatementCall) statement, structUnwinding); + } else if(statement instanceof StatementReturn) { + modified |= unwindReturn((StatementReturn) statement, structUnwinding); } } } @@ -72,7 +75,7 @@ public class Pass1UnwindStructValues extends Pass1Base { * * @param structUnwinding Information about all unwound struct variables */ - private boolean unwindMemberReferences(StructUnwinding structUnwinding) { + private boolean unwindStructMemberReferences(StructUnwinding structUnwinding) { AtomicBoolean modified = new AtomicBoolean(false); ProgramValueIterator.execute( getProgram(), (programValue, currentStmt, stmtIt, currentBlock) -> @@ -97,14 +100,34 @@ public class Pass1UnwindStructValues extends Pass1Base { /** * Unwind any call parameter that is a struct value into the member values * - * @param statementCall The call to unwind + * @param call The call to unwind * @param structUnwinding Information about all unwound struct variables */ - private boolean unwindCall(StatementCall statementCall, StructUnwinding structUnwinding) { - //Procedure procedure = getScope().getProcedure(statementCall.getProcedure()); + private boolean unwindCall(StatementCall call, StructUnwinding structUnwinding) { + + // Unwind struct value return value + boolean lvalUnwound = false; + if(call.getlValue() instanceof VariableRef) { + Variable lvalueVar = getScope().getVariable((VariableRef) call.getlValue()); + if(lvalueVar.getType() instanceof SymbolTypeStruct) { + StructUnwinding.VariableUnwinding lValueVarUnwinding = structUnwinding.getVariableUnwinding(lvalueVar.getRef()); + if(lValueVarUnwinding != null) { + ArrayList unwoundMembers = new ArrayList<>(); + for(String memberName : lValueVarUnwinding.getMemberNames()) { + unwoundMembers.add(lValueVarUnwinding.getMemberUnwinding(memberName)); + } + ValueList unwoundLValue = new ValueList(unwoundMembers); + call.setlValue(unwoundLValue); + getLog().append("Converted procedure call LValue to member variables " + call.toString(getProgram(), false)); + lvalUnwound = true; + } + } + } + + // Unwind any struct value parameters ArrayList unwoundParameters = new ArrayList<>(); - boolean anyUnwound = false; - for(RValue parameter : statementCall.getParameters()) { + boolean anyParameterUnwound = false; + for(RValue parameter : call.getParameters()) { boolean unwound = false; if(parameter instanceof VariableRef) { Variable variable = getScope().getVariable((VariableRef) parameter); @@ -116,7 +139,7 @@ public class Pass1UnwindStructValues extends Pass1Base { unwoundParameters.add(variableUnwinding.getMemberUnwinding(memberName)); } unwound = true; - anyUnwound = true; + anyParameterUnwound = true; } } } @@ -125,19 +148,49 @@ public class Pass1UnwindStructValues extends Pass1Base { } } - if(anyUnwound) { - statementCall.setParameters(unwoundParameters); - getLog().append("Converted procedure struct value parameter to member variables in call " + statementCall.toString(getProgram(), false)); + if(anyParameterUnwound) { + call.setParameters(unwoundParameters); + getLog().append("Converted procedure struct value parameter to member variables in call " + call.toString(getProgram(), false)); } - return anyUnwound; + return (anyParameterUnwound || lvalUnwound); } + /** + * Unwind any return value that is a struct value into the member values + * + * @param statementReturn The return to unwind + * @param structUnwinding Information about all unwound struct variables + */ + + private boolean unwindReturn(StatementReturn statementReturn, StructUnwinding structUnwinding) { + boolean modified = false; + // Unwind struct value return value + if(statementReturn.getValue() instanceof VariableRef) { + Variable returnValueVar = getScope().getVariable((VariableRef) statementReturn.getValue()); + if(returnValueVar.getType() instanceof SymbolTypeStruct) { + StructUnwinding.VariableUnwinding returnVarUnwinding = structUnwinding.getVariableUnwinding(returnValueVar.getRef()); + if(returnVarUnwinding != null) { + ArrayList unwoundMembers = new ArrayList<>(); + for(String memberName : returnVarUnwinding.getMemberNames()) { + unwoundMembers.add(returnVarUnwinding.getMemberUnwinding(memberName)); + } + ValueList unwoundReturnValue = new ValueList(unwoundMembers); + statementReturn.setValue(unwoundReturnValue); + getLog().append("Converted procedure struct return value to member variables " + statementReturn.toString(getProgram(), false)); + modified = true; + } + } + } + return modified; + } + + /** * Iterate through all procedures changing parameter lists by unwinding each struct value parameter to the unwound member variables * * @param structUnwinding Information about all unwound struct variables (including procedure parameters) */ - private boolean unwindProcedureParameters(StructUnwinding structUnwinding) { + private boolean unwindStructParameters(StructUnwinding structUnwinding) { boolean modified = false; // Iterate through all procedures changing parameter lists by unwinding each struct value parameter for(Procedure procedure : getScope().getAllProcedures(true)) { @@ -168,7 +221,7 @@ public class Pass1UnwindStructValues extends Pass1Base { * * @return Information about all unwound struct variables */ - private boolean unwindAllStructVariables(StructUnwinding structUnwinding) { + private boolean unwindStructVariables(StructUnwinding structUnwinding) { boolean modified = false; // Iterate through all scopes generating member-variables for each struct for(Variable variable : getScope().getAllVariables(true)) { diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index b4fffe0fb..313f0447b 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -80,10 +80,10 @@ public class TestPrograms { compileAndCompare("struct-6"); } - //@Test - //public void testStruct5() throws IOException, URISyntaxException { - // compileAndCompare("struct-5", log().verboseParse().verboseCreateSsa()); - //} + @Test + public void testStruct5() throws IOException, URISyntaxException { + compileAndCompare("struct-5", log().verboseParse().verboseCreateSsa()); + } @Test public void testStruct4() throws IOException, URISyntaxException { diff --git a/src/test/kc/struct-5.kc b/src/test/kc/struct-5.kc index 9e418bcd4..b26af0be8 100644 --- a/src/test/kc/struct-5.kc +++ b/src/test/kc/struct-5.kc @@ -7,14 +7,14 @@ struct Point { void main() { struct Point q; - q = point(2,3); + q = point(); const byte* SCREEN = 0x0400; SCREEN[0] = q.x; SCREEN[1] = q.y; } -struct Point point(byte x, byte y) { - struct Point p = { x, y }; +struct Point point() { + struct Point p = { 2, 3 }; return p; } \ No newline at end of file