diff --git a/src/main/fragment/mos6502-common/_deref_pptc1=pprc2.asm b/src/main/fragment/mos6502-common/_deref_pptc1=pprc2.asm deleted file mode 100644 index e8b915e3e..000000000 --- a/src/main/fragment/mos6502-common/_deref_pptc1=pprc2.asm +++ /dev/null @@ -1,4 +0,0 @@ -lda #<{c2} -sta {c1} -lda #>{c2} -sta {c1}+1 \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/_deref_pptc1=pssc2.asm b/src/main/fragment/mos6502-common/_deref_pptc1=pssc2.asm deleted file mode 100644 index e8b915e3e..000000000 --- a/src/main/fragment/mos6502-common/_deref_pptc1=pssc2.asm +++ /dev/null @@ -1,4 +0,0 @@ -lda #<{c2} -sta {c1} -lda #>{c2} -sta {c1}+1 \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/_deref_pptc1=pbuc2.asm b/src/main/fragment/mos6502-common/_deref_pptc1=vwuc2.asm similarity index 100% rename from src/main/fragment/mos6502-common/_deref_pptc1=pbuc2.asm rename to src/main/fragment/mos6502-common/_deref_pptc1=vwuc2.asm diff --git a/src/main/fragment/mos6502-common/_deref_pptc1=pbum1.asm b/src/main/fragment/mos6502-common/_deref_pptc1=vwum1.asm similarity index 100% rename from src/main/fragment/mos6502-common/_deref_pptc1=pbum1.asm rename to src/main/fragment/mos6502-common/_deref_pptc1=vwum1.asm diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 53a238c97..0811acc76 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -265,6 +265,8 @@ public class Compiler { //getLog().append(program.getGraph().toString(program)); new Pass1ProcedureCallParameters(program).generate(); + //getLog().append("CONTROL FLOW GRAPH (BEFORE LIST UNWINDING)"); + //getLog().append(program.getGraph().toString(program)); new PassNUnwindLValueLists(program).execute(); //new Pass1PointifyMemoryVariables(program).execute(); @@ -277,6 +279,8 @@ public class Compiler { //getLog().append(program.getGraph().toString(program)); program.setGraph(new Pass1ProcedureCallsReturnValue(program).generate()); + //getLog().append("CONTROL FLOW GRAPH (BEFORE LIST UNWINDING)"); + //getLog().append(program.getGraph().toString(program)); new PassNUnwindLValueLists(program).execute(); getLog().append("\nCONTROL FLOW GRAPH SSA"); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass3AssertRValues.java b/src/main/java/dk/camelot64/kickc/passes/Pass3AssertRValues.java index b0b9cdf05..81f3e602d 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass3AssertRValues.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass3AssertRValues.java @@ -5,7 +5,6 @@ import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.iterator.ProgramValueIterator; import dk.camelot64.kickc.model.values.RangeValue; import dk.camelot64.kickc.model.values.Value; -import dk.camelot64.kickc.model.values.ValueList; /** * Assert that all intermediate RValues in the code have been replaced in pass 2. @@ -23,14 +22,6 @@ public class Pass3AssertRValues extends Pass2SsaAssertion { public void check() throws AssertionFailed { ProgramValueIterator.execute(getGraph(), (programValue, currentStmt, stmtIt, currentBlock) -> { Value value = programValue.get(); - if(value instanceof ValueList) { - throw new InternalError( - "Error! Value list not resolved to word constructor or array initializer" + - "\n value list: " + value.toString(getProgram()) + - "\n statement: " + currentStmt.toString(getProgram(), false) - , currentStmt.getSource() - ); - } if(value instanceof RangeValue) { throw new InternalError( "Error! Ranged for() not resolved to constants" + diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4AssertNoCpuClobber.java b/src/main/java/dk/camelot64/kickc/passes/Pass4AssertNoCpuClobber.java index b9470e4ab..bdf1c3358 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4AssertNoCpuClobber.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4AssertNoCpuClobber.java @@ -4,12 +4,11 @@ import dk.camelot64.kickc.asm.AsmChunk; import dk.camelot64.kickc.asm.AsmClobber; import dk.camelot64.kickc.asm.AsmProgram; import dk.camelot64.kickc.model.*; -import dk.camelot64.kickc.model.statements.Statement; -import dk.camelot64.kickc.model.statements.StatementCallPrepare; -import dk.camelot64.kickc.model.statements.StatementPhiBlock; +import dk.camelot64.kickc.model.statements.*; import dk.camelot64.kickc.model.symbols.Variable; import dk.camelot64.kickc.model.values.LabelRef; import dk.camelot64.kickc.model.values.RValue; +import dk.camelot64.kickc.model.values.ValueList; import dk.camelot64.kickc.model.values.VariableRef; import java.util.ArrayList; @@ -85,37 +84,62 @@ public class Pass4AssertNoCpuClobber extends Pass2Base { PhiTransitions phiTransitions = programPhiTransitions.get(statementBlock.getLabel()); PhiTransitions.PhiTransition phiTransition = phiTransitions.getTransition(phiTransitionId); for(PhiTransitions.PhiTransition.PhiAssignment phiAssignment : phiTransition.getAssignments()) { + if(phiAssignment.getAssignmentIdx() != transitionAssignmentIdx) { + // Only the current transition var is assigned + VariableRef assignedVar = phiAssignment.getVariable(); + assignedVars.remove(assignedVar); + } if(phiAssignment.getAssignmentIdx() > transitionAssignmentIdx) { - // IF the assignment is later than the current one + // For all assignments later than the current one - add referenced vars to alive RValue rValue = phiAssignment.getrValue(); Collection alive = VariableReferenceInfos.getReferencedVars(rValue); aliveVars.addAll(alive); - VariableRef assignedVar = phiAssignment.getVariable(); - assignedVars.remove(assignedVar); - alive.remove(assignedVar); - } else if(phiAssignment.getAssignmentIdx() < transitionAssignmentIdx) { - // IF the assignment is before the current one - VariableRef assignedVar = phiAssignment.getVariable(); - assignedVars.remove(assignedVar); } } } // If the chunk is an call parameter prepare, examine the later call parameter prepares and update alive variables if(statement instanceof StatementCallPrepare && asmChunk.getSubStatementId() != null && asmChunk.getSubStatementIdx() != null) { - int transitionAssignmentIdx = asmChunk.getSubStatementIdx(); + int parameterIdx = asmChunk.getSubStatementIdx(); final StatementCallPrepare callPrepare = (StatementCallPrepare) statement; final int numParameters = callPrepare.getNumParameters(); - for(int idx = 0; idx < numParameters; idx++) { + for(int idx = parameterIdx + 1; idx < numParameters; idx++) { + // For parameter prepares later than the current one - add referenced to alive final RValue parameter = callPrepare.getParameter(idx); - if(idx > transitionAssignmentIdx) { - // If the parameter prepare is later than the current one - Collection alive = VariableReferenceInfos.getReferencedVars(parameter); - aliveVars.addAll(alive); + Collection alive = VariableReferenceInfos.getReferencedVars(parameter); + aliveVars.addAll(alive); + } + } + + // If the chunk is call finalize with a value list LValue, examine the LValue assigns and update alive variables + if(statement instanceof StatementCallFinalize && asmChunk.getSubStatementId() != null && asmChunk.getSubStatementIdx() != null) { + int memberIdx = asmChunk.getSubStatementIdx(); + final StatementCallFinalize stmtCallFinalize = (StatementCallFinalize) statement; + final List lValues = ((ValueList)stmtCallFinalize.getlValue()).getList(); + final int numLValues = lValues.size(); + for(int idx = 0; idx < numLValues; idx++) { + if(idx != memberIdx) { + // Only the current transition var is assigned + final VariableRef assignedVar = (VariableRef) lValues.get(idx); + assignedVars.remove(assignedVar); } } } + // If the chunk is call with a value list LValue, examine the later LValue assigns and update alive variables + if(statement instanceof StatementReturn && asmChunk.getSubStatementId() != null && asmChunk.getSubStatementIdx() != null) { + int memberIdx = asmChunk.getSubStatementIdx(); + final StatementReturn stmtReturn = (StatementReturn) statement; + final List returnValues = ((ValueList) stmtReturn.getValue()).getList(); + final int numReturnValues = returnValues.size(); + for(int idx = memberIdx + 1; idx < numReturnValues; idx++) { + // Add all referenced vars in later return values to alive + final RValue returnValue = returnValues.get(idx); + Collection alive = VariableReferenceInfos.getReferencedVars(returnValue); + aliveVars.addAll(alive); + } + } + // Non-assigned alive variables must not be clobbered for(VariableRef aliveVar : aliveVars) { Variable variable = getProgram().getSymbolInfos().getVariable(aliveVar); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 2dea37fb3..17b82a403 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -896,12 +896,33 @@ public class Pass4CodeGeneration { } // Pull result from the stack if(call.getlValue() != null) { - if(stackCleanBytes>0) + if(stackCleanBytes > 0) asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); - SymbolType returnType = procedure.getReturnType(); - AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(call.getlValue(), new StackPullValue(returnType), program, block.getScope()); - ensureEncoding(asm, asmFragmentInstanceSpecFactory); - generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); + boolean isValueListStruct = (call.getlValue() instanceof ValueList) && (procedure.getReturnType() instanceof SymbolTypeStruct); + if(!isValueListStruct) { + // Simple return value - fetch from stack + SymbolType returnType = procedure.getReturnType(); + AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(call.getlValue(), new StackPullValue(returnType), program, block.getScope()); + ensureEncoding(asm, asmFragmentInstanceSpecFactory); + generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); + } else { + // Struct value list return value - fetch each member from stack + final List lValues = ((ValueList) call.getlValue()).getList(); + final StructDefinition structDefinition = ((SymbolTypeStruct) procedure.getReturnType()).getStructDefinition(getScope()); + final List memberVars = new ArrayList<>(structDefinition.getAllVars(false)); + for(int i = 0; i < memberVars.size(); i++) { + LValue lValue = (LValue) lValues.get(i); + Variable memberDef = memberVars.get(i); + final SymbolType memberType = memberDef.getType(); + if(i > 0) + asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); + AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(lValue, new StackPullValue(memberType), program, block.getScope()); + ensureEncoding(asm, asmFragmentInstanceSpecFactory); + generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); + asm.getCurrentChunk().setSubStatementIdx(i); + asm.getCurrentChunk().setSubStatementId(memberDef.toString(program)); + } + } } } } else if(statement instanceof StatementReturn) { @@ -915,13 +936,36 @@ public class Pass4CodeGeneration { StatementReturn returnStatement = (StatementReturn) statement; if(returnStatement.getValue() != null) { // Store return value on stack - SymbolType returnType = procedure.getReturnType(); - // Find parameter/return stack size - ConstantRef returnOffsetConstant = CallingConventionStack.getReturnOffsetConstant(procedure); - AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(new StackIdxValue(returnOffsetConstant, returnType), returnStatement.getValue(), program, block.getScope()); asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); - ensureEncoding(asm, asmFragmentInstanceSpecFactory); - generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); + SymbolType returnType = procedure.getReturnType(); + boolean isValueListStruct = (returnStatement.getValue() instanceof ValueList) && (returnType instanceof SymbolTypeStruct); + if(!isValueListStruct) { + // A simple return type - put it on the stack + ConstantRef returnOffsetConstant = CallingConventionStack.getReturnOffsetConstant(procedure); + AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(new StackIdxValue(returnOffsetConstant, returnType), returnStatement.getValue(), program, block.getScope()); + ensureEncoding(asm, asmFragmentInstanceSpecFactory); + generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); + } else { + // A struct return value in a value list - Store each value on the stack + final List returnValues = ((ValueList) returnStatement.getValue()).getList(); + final StructDefinition structDefinition = ((SymbolTypeStruct) returnType).getStructDefinition(getScope()); + final List memberVars = new ArrayList<>(structDefinition.getAllVars(false)); + for(int i = 0; i < memberVars.size(); i++) { + RValue returnValue = returnValues.get(i); + Variable memberDef = memberVars.get(i); + final SymbolType memberType = memberDef.getType(); + ConstantRef returnOffsetConstant = CallingConventionStack.getReturnOffsetConstant(procedure); + ConstantRef structMemberOffsetConstant = SizeOfConstants.getStructMemberOffsetConstant(getScope(), structDefinition, memberDef.getLocalName()); + ConstantBinary memberReturnOffsetConstant = new ConstantBinary(returnOffsetConstant, Operators.PLUS, structMemberOffsetConstant); + if(i > 0) + asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); + AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(new StackIdxValue(memberReturnOffsetConstant, memberType), returnValue, program, block.getScope()); + ensureEncoding(asm, asmFragmentInstanceSpecFactory); + generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); + asm.getCurrentChunk().setSubStatementIdx(i); + asm.getCurrentChunk().setSubStatementId(memberDef.toString(program)); + } + } } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java b/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java index fff1199f4..3f85505e2 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java @@ -5,6 +5,8 @@ import dk.camelot64.kickc.model.statements.StatementAssignment; import dk.camelot64.kickc.model.statements.StatementCall; import dk.camelot64.kickc.model.statements.StatementCallFinalize; import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.values.RValue; +import dk.camelot64.kickc.model.values.ValueList; import dk.camelot64.kickc.model.values.VariableRef; import java.util.ArrayList; @@ -114,32 +116,32 @@ public class Pass4LiveRangeEquivalenceClassesFinalize extends Pass2Base { @Override public Void visitAssignment(StatementAssignment assignment) { - if(assignment.getlValue() instanceof VariableRef) { - VariableRef lValVar = (VariableRef) assignment.getlValue(); - List preferences = new ArrayList<>(); - addToEquivalenceClassSet(lValVar, preferences, liveRangeEquivalenceClassSet); - } + addValueToEquivalenceClassSet(assignment.getlValue()); return null; } @Override public Void visitCall(StatementCall call) { - if(call.getlValue() instanceof VariableRef) { - VariableRef lValVar = (VariableRef) call.getlValue(); - List preferences = new ArrayList<>(); - addToEquivalenceClassSet(lValVar, preferences, liveRangeEquivalenceClassSet); - } + addValueToEquivalenceClassSet(call.getlValue()); return null; } @Override public Void visitCallFinalize(StatementCallFinalize callFinalize) { - if(callFinalize.getlValue() instanceof VariableRef) { - VariableRef lValVar = (VariableRef) callFinalize.getlValue(); + addValueToEquivalenceClassSet(callFinalize.getlValue()); + return null; + } + + private void addValueToEquivalenceClassSet(RValue lValue) { + if(lValue instanceof VariableRef) { + VariableRef lValVar = (VariableRef) lValue; List preferences = new ArrayList<>(); addToEquivalenceClassSet(lValVar, preferences, liveRangeEquivalenceClassSet); + } else if(lValue instanceof ValueList) { + for(RValue rValue : ((ValueList) lValue).getList()) { + addValueToEquivalenceClassSet(rValue); + } } - return null; } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java index ead0111ec..a419b0cc1 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegisterUpliftPotentialRegisterAnalysis.java @@ -7,6 +7,8 @@ import dk.camelot64.kickc.fragment.AsmFragmentTemplateSynthesizer; import dk.camelot64.kickc.model.*; import dk.camelot64.kickc.model.statements.*; import dk.camelot64.kickc.model.values.LValue; +import dk.camelot64.kickc.model.values.RValue; +import dk.camelot64.kickc.model.values.ValueList; import dk.camelot64.kickc.model.values.VariableRef; import java.util.*; @@ -39,13 +41,21 @@ public class Pass4RegisterUpliftPotentialRegisterAnalysis extends Pass2Base { return variableRefs; } else if(statement instanceof StatementLValue) { LValue lValue = ((StatementLValue) statement).getlValue(); - if(lValue instanceof VariableRef) { - variableRefs.add((VariableRef) lValue); - } + addAssignedVars(lValue, variableRefs); } return variableRefs; } + private static void addAssignedVars(RValue lValue, LinkedHashSet variableRefs) { + if(lValue instanceof VariableRef) { + variableRefs.add((VariableRef) lValue); + } else if(lValue instanceof ValueList) { + for(RValue rValue : ((ValueList) lValue).getList()) { + addAssignedVars(rValue, variableRefs); + } + } + } + /*** * For each statement - try out all potential register combinations and examine the clobber *