From 665b9adbfb7f54001fbee40ae0d62193c8bd87b5 Mon Sep 17 00:00:00 2001 From: Jesper Gravgaard Date: Mon, 23 Sep 2019 17:53:23 +0200 Subject: [PATCH] Added constant for return value offset on stack. Added padding when return value is larger than parameters. Improved calculations of stack frame sizes. #316 --- .../_stackidxsword_vbuc1=vwsz1.asm | 5 + .../mos6502-common/_stackpushbyte_1.asm | 1 + .../mos6502-common/_stackpushbyte_2.asm | 2 + .../mos6502-common/vwsz1=_stackpullsword_.asm | 4 + .../AsmFragmentInstanceSpecFactory.java | 18 +- .../kickc/model/CallingConventionStack.java | 131 ++++ .../kickc/passes/Pass4CodeGeneration.java | 51 +- .../passes/PassNCallingConventionStack.java | 70 +-- .../dk/camelot64/kickc/test/TestPrograms.java | 5 + .../kc/procedure-callingconvention-stack-5.kc | 15 + .../procedure-callingconvention-stack-0.asm | 3 +- .../procedure-callingconvention-stack-0.log | 10 +- .../procedure-callingconvention-stack-0.sym | 1 + .../procedure-callingconvention-stack-1.asm | 3 +- .../procedure-callingconvention-stack-1.log | 10 +- .../procedure-callingconvention-stack-1.sym | 1 + .../procedure-callingconvention-stack-2.asm | 5 +- .../procedure-callingconvention-stack-2.log | 16 +- .../procedure-callingconvention-stack-2.sym | 1 + .../procedure-callingconvention-stack-3.asm | 5 +- .../procedure-callingconvention-stack-3.log | 16 +- .../procedure-callingconvention-stack-3.sym | 1 + .../procedure-callingconvention-stack-4.asm | 3 +- .../procedure-callingconvention-stack-4.log | 10 +- .../procedure-callingconvention-stack-4.sym | 1 + .../procedure-callingconvention-stack-5.asm | 46 ++ .../procedure-callingconvention-stack-5.cfg | 34 ++ .../procedure-callingconvention-stack-5.log | 565 ++++++++++++++++++ .../procedure-callingconvention-stack-5.sym | 21 + 29 files changed, 939 insertions(+), 115 deletions(-) create mode 100644 src/main/fragment/mos6502-common/_stackidxsword_vbuc1=vwsz1.asm create mode 100644 src/main/fragment/mos6502-common/_stackpushbyte_1.asm create mode 100644 src/main/fragment/mos6502-common/_stackpushbyte_2.asm create mode 100644 src/main/fragment/mos6502-common/vwsz1=_stackpullsword_.asm create mode 100644 src/main/java/dk/camelot64/kickc/model/CallingConventionStack.java create mode 100644 src/test/kc/procedure-callingconvention-stack-5.kc create mode 100644 src/test/ref/procedure-callingconvention-stack-5.asm create mode 100644 src/test/ref/procedure-callingconvention-stack-5.cfg create mode 100644 src/test/ref/procedure-callingconvention-stack-5.log create mode 100644 src/test/ref/procedure-callingconvention-stack-5.sym diff --git a/src/main/fragment/mos6502-common/_stackidxsword_vbuc1=vwsz1.asm b/src/main/fragment/mos6502-common/_stackidxsword_vbuc1=vwsz1.asm new file mode 100644 index 000000000..568ff503a --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackidxsword_vbuc1=vwsz1.asm @@ -0,0 +1,5 @@ +tsx +lda {z1} +sta STACK_BASE+{c1},x +lda {z1}+1 +sta STACK_BASE+{c1}+1,x diff --git a/src/main/fragment/mos6502-common/_stackpushbyte_1.asm b/src/main/fragment/mos6502-common/_stackpushbyte_1.asm new file mode 100644 index 000000000..412cece15 --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpushbyte_1.asm @@ -0,0 +1 @@ +pha \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/_stackpushbyte_2.asm b/src/main/fragment/mos6502-common/_stackpushbyte_2.asm new file mode 100644 index 000000000..2d34ec264 --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpushbyte_2.asm @@ -0,0 +1,2 @@ +pha +pha \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/vwsz1=_stackpullsword_.asm b/src/main/fragment/mos6502-common/vwsz1=_stackpullsword_.asm new file mode 100644 index 000000000..d68c51f7f --- /dev/null +++ b/src/main/fragment/mos6502-common/vwsz1=_stackpullsword_.asm @@ -0,0 +1,4 @@ +pla +sta {z1} +pla +sta {z1}+1 \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java index 4a981431d..efbf17aaa 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java @@ -163,7 +163,7 @@ public class AsmFragmentInstanceSpecFactory { signature.append(((ConstantInteger) rValue2).getValue()); } else if( rValue2 instanceof ConstantInteger && - ((((ConstantInteger) rValue2).getValue()) % 8 == 0) && + ((((ConstantInteger) rValue2).getValue()) % 8 == 0) && operator != null && (operator.getOperator().equals(">>") || operator.getOperator().equals("<<"))) { signature.append(((ConstantInteger) rValue2).getValue()); @@ -313,7 +313,7 @@ public class AsmFragmentInstanceSpecFactory { if(bindPointer.contains("deref")) { // Special handling of nested derefs - add parenthesis! return "_deref_" + "(" + bindPointer + ")"; - } else { + } else { return "_deref_" + bindPointer; } } else if(value instanceof PointerDereferenceIndexed) { @@ -323,7 +323,7 @@ public class AsmFragmentInstanceSpecFactory { if(bindPointer.contains("deref")) { // Special handling of nested derefs - add parenthesis! bindValue.append("(").append(bindPointer).append(")"); - } else { + } else { bindValue.append(bindPointer); } bindValue.append("_derefidx_"); @@ -358,11 +358,17 @@ public class AsmFragmentInstanceSpecFactory { return name; } else if(value instanceof StackIdxValue) { StackIdxValue stackIdxValue = (StackIdxValue) value; - return "_stackidx"+ stackIdxValue.getValueType().getTypeName()+"_"+bind(stackIdxValue.getStackOffset()); + SymbolType type = stackIdxValue.getValueType(); + String typeShortName = Operators.getCastUnary(type).getAsmOperator().replace("_", ""); + return "_stackidx" + typeShortName + "_" + bind(stackIdxValue.getStackOffset()); } else if(value instanceof StackPushValue) { - return "_stackpush"+((StackPushValue) value).getType().getTypeName()+"_"; + SymbolType type = ((StackPushValue) value).getType(); + String typeShortName = Operators.getCastUnary(type).getAsmOperator().replace("_", ""); + return "_stackpush" + typeShortName + "_"; } else if(value instanceof StackPullValue) { - return "_stackpull"+((StackPullValue) value).getType().getTypeName()+"_"; + SymbolType type = ((StackPullValue) value).getType(); + String typeShortName = Operators.getCastUnary(type).getAsmOperator().replace("_", ""); + return "_stackpull" + typeShortName + "_"; } throw new RuntimeException("Binding of value type not supported " + value.toString(program)); } diff --git a/src/main/java/dk/camelot64/kickc/model/CallingConventionStack.java b/src/main/java/dk/camelot64/kickc/model/CallingConventionStack.java new file mode 100644 index 000000000..67fb4546d --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/CallingConventionStack.java @@ -0,0 +1,131 @@ +package dk.camelot64.kickc.model; + +import dk.camelot64.kickc.model.symbols.*; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.values.ConstantInteger; +import dk.camelot64.kickc.model.values.ConstantRef; + +/** + * Utility methods for {@link dk.camelot64.kickc.model.symbols.Procedure.CallingConvension#STACK_CALL} + */ +public class CallingConventionStack { + + + /** + * Get the constant variable containing the (byte) index of the return value on the stack + * + * @param procedure The procedure + * @return The return value stack offset constant + */ + public static ConstantRef getReturnOffsetConstant(Procedure procedure) { + String returnOffsetConstantName = "OFFSET_STACK_RETURN"; + ConstantVar returnOffsetConstant = procedure.getConstant(returnOffsetConstantName); + if(returnOffsetConstant == null) { + // Constant not found - create it + long returnByteOffset = getReturnByteOffset(procedure); + returnOffsetConstant = new ConstantVar(returnOffsetConstantName, procedure, SymbolType.BYTE, new ConstantInteger(returnByteOffset & 0xff, SymbolType.BYTE), Scope.SEGMENT_DATA_DEFAULT); + procedure.add(returnOffsetConstant); + } + return returnOffsetConstant.getRef(); + } + + /** + * Get the constant variable containing the (byte) index of a parameter on the stack + * + * @param procedure The procedure + * @param parameter The parameter + * @return The constant variable + */ + public static ConstantRef getParameterOffsetConstant(Procedure procedure, Variable parameter) { + String paramOffsetConstantName = getParameterOffsetConstantName(parameter.getName()); + ConstantVar paramOffsetConstant = procedure.getConstant(paramOffsetConstantName); + if(paramOffsetConstant == null) { + // Constant not found - create it + long paramByteOffset = getParameterByteOffset(procedure, parameter); + paramOffsetConstant = new ConstantVar(paramOffsetConstantName, procedure, SymbolType.BYTE, new ConstantInteger(paramByteOffset & 0xff, SymbolType.BYTE), Scope.SEGMENT_DATA_DEFAULT); + procedure.add(paramOffsetConstant); + } + return paramOffsetConstant.getRef(); + } + + /** + * Get the name of the constant variable containing the (byte) offset of a specific parameter on the stack + * + * @param parameterName The name of the struct member + * @return The name of the constant + */ + private static String getParameterOffsetConstantName(String parameterName) { + return "OFFSET_STACK_" + parameterName.toUpperCase(); + } + + /** + * Get the number of bytes that needed on the stack to pass parameters/return value to/from a procedure + * + * @param procedure The procedure to find the stack frame size for + * @return The byte size of the stack frame + */ + public static long getStackFrameByteSize(Procedure procedure) { + long byteSize = getParametersByteSize(procedure); + if(procedure.getReturnType() != null) { + int returnBytes = procedure.getReturnType().getSizeBytes(); + if(returnBytes > byteSize) byteSize = returnBytes; + } + return byteSize; + } + + /** + * Get the number of bytes needed on the stack to store the parameters from a procedure + * @param procedure The procedure + * @return The byte size of parameters + */ + public static long getParametersByteSize(Procedure procedure) { + long byteSize = 0; + for(Variable procedureParameter : procedure.getParameters()) { + byteSize += procedureParameter.getType().getSizeBytes(); + } + return byteSize; + } + + /** + * Get the number of bytes that a parameter is offset on the stack + * + * @param parameter The parameter to find offset for + * @return The byte offset of the start of the member data + */ + public static long getParameterByteOffset(Procedure procedure, Variable parameter) { + long byteOffset = 0; + for(Variable procedureParameter : procedure.getParameters()) { + if(parameter.equals(procedureParameter)) { + break; + } else { + // TODO: Consider how passing a struct should handle inline arrays byteOffset += SymbolTypeStruct.getMemberSizeBytes(parameter.getType(), programScope); + byteOffset += procedureParameter.getType().getSizeBytes(); + } + } + return byteOffset; + } + + /** + * Get the number of bytes that the return value is offset on the stack + * + * @param procedure The procedure + * @return The byte offset of the return value + */ + private static long getReturnByteOffset(Procedure procedure) { + return getStackFrameByteSize(procedure) - procedure.getReturnType().getSizeBytes(); + } + + /** + * Get he global STACK_BASE constant. Create it if it does not exist. + * + * @param programScope The program scope + * @return The reference to the global constant + */ + public static ConstantRef getStackBaseConstant(ProgramScope programScope) { + long STACK_BASE_ADDRESS = 0x103L; + ConstantVar stackBase = new ConstantVar("STACK_BASE", programScope, SymbolType.WORD, new ConstantInteger(STACK_BASE_ADDRESS, SymbolType.WORD), Scope.SEGMENT_DATA_DEFAULT); + programScope.add(stackBase); + return stackBase.getRef(); + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 0039343c9..35279cfb4 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -765,6 +765,17 @@ public class Pass4CodeGeneration { ensureEncoding(asm, asmFragmentInstanceSpecFactory); generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); } + // Push additional bytes if needed + long stackFrameByteSize = CallingConventionStack.getStackFrameByteSize(procedure); + long parametersByteSize = CallingConventionStack.getParametersByteSize(procedure); + if(stackFrameByteSize > parametersByteSize) { + // Add padding to the stack to make room for the return value + String pushSignature = "_stackpushbyte_" + (stackFrameByteSize - parametersByteSize); + AsmFragmentInstanceSpec pushFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, pushSignature, new LinkedHashMap<>(), block.getScope()); + asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); + generateAsm(asm, pushFragmentInstanceSpec); + + } asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); } } else if(statement instanceof StatementCallExecute) { @@ -778,21 +789,16 @@ public class Pass4CodeGeneration { StatementCallFinalize call = (StatementCallFinalize) statement; Procedure procedure = getScope().getProcedure(call.getProcedure()); if(Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) { - // Find parameter/return stack size - int parameterBytes = 0; - for(Variable parameter : procedure.getParameters()) { - parameterBytes += parameter.getType().getSizeBytes(); + + long stackFrameByteSize = CallingConventionStack.getStackFrameByteSize(procedure); + long returnByteSize = procedure.getReturnType()==null?0:procedure.getReturnType().getSizeBytes(); + if(stackFrameByteSize > returnByteSize) { + // Clean up the stack + String pullSignature = "_stackpullbyte_" + (stackFrameByteSize-returnByteSize); + AsmFragmentInstanceSpec pullFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, pullSignature, new LinkedHashMap<>(), block.getScope()); + asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); + generateAsm(asm, pullFragmentInstanceSpec); } - int stackSizeBytes = parameterBytes; - if(call.getlValue() != null) { - SymbolType returnType = procedure.getReturnType(); - stackSizeBytes -= returnType.getSizeBytes(); - } - // Clean up the stack - String pullSignature = "_stackpullbyte_" + Integer.toString(stackSizeBytes); - AsmFragmentInstanceSpec pullFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, pullSignature, new LinkedHashMap<>(), block.getScope()); - asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); - generateAsm(asm, pullFragmentInstanceSpec); // Pull result from the stack if(call.getlValue() != null) { @@ -811,28 +817,21 @@ public class Pass4CodeGeneration { procedure = getScope().getProcedure(scope.getFullName()); } - if(procedure!=null && Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) { + if(procedure != null && Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) { StatementReturn returnStatement = (StatementReturn) statement; - if(returnStatement.getValue()!=null) { + if(returnStatement.getValue() != null) { // Store return value on stack SymbolType returnType = procedure.getReturnType(); - // Find parameter/return stack size - int parameterBytes = 0; - for(Variable parameter : procedure.getParameters()) { - parameterBytes += parameter.getType().getSizeBytes(); - } - int returnOffset = parameterBytes - returnType.getSizeBytes(); - // TODO: Put the return stack offset into a named constant (look at PassNCallingConventionStack) - ConstantValue returnValueStackOffset = new ConstantInteger((long)returnOffset, SymbolType.BYTE); - AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(new StackIdxValue(returnValueStackOffset, returnType), returnStatement.getValue(), program, block.getScope()); + 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()); } } - if(procedure==null || procedure.getInterruptType() == null) { + if(procedure == null || procedure.getInterruptType() == null) { asm.addInstruction("rts", AsmAddressingMode.NON, null, false); } else { generateInterruptExit(asm, statement, procedure.getInterruptType()); diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNCallingConventionStack.java b/src/main/java/dk/camelot64/kickc/passes/PassNCallingConventionStack.java index 52a1783c1..f765570a8 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNCallingConventionStack.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNCallingConventionStack.java @@ -1,5 +1,6 @@ package dk.camelot64.kickc.passes; +import dk.camelot64.kickc.model.CallingConventionStack; import dk.camelot64.kickc.model.ControlFlowBlock; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.iterator.ProgramValueIterator; @@ -26,15 +27,27 @@ public class PassNCallingConventionStack extends Pass2SsaOptimization { Map offsetConstants = new HashMap<>(); // Introduce STACK_OFFSET constants + boolean createStackBase = false; for(Procedure procedure : getScope().getAllProcedures(true)) { if(Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) { + // Introduce the parameter offsets for(Variable parameter : procedure.getParameters()) { - ConstantRef parameterOffsetConstant = getParameterOffsetConstant(procedure, parameter, getScope()); + ConstantRef parameterOffsetConstant = CallingConventionStack.getParameterOffsetConstant(procedure, parameter); offsetConstants.put(parameter.getRef(), parameterOffsetConstant); + createStackBase = true; + } + // Introduce the return value offset + if(procedure.getReturnType() != null) { + CallingConventionStack.getReturnOffsetConstant(procedure); + createStackBase = true; } } } + // Add global STACK_BASE constant + if(createStackBase) + CallingConventionStack.getStackBaseConstant(getScope()); + // Transform STACK_CALL calls to call-prepare, call-execute, call-finalize for(ControlFlowBlock block : getGraph().getAllBlocks()) { ListIterator stmtIt = block.getStatements().listIterator(); @@ -49,17 +62,13 @@ public class PassNCallingConventionStack extends Pass2SsaOptimization { stmtIt.add(new StatementCallPrepare(procedureRef, call.getParameters(), call.getSource(), call.getComments())); stmtIt.add(new StatementCallExecute(procedureRef, call.getSource(), call.getComments())); stmtIt.add(new StatementCallFinalize(call.getlValue(), procedureRef, call.getSource(), call.getComments())); - getLog().append("Calling convention " + Procedure.CallingConvension.STACK_CALL + " adding prepare/execute/finalize for "+call.toString(getProgram(), false) ); + getLog().append("Calling convention " + Procedure.CallingConvension.STACK_CALL + " adding prepare/execute/finalize for " + call.toString(getProgram(), false)); } } } } if(offsetConstants.size() > 0) { - // Add global STACK_BASE constant - long STACK_BASE = 0x103L; - getScope().add(new ConstantVar("STACK_BASE", getScope(), SymbolType.WORD, new ConstantInteger(STACK_BASE, SymbolType.WORD), Scope.SEGMENT_DATA_DEFAULT)); - // Convert ParamValue to StackIdxValue ProgramValueIterator.execute(getGraph(), (programValue, currentStmt, stmtIt, currentBlock) -> { if(programValue.get() instanceof ParamValue) { @@ -78,53 +87,4 @@ public class PassNCallingConventionStack extends Pass2SsaOptimization { return false; } - /** - * Get the constant variable containing the (byte) index of a parameter on the stack - * - * @param procedure The procedure - * @param procedure The parameter - * @param programScope The program scope (used for finding/adding the constant). - * @return The constant variable - */ - public static ConstantRef getParameterOffsetConstant(Procedure procedure, Variable parameter, ProgramScope programScope) { - String paramOffsetConstantName = getParameterOffsetConstantName(parameter.getName()); - ConstantVar paramOffsetConstant = procedure.getConstant(paramOffsetConstantName); - if(paramOffsetConstant == null) { - // Constant not found - create it - long paramByteOffset = getParameterByteOffset(procedure, parameter, programScope); - paramOffsetConstant = new ConstantVar(paramOffsetConstantName, procedure, SymbolType.BYTE, new ConstantInteger(paramByteOffset & 0xff, SymbolType.BYTE), Scope.SEGMENT_DATA_DEFAULT); - procedure.add(paramOffsetConstant); - } - return paramOffsetConstant.getRef(); - } - - /** - * Get the name of the constant variable containing the (byte) offset of a specific parameter on the stack - * - * @param parameterName The name of the struct member - * @return The name of the constant - */ - private static String getParameterOffsetConstantName(String parameterName) { - return "OFFSET_STACK_" + parameterName.toUpperCase(); - } - - /** - * Get the number of bytes that a parameter is offset on the stack - * - * @param parameter The parameter to find offset for - * @return The byte offset of the start of the member data - */ - public static long getParameterByteOffset(Procedure procedure, Variable parameter, ProgramScope programScope) { - long byteOffset = 0; - for(Variable procedureParameter : procedure.getParameters()) { - if(parameter.equals(procedureParameter)) { - break; - } else { - // TODO: Consider hos passing a struct should handle inline arrays byteOffset += SymbolTypeStruct.getMemberSizeBytes(parameter.getType(), programScope); - byteOffset += parameter.getType().getSizeBytes(); - } - } - return byteOffset; - } - } diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 9ae8ceaf7..2d5b8b773 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -35,6 +35,11 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testProcedureCallingConventionStack5() throws IOException, URISyntaxException { + compileAndCompare("procedure-callingconvention-stack-5"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence()); + } + @Test public void testProcedureCallingConventionStack4() throws IOException, URISyntaxException { compileAndCompare("procedure-callingconvention-stack-4"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence()); diff --git a/src/test/kc/procedure-callingconvention-stack-5.kc b/src/test/kc/procedure-callingconvention-stack-5.kc new file mode 100644 index 000000000..e8e960934 --- /dev/null +++ b/src/test/kc/procedure-callingconvention-stack-5.kc @@ -0,0 +1,15 @@ +// Test a procedure with calling convention stack +// Return value larger than parameter + +const int* SCREEN = 0x0400; + +void main(void) { + SCREEN[0] = next(); + SCREEN[1] = next(); +} + +int current = 48; + +int __stackcall next() { + return current++; +} \ No newline at end of file diff --git a/src/test/ref/procedure-callingconvention-stack-0.asm b/src/test/ref/procedure-callingconvention-stack-0.asm index 3293ed5f3..9babbaf46 100644 --- a/src/test/ref/procedure-callingconvention-stack-0.asm +++ b/src/test/ref/procedure-callingconvention-stack-0.asm @@ -19,6 +19,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 2 tsx lda STACK_BASE+OFFSET_STACK_A,x @@ -28,6 +29,6 @@ plus: { clc adc.z a tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } diff --git a/src/test/ref/procedure-callingconvention-stack-0.log b/src/test/ref/procedure-callingconvention-stack-0.log index 679438fb2..bfe4198c7 100644 --- a/src/test/ref/procedure-callingconvention-stack-0.log +++ b/src/test/ref/procedure-callingconvention-stack-0.log @@ -214,6 +214,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 3 .label b = 4 .label return = 5 @@ -237,7 +238,7 @@ plus: { // [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuz1 lda.z return tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } // File Data @@ -330,6 +331,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 2 // [10] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1 tsx @@ -347,7 +349,7 @@ plus: { // [13] return (byte) plus::return#0 // [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } // File Data @@ -387,6 +389,7 @@ __stackcall (byte()) plus((byte) plus::a , (byte) plus::b) (label) plus::@return (const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0 (const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 1 +(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1 (byte) plus::a (byte) plus::a#0 a zp ZP_BYTE:2 2.0 (byte) plus::b @@ -450,6 +453,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 2 // [10] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1 tsx @@ -467,7 +471,7 @@ plus: { // [13] return (byte) plus::return#0 // [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } // File Data diff --git a/src/test/ref/procedure-callingconvention-stack-0.sym b/src/test/ref/procedure-callingconvention-stack-0.sym index 3f16225e7..ab9734640 100644 --- a/src/test/ref/procedure-callingconvention-stack-0.sym +++ b/src/test/ref/procedure-callingconvention-stack-0.sym @@ -11,6 +11,7 @@ __stackcall (byte()) plus((byte) plus::a , (byte) plus::b) (label) plus::@return (const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0 (const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 1 +(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1 (byte) plus::a (byte) plus::a#0 a zp ZP_BYTE:2 2.0 (byte) plus::b diff --git a/src/test/ref/procedure-callingconvention-stack-1.asm b/src/test/ref/procedure-callingconvention-stack-1.asm index 3293ed5f3..9babbaf46 100644 --- a/src/test/ref/procedure-callingconvention-stack-1.asm +++ b/src/test/ref/procedure-callingconvention-stack-1.asm @@ -19,6 +19,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 2 tsx lda STACK_BASE+OFFSET_STACK_A,x @@ -28,6 +29,6 @@ plus: { clc adc.z a tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } diff --git a/src/test/ref/procedure-callingconvention-stack-1.log b/src/test/ref/procedure-callingconvention-stack-1.log index 679438fb2..bfe4198c7 100644 --- a/src/test/ref/procedure-callingconvention-stack-1.log +++ b/src/test/ref/procedure-callingconvention-stack-1.log @@ -214,6 +214,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 3 .label b = 4 .label return = 5 @@ -237,7 +238,7 @@ plus: { // [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuz1 lda.z return tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } // File Data @@ -330,6 +331,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 2 // [10] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1 tsx @@ -347,7 +349,7 @@ plus: { // [13] return (byte) plus::return#0 // [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } // File Data @@ -387,6 +389,7 @@ __stackcall (byte()) plus((byte) plus::a , (byte) plus::b) (label) plus::@return (const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0 (const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 1 +(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1 (byte) plus::a (byte) plus::a#0 a zp ZP_BYTE:2 2.0 (byte) plus::b @@ -450,6 +453,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 2 // [10] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1 tsx @@ -467,7 +471,7 @@ plus: { // [13] return (byte) plus::return#0 // [13] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } // File Data diff --git a/src/test/ref/procedure-callingconvention-stack-1.sym b/src/test/ref/procedure-callingconvention-stack-1.sym index 3f16225e7..ab9734640 100644 --- a/src/test/ref/procedure-callingconvention-stack-1.sym +++ b/src/test/ref/procedure-callingconvention-stack-1.sym @@ -11,6 +11,7 @@ __stackcall (byte()) plus((byte) plus::a , (byte) plus::b) (label) plus::@return (const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0 (const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 1 +(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1 (byte) plus::a (byte) plus::a#0 a zp ZP_BYTE:2 2.0 (byte) plus::b diff --git a/src/test/ref/procedure-callingconvention-stack-2.asm b/src/test/ref/procedure-callingconvention-stack-2.asm index bc3a11172..b678c4d23 100644 --- a/src/test/ref/procedure-callingconvention-stack-2.asm +++ b/src/test/ref/procedure-callingconvention-stack-2.asm @@ -31,6 +31,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 2 + .const OFFSET_STACK_RETURN = 2 .label a = 2 .label b = 4 .label return = 2 @@ -53,8 +54,8 @@ plus: { sta.z return+1 tsx lda.z return - sta STACK_BASE+2,x + sta STACK_BASE+OFFSET_STACK_RETURN,x lda.z return+1 - sta STACK_BASE+2+1,x + sta STACK_BASE+OFFSET_STACK_RETURN+1,x rts } diff --git a/src/test/ref/procedure-callingconvention-stack-2.log b/src/test/ref/procedure-callingconvention-stack-2.log index b4ec2f367..48ec70fa7 100644 --- a/src/test/ref/procedure-callingconvention-stack-2.log +++ b/src/test/ref/procedure-callingconvention-stack-2.log @@ -240,6 +240,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 2 + .const OFFSET_STACK_RETURN = 2 .label a = 4 .label b = 6 .label return = 8 @@ -270,9 +271,9 @@ plus: { // [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1 tsx lda.z return - sta STACK_BASE+2,x + sta STACK_BASE+OFFSET_STACK_RETURN,x lda.z return+1 - sta STACK_BASE+2+1,x + sta STACK_BASE+OFFSET_STACK_RETURN+1,x rts } // File Data @@ -371,6 +372,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 2 + .const OFFSET_STACK_RETURN = 2 .label a = 2 .label b = 4 .label return = 2 @@ -401,9 +403,9 @@ plus: { // [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1 tsx lda.z return - sta STACK_BASE+2,x + sta STACK_BASE+OFFSET_STACK_RETURN,x lda.z return+1 - sta STACK_BASE+2+1,x + sta STACK_BASE+OFFSET_STACK_RETURN+1,x rts } // File Data @@ -443,6 +445,7 @@ __stackcall (word()) plus((word) plus::a , (word) plus::b) (label) plus::@return (const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0 (const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 2 +(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 2 (word) plus::a (word) plus::a#0 a zp ZP_WORD:2 2.0 (word) plus::b @@ -516,6 +519,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 2 + .const OFFSET_STACK_RETURN = 2 .label a = 2 .label b = 4 .label return = 2 @@ -546,9 +550,9 @@ plus: { // [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1 tsx lda.z return - sta STACK_BASE+2,x + sta STACK_BASE+OFFSET_STACK_RETURN,x lda.z return+1 - sta STACK_BASE+2+1,x + sta STACK_BASE+OFFSET_STACK_RETURN+1,x rts } // File Data diff --git a/src/test/ref/procedure-callingconvention-stack-2.sym b/src/test/ref/procedure-callingconvention-stack-2.sym index 0733ac836..86980b099 100644 --- a/src/test/ref/procedure-callingconvention-stack-2.sym +++ b/src/test/ref/procedure-callingconvention-stack-2.sym @@ -11,6 +11,7 @@ __stackcall (word()) plus((word) plus::a , (word) plus::b) (label) plus::@return (const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0 (const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 2 +(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 2 (word) plus::a (word) plus::a#0 a zp ZP_WORD:2 2.0 (word) plus::b diff --git a/src/test/ref/procedure-callingconvention-stack-3.asm b/src/test/ref/procedure-callingconvention-stack-3.asm index 8c897ff0d..a7b759ed2 100644 --- a/src/test/ref/procedure-callingconvention-stack-3.asm +++ b/src/test/ref/procedure-callingconvention-stack-3.asm @@ -34,6 +34,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 2 + .const OFFSET_STACK_RETURN = 2 .label a = 2 .label b = 4 .label return = 2 @@ -56,8 +57,8 @@ plus: { sta.z return+1 tsx lda.z return - sta STACK_BASE+2,x + sta STACK_BASE+OFFSET_STACK_RETURN,x lda.z return+1 - sta STACK_BASE+2+1,x + sta STACK_BASE+OFFSET_STACK_RETURN+1,x rts } diff --git a/src/test/ref/procedure-callingconvention-stack-3.log b/src/test/ref/procedure-callingconvention-stack-3.log index 871d96339..177b55ca8 100644 --- a/src/test/ref/procedure-callingconvention-stack-3.log +++ b/src/test/ref/procedure-callingconvention-stack-3.log @@ -240,6 +240,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 2 + .const OFFSET_STACK_RETURN = 2 .label a = 4 .label b = 6 .label return = 8 @@ -270,9 +271,9 @@ plus: { // [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1 tsx lda.z return - sta STACK_BASE+2,x + sta STACK_BASE+OFFSET_STACK_RETURN,x lda.z return+1 - sta STACK_BASE+2+1,x + sta STACK_BASE+OFFSET_STACK_RETURN+1,x rts } // File Data @@ -374,6 +375,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 2 + .const OFFSET_STACK_RETURN = 2 .label a = 2 .label b = 4 .label return = 2 @@ -404,9 +406,9 @@ plus: { // [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1 tsx lda.z return - sta STACK_BASE+2,x + sta STACK_BASE+OFFSET_STACK_RETURN,x lda.z return+1 - sta STACK_BASE+2+1,x + sta STACK_BASE+OFFSET_STACK_RETURN+1,x rts } // File Data @@ -446,6 +448,7 @@ __stackcall (word()) plus((word) plus::a , (word) plus::b) (label) plus::@return (const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0 (const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 2 +(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 2 (word) plus::a (word) plus::a#0 a zp ZP_WORD:2 2.0 (word) plus::b @@ -522,6 +525,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 2 + .const OFFSET_STACK_RETURN = 2 .label a = 2 .label b = 4 .label return = 2 @@ -552,9 +556,9 @@ plus: { // [13] return (word) plus::return#0 -- _stackidxword_vbuc1=vwuz1 tsx lda.z return - sta STACK_BASE+2,x + sta STACK_BASE+OFFSET_STACK_RETURN,x lda.z return+1 - sta STACK_BASE+2+1,x + sta STACK_BASE+OFFSET_STACK_RETURN+1,x rts } // File Data diff --git a/src/test/ref/procedure-callingconvention-stack-3.sym b/src/test/ref/procedure-callingconvention-stack-3.sym index 0733ac836..86980b099 100644 --- a/src/test/ref/procedure-callingconvention-stack-3.sym +++ b/src/test/ref/procedure-callingconvention-stack-3.sym @@ -11,6 +11,7 @@ __stackcall (word()) plus((word) plus::a , (word) plus::b) (label) plus::@return (const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0 (const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 2 +(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 2 (word) plus::a (word) plus::a#0 a zp ZP_WORD:2 2.0 (word) plus::b diff --git a/src/test/ref/procedure-callingconvention-stack-4.asm b/src/test/ref/procedure-callingconvention-stack-4.asm index f5759ae04..fee9dd65a 100644 --- a/src/test/ref/procedure-callingconvention-stack-4.asm +++ b/src/test/ref/procedure-callingconvention-stack-4.asm @@ -31,6 +31,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 2 tsx lda STACK_BASE+OFFSET_STACK_A,x @@ -40,6 +41,6 @@ plus: { clc adc.z a tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } diff --git a/src/test/ref/procedure-callingconvention-stack-4.log b/src/test/ref/procedure-callingconvention-stack-4.log index 6c9854396..999fdb044 100644 --- a/src/test/ref/procedure-callingconvention-stack-4.log +++ b/src/test/ref/procedure-callingconvention-stack-4.log @@ -349,6 +349,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 6 .label b = 7 .label return = 8 @@ -372,7 +373,7 @@ plus: { // [18] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuz1 lda.z return tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } // File Data @@ -498,6 +499,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 2 // [15] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1 tsx @@ -515,7 +517,7 @@ plus: { // [18] return (byte) plus::return#0 // [18] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } // File Data @@ -570,6 +572,7 @@ __stackcall (byte()) plus((byte) plus::a , (byte) plus::b) (label) plus::@return (const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0 (const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 1 +(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1 (byte) plus::a (byte) plus::a#0 a zp ZP_BYTE:2 2.0 (byte) plus::b @@ -660,6 +663,7 @@ main: { plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 + .const OFFSET_STACK_RETURN = 1 .label a = 2 // [15] (byte) plus::a#0 ← stackidx(byte,(const byte) plus::OFFSET_STACK_A) -- vbuz1=_stackidxbyte_vbuc1 tsx @@ -677,7 +681,7 @@ plus: { // [18] return (byte) plus::return#0 // [18] return (byte) plus::return#0 -- _stackidxbyte_vbuc1=vbuaa tsx - sta STACK_BASE+1,x + sta STACK_BASE+OFFSET_STACK_RETURN,x rts } // File Data diff --git a/src/test/ref/procedure-callingconvention-stack-4.sym b/src/test/ref/procedure-callingconvention-stack-4.sym index 05f95ff24..352603d3d 100644 --- a/src/test/ref/procedure-callingconvention-stack-4.sym +++ b/src/test/ref/procedure-callingconvention-stack-4.sym @@ -20,6 +20,7 @@ __stackcall (byte()) plus((byte) plus::a , (byte) plus::b) (label) plus::@return (const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0 (const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 1 +(const byte) plus::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 1 (byte) plus::a (byte) plus::a#0 a zp ZP_BYTE:2 2.0 (byte) plus::b diff --git a/src/test/ref/procedure-callingconvention-stack-5.asm b/src/test/ref/procedure-callingconvention-stack-5.asm new file mode 100644 index 000000000..aa1d8ce7c --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-5.asm @@ -0,0 +1,46 @@ +// Test a procedure with calling convention stack +// Return value larger than parameter +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const SIZEOF_SIGNED_WORD = 2 + .label SCREEN = $400 + .const STACK_BASE = $103 + .label current = 2 +main: { + .label _0 = 2 + .label _1 = 4 + pha + pha + jsr next + pla + sta.z _0 + pla + sta.z _0+1 + lda.z _0 + sta SCREEN + lda.z _0+1 + sta SCREEN+1 + pha + pha + jsr next + pla + sta.z _1 + pla + sta.z _1+1 + lda.z _1 + sta SCREEN+1*SIZEOF_SIGNED_WORD + lda.z _1+1 + sta SCREEN+1*SIZEOF_SIGNED_WORD+1 + rts +} +next: { + .const OFFSET_STACK_RETURN = 0 + .label return = 2 + tsx + lda.z return + sta STACK_BASE+OFFSET_STACK_RETURN,x + lda.z return+1 + sta STACK_BASE+OFFSET_STACK_RETURN+1,x + rts +} diff --git a/src/test/ref/procedure-callingconvention-stack-5.cfg b/src/test/ref/procedure-callingconvention-stack-5.cfg new file mode 100644 index 000000000..cc653a1c2 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-5.cfg @@ -0,0 +1,34 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] phi() + [5] callprepare next + [6] callexecute next + [7] (signed word~) main::$0 ← callfinalize next + [8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0 + [9] callprepare next + [10] callexecute next + [11] (signed word~) main::$1 ← callfinalize next + [12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1 + to:main::@return +main::@return: scope:[main] from main + [13] return + to:@return + +__stackcall (signed word()) next() +next: scope:[next] from + [14] (signed word) current#5 ← phi( ) + [15] (signed word) next::return#0 ← (signed word) current#5 + to:next::@return +next::@return: scope:[next] from next + [16] return (signed word) next::return#0 + to:@return diff --git a/src/test/ref/procedure-callingconvention-stack-5.log b/src/test/ref/procedure-callingconvention-stack-5.log new file mode 100644 index 000000000..92e38b658 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-5.log @@ -0,0 +1,565 @@ +Fixing pointer array-indexing *((signed word*) SCREEN + (number) 0) +Fixing pointer array-indexing *((signed word*) SCREEN + (number) 1) +Culled Empty Block (label) next::@1 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (signed word*) SCREEN#0 ← ((signed word*)) (number) $400 + to:@1 + +(void()) main() +main: scope:[main] from @2 + (signed word) current#7 ← phi( @2/(signed word) current#8 ) + (signed word~) main::$0 ← call next + (number~) main::$2 ← (number) 0 * (const byte) SIZEOF_SIGNED_WORD + *((signed word*) SCREEN#0 + (number~) main::$2) ← (signed word~) main::$0 + (signed word~) main::$1 ← call next + (number~) main::$3 ← (number) 1 * (const byte) SIZEOF_SIGNED_WORD + *((signed word*) SCREEN#0 + (number~) main::$3) ← (signed word~) main::$1 + to:main::@return +main::@return: scope:[main] from main + (signed word) current#4 ← phi( main/(signed word) current#7 ) + (signed word) current#0 ← (signed word) current#4 + return + to:@return +@1: scope:[] from @begin + (signed word) current#1 ← (number) $30 + to:@2 + +__stackcall (signed word()) next() +next: scope:[next] from + (signed word) current#5 ← phi( ) + (signed word) next::return#0 ← (signed word) current#5 + (signed word) current#2 ← ++ (signed word) current#5 + to:next::@return +next::@return: scope:[next] from next + (signed word) next::return#1 ← phi( next/(signed word) next::return#0 ) + return (signed word) next::return#1 + to:@return +@2: scope:[] from @1 + (signed word) current#8 ← phi( @1/(signed word) current#1 ) + call main + to:@3 +@3: scope:[] from @2 + (signed word) current#6 ← phi( @2/(signed word) current#0 ) + (signed word) current#3 ← (signed word) current#6 + to:@end +@end: scope:[] from @3 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @3 +(label) @begin +(label) @end +(signed word*) SCREEN +(signed word*) SCREEN#0 +(const byte) SIZEOF_SIGNED_WORD = (byte) 2 +(signed word) current +(signed word) current#0 +(signed word) current#1 +(signed word) current#2 +(signed word) current#3 +(signed word) current#4 +(signed word) current#5 +(signed word) current#6 +(signed word) current#7 +(signed word) current#8 +(void()) main() +(signed word~) main::$0 +(signed word~) main::$1 +(number~) main::$2 +(number~) main::$3 +(label) main::@return +__stackcall (signed word()) next() +(label) next::@return +(signed word) next::return +(signed word) next::return#0 +(signed word) next::return#1 + +Adding number conversion cast (unumber) 0 in (number~) main::$2 ← (number) 0 * (const byte) SIZEOF_SIGNED_WORD +Adding number conversion cast (unumber) main::$2 in (number~) main::$2 ← (unumber)(number) 0 * (const byte) SIZEOF_SIGNED_WORD +Adding number conversion cast (unumber) 1 in (number~) main::$3 ← (number) 1 * (const byte) SIZEOF_SIGNED_WORD +Adding number conversion cast (unumber) main::$3 in (number~) main::$3 ← (unumber)(number) 1 * (const byte) SIZEOF_SIGNED_WORD +Adding number conversion cast (snumber) $30 in (signed word) current#1 ← (number) $30 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (signed word*) SCREEN#0 ← (signed word*)(number) $400 +Inlining cast (signed word) current#1 ← (snumber)(number) $30 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (signed word*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Simplifying constant integer cast $30 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Finalized signed number type (signed byte) $30 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inferred type updated to byte in (unumber~) main::$2 ← (byte) 0 * (const byte) SIZEOF_SIGNED_WORD +Inferred type updated to byte in (unumber~) main::$3 ← (byte) 1 * (const byte) SIZEOF_SIGNED_WORD +Alias (signed word) current#0 = (signed word) current#4 (signed word) current#7 +Alias (signed word) next::return#0 = (signed word) next::return#1 +Alias (signed word) current#1 = (signed word) current#8 +Alias (signed word) current#3 = (signed word) current#6 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (signed word) current#0 (signed word) current#1 +Identical Phi Values (signed word) current#3 (signed word) current#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant right-side identified [3] (byte~) main::$2 ← (byte) 0 * (const byte) SIZEOF_SIGNED_WORD +Constant right-side identified [6] (byte~) main::$3 ← (byte) 1 * (const byte) SIZEOF_SIGNED_WORD +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const signed word*) SCREEN#0 = (signed word*) 1024 +Constant (const byte) main::$2 = 0*SIZEOF_SIGNED_WORD +Constant (const byte) main::$3 = 1*SIZEOF_SIGNED_WORD +Constant (const signed word) current#1 = $30 +Successful SSA optimization Pass2ConstantIdentification +Simplifying constant evaluating to zero (byte) 0*(const byte) SIZEOF_SIGNED_WORD in +Successful SSA optimization PassNSimplifyConstantZero +Simplifying expression containing zero SCREEN#0 in [4] *((const signed word*) SCREEN#0 + (const byte) main::$2) ← (signed word~) main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable (signed word) current#2 and assignment [7] (signed word) current#2 ← ++ (signed word) current#5 +Eliminating unused constant (const byte) main::$2 +Eliminating unused constant (const signed word) current#1 +Successful SSA optimization PassNEliminateUnusedVars +Constant inlined main::$3 = (byte) 1*(const byte) SIZEOF_SIGNED_WORD +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *(SCREEN#0+1*SIZEOF_SIGNED_WORD) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @3 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +CALL GRAPH +Calls in [] to main:3 +Calls in [main] to next:7 next:9 + +Created 1 initial phi equivalence classes +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) @1 +Culled Empty Block (label) @3 +Renumbering block @2 to @1 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Calling convention STACK_CALL adding prepare/execute/finalize for [5] (signed word~) main::$0 ← call next +Calling convention STACK_CALL adding prepare/execute/finalize for [7] (signed word~) main::$1 ← call next + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] phi() + [5] callprepare next + [6] callexecute next + [7] (signed word~) main::$0 ← callfinalize next + [8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0 + [9] callprepare next + [10] callexecute next + [11] (signed word~) main::$1 ← callfinalize next + [12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1 + to:main::@return +main::@return: scope:[main] from main + [13] return + to:@return + +__stackcall (signed word()) next() +next: scope:[next] from + [14] (signed word) current#5 ← phi( ) + [15] (signed word) next::return#0 ← (signed word) current#5 + to:next::@return +next::@return: scope:[next] from next + [16] return (signed word) next::return#0 + to:@return + + +VARIABLE REGISTER WEIGHTS +(signed word*) SCREEN +(signed word) current +(signed word) current#5 2.0 +(void()) main() +(signed word~) main::$0 2.0 +(signed word~) main::$1 2.0 +__stackcall (signed word()) next() +(signed word) next::return +(signed word) next::return#0 2.0 + +Initial phi equivalence classes +[ current#5 ] +Added variable main::$0 to zero page equivalence class [ main::$0 ] +Added variable main::$1 to zero page equivalence class [ main::$1 ] +Added variable next::return#0 to zero page equivalence class [ next::return#0 ] +Complete equivalence classes +[ current#5 ] +[ main::$0 ] +[ main::$1 ] +[ next::return#0 ] +Allocated zp ZP_WORD:2 [ current#5 ] +Allocated zp ZP_WORD:4 [ main::$0 ] +Allocated zp ZP_WORD:6 [ main::$1 ] +Allocated zp ZP_WORD:8 [ next::return#0 ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Test a procedure with calling convention stack +// Return value larger than parameter + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_SIGNED_WORD = 2 + .label SCREEN = $400 + .const STACK_BASE = $103 + .label current = 2 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label _0 = 4 + .label _1 = 6 + // [5] callprepare next + // [5] callprepare next -- _stackpushbyte_2 + pha + pha + // [5] callprepare next + // [6] callexecute next -- jsr + jsr next + // [7] (signed word~) main::$0 ← callfinalize next + // [7] (signed word~) main::$0 ← callfinalize next -- vwsz1=_stackpullsword_ + pla + sta.z _0 + pla + sta.z _0+1 + // [8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0 -- _deref_pwsc1=vwsz1 + lda.z _0 + sta SCREEN + lda.z _0+1 + sta SCREEN+1 + // [9] callprepare next + // [9] callprepare next -- _stackpushbyte_2 + pha + pha + // [9] callprepare next + // [10] callexecute next -- jsr + jsr next + // [11] (signed word~) main::$1 ← callfinalize next + // [11] (signed word~) main::$1 ← callfinalize next -- vwsz1=_stackpullsword_ + pla + sta.z _1 + pla + sta.z _1+1 + // [12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1 -- _deref_pwsc1=vwsz1 + lda.z _1 + sta SCREEN+1*SIZEOF_SIGNED_WORD + lda.z _1+1 + sta SCREEN+1*SIZEOF_SIGNED_WORD+1 + jmp breturn + // main::@return + breturn: + // [13] return + rts +} + // next +next: { + .const OFFSET_STACK_RETURN = 0 + .label return = 8 + // [15] (signed word) next::return#0 ← (signed word) current#5 -- vwsz1=vwsz2 + lda.z current + sta.z return + lda.z current+1 + sta.z return+1 + jmp breturn + // next::@return + breturn: + // [16] return (signed word) next::return#0 + // [16] return (signed word) next::return#0 -- _stackidxsword_vbuc1=vwsz1 + tsx + lda.z return + sta STACK_BASE+OFFSET_STACK_RETURN,x + lda.z return+1 + sta STACK_BASE+OFFSET_STACK_RETURN+1,x + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [7] (signed word~) main::$0 ← callfinalize next [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a +Statement [8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [11] (signed word~) main::$1 ← callfinalize next [ main::$1 ] ( main:2 [ main::$1 ] ) always clobbers reg byte a +Statement [12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [15] (signed word) next::return#0 ← (signed word) current#5 [ next::return#0 ] ( main:2::next:6 [ next::return#0 ] main:2::next:10 [ next::return#0 ] ) always clobbers reg byte a +Statement [16] return (signed word) next::return#0 [ ] ( main:2::next:6 [ ] main:2::next:10 [ ] ) always clobbers reg byte a reg byte x +Potential registers zp ZP_WORD:2 [ current#5 ] : zp ZP_WORD:2 , +Potential registers zp ZP_WORD:4 [ main::$0 ] : zp ZP_WORD:4 , +Potential registers zp ZP_WORD:6 [ main::$1 ] : zp ZP_WORD:6 , +Potential registers zp ZP_WORD:8 [ next::return#0 ] : zp ZP_WORD:8 , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 2: zp ZP_WORD:4 [ main::$0 ] 2: zp ZP_WORD:6 [ main::$1 ] +Uplift Scope [next] 2: zp ZP_WORD:8 [ next::return#0 ] +Uplift Scope [] 2: zp ZP_WORD:2 [ current#5 ] + +Uplifting [main] best 140 combination zp ZP_WORD:4 [ main::$0 ] zp ZP_WORD:6 [ main::$1 ] +Uplifting [next] best 140 combination zp ZP_WORD:8 [ next::return#0 ] +Uplifting [] best 140 combination zp ZP_WORD:2 [ current#5 ] +Coalescing zero page register [ zp ZP_WORD:2 [ current#5 ] ] with [ zp ZP_WORD:8 [ next::return#0 ] ] - score: 1 +Coalescing zero page register [ zp ZP_WORD:4 [ main::$0 ] ] with [ zp ZP_WORD:2 [ current#5 next::return#0 ] ] +Allocated (was zp ZP_WORD:4) zp ZP_WORD:2 [ main::$0 current#5 next::return#0 ] +Allocated (was zp ZP_WORD:6) zp ZP_WORD:4 [ main::$1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test a procedure with calling convention stack +// Return value larger than parameter + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_SIGNED_WORD = 2 + .label SCREEN = $400 + .const STACK_BASE = $103 + .label current = 2 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label _0 = 2 + .label _1 = 4 + // [5] callprepare next + // [5] callprepare next -- _stackpushbyte_2 + pha + pha + // [5] callprepare next + // [6] callexecute next -- jsr + jsr next + // [7] (signed word~) main::$0 ← callfinalize next + // [7] (signed word~) main::$0 ← callfinalize next -- vwsz1=_stackpullsword_ + pla + sta.z _0 + pla + sta.z _0+1 + // [8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0 -- _deref_pwsc1=vwsz1 + lda.z _0 + sta SCREEN + lda.z _0+1 + sta SCREEN+1 + // [9] callprepare next + // [9] callprepare next -- _stackpushbyte_2 + pha + pha + // [9] callprepare next + // [10] callexecute next -- jsr + jsr next + // [11] (signed word~) main::$1 ← callfinalize next + // [11] (signed word~) main::$1 ← callfinalize next -- vwsz1=_stackpullsword_ + pla + sta.z _1 + pla + sta.z _1+1 + // [12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1 -- _deref_pwsc1=vwsz1 + lda.z _1 + sta SCREEN+1*SIZEOF_SIGNED_WORD + lda.z _1+1 + sta SCREEN+1*SIZEOF_SIGNED_WORD+1 + jmp breturn + // main::@return + breturn: + // [13] return + rts +} + // next +next: { + .const OFFSET_STACK_RETURN = 0 + .label return = 2 + // [15] (signed word) next::return#0 ← (signed word) current#5 + jmp breturn + // next::@return + breturn: + // [16] return (signed word) next::return#0 + // [16] return (signed word) next::return#0 -- _stackidxsword_vbuc1=vwsz1 + tsx + lda.z return + sta STACK_BASE+OFFSET_STACK_RETURN,x + lda.z return+1 + sta STACK_BASE+OFFSET_STACK_RETURN+1,x + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp breturn +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction breturn: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(signed word*) SCREEN +(const signed word*) SCREEN#0 SCREEN = (signed word*) 1024 +(const byte) SIZEOF_SIGNED_WORD SIZEOF_SIGNED_WORD = (byte) 2 +(const word) STACK_BASE STACK_BASE = (word) $103 +(signed word) current +(signed word) current#5 current zp ZP_WORD:2 2.0 +(void()) main() +(signed word~) main::$0 $0 zp ZP_WORD:2 2.0 +(signed word~) main::$1 $1 zp ZP_WORD:4 2.0 +(label) main::@return +__stackcall (signed word()) next() +(label) next::@return +(const byte) next::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 0 +(signed word) next::return +(signed word) next::return#0 return zp ZP_WORD:2 2.0 + +zp ZP_WORD:2 [ main::$0 current#5 next::return#0 ] +zp ZP_WORD:4 [ main::$1 ] + + +FINAL ASSEMBLER +Score: 110 + + // File Comments +// Test a procedure with calling convention stack +// Return value larger than parameter + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_SIGNED_WORD = 2 + .label SCREEN = $400 + .const STACK_BASE = $103 + .label current = 2 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + .label _0 = 2 + .label _1 = 4 + // next() + // [5] callprepare next + // [5] callprepare next -- _stackpushbyte_2 + pha + pha + // [5] callprepare next + // [6] callexecute next -- jsr + jsr next + // [7] (signed word~) main::$0 ← callfinalize next + // [7] (signed word~) main::$0 ← callfinalize next -- vwsz1=_stackpullsword_ + pla + sta.z _0 + pla + sta.z _0+1 + // SCREEN[0] = next() + // [8] *((const signed word*) SCREEN#0) ← (signed word~) main::$0 -- _deref_pwsc1=vwsz1 + lda.z _0 + sta SCREEN + lda.z _0+1 + sta SCREEN+1 + // next() + // [9] callprepare next + // [9] callprepare next -- _stackpushbyte_2 + pha + pha + // [9] callprepare next + // [10] callexecute next -- jsr + jsr next + // [11] (signed word~) main::$1 ← callfinalize next + // [11] (signed word~) main::$1 ← callfinalize next -- vwsz1=_stackpullsword_ + pla + sta.z _1 + pla + sta.z _1+1 + // SCREEN[1] = next() + // [12] *((const signed word*) SCREEN#0+(byte) 1*(const byte) SIZEOF_SIGNED_WORD) ← (signed word~) main::$1 -- _deref_pwsc1=vwsz1 + lda.z _1 + sta SCREEN+1*SIZEOF_SIGNED_WORD + lda.z _1+1 + sta SCREEN+1*SIZEOF_SIGNED_WORD+1 + // main::@return + // } + // [13] return + rts +} + // next +next: { + .const OFFSET_STACK_RETURN = 0 + .label return = 2 + // return current++; + // [15] (signed word) next::return#0 ← (signed word) current#5 + // next::@return + // } + // [16] return (signed word) next::return#0 + // [16] return (signed word) next::return#0 -- _stackidxsword_vbuc1=vwsz1 + tsx + lda.z return + sta STACK_BASE+OFFSET_STACK_RETURN,x + lda.z return+1 + sta STACK_BASE+OFFSET_STACK_RETURN+1,x + rts +} + // File Data + diff --git a/src/test/ref/procedure-callingconvention-stack-5.sym b/src/test/ref/procedure-callingconvention-stack-5.sym new file mode 100644 index 000000000..4586bb72c --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-5.sym @@ -0,0 +1,21 @@ +(label) @1 +(label) @begin +(label) @end +(signed word*) SCREEN +(const signed word*) SCREEN#0 SCREEN = (signed word*) 1024 +(const byte) SIZEOF_SIGNED_WORD SIZEOF_SIGNED_WORD = (byte) 2 +(const word) STACK_BASE STACK_BASE = (word) $103 +(signed word) current +(signed word) current#5 current zp ZP_WORD:2 2.0 +(void()) main() +(signed word~) main::$0 $0 zp ZP_WORD:2 2.0 +(signed word~) main::$1 $1 zp ZP_WORD:4 2.0 +(label) main::@return +__stackcall (signed word()) next() +(label) next::@return +(const byte) next::OFFSET_STACK_RETURN OFFSET_STACK_RETURN = (byte) 0 +(signed word) next::return +(signed word) next::return#0 return zp ZP_WORD:2 2.0 + +zp ZP_WORD:2 [ main::$0 current#5 next::return#0 ] +zp ZP_WORD:4 [ main::$1 ]