From bcddd821ff69bb09cc0081bb39e4017689ffcd1c Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Fri, 20 Sep 2019 16:27:50 +0200 Subject: [PATCH] Renamed stack-manipulating fragments. Added fragment-based stack clean-up. #316 --- .../mos6502-common/_stackpullbyte_1.asm | 1 + .../mos6502-common/_stackpullbyte_2.asm | 2 + .../mos6502-common/_stackpullbyte_3.asm | 3 + .../mos6502-common/_stackpullbyte_4.asm | 4 + .../mos6502-common/_stackpullbyte_5.asm | 5 + .../mos6502-common/_stackpullbyte_6.asm | 6 + .../mos6502-common/_stackpullbyte_7.asm | 7 + .../mos6502-common/_stackpullbyte_8.asm | 8 + ...e_=vbuaa.asm => _stackpushbyte_=vbuaa.asm} | 0 .../mos6502-common/_stackpushword_=vwuc1.asm | 4 + ...buc1.asm => vbuaa=_stackgetbyte_vbuc1.asm} | 0 .../vwuz1=_stackgetword_vbuc1.asm | 5 + .../mos6502-undoc/_stackpullbyte_3.asm | 4 + .../mos6502-undoc/_stackpullbyte_4.asm | 4 + .../mos6502-undoc/_stackpullbyte_5.asm | 4 + .../mos6502-undoc/_stackpullbyte_6.asm | 4 + .../mos6502-undoc/_stackpullbyte_7.asm | 4 + .../mos6502-undoc/_stackpullbyte_8.asm | 4 + .../AsmFragmentInstanceSpecFactory.java | 4 +- .../kickc/passes/Pass4CodeGeneration.java | 48 +- .../dk/camelot64/kickc/test/TestPrograms.java | 9 +- .../kc/procedure-callingconvention-stack-2.kc | 11 + .../procedure-callingconvention-stack-0.log | 30 +- .../procedure-callingconvention-stack-1.asm | 31 ++ .../procedure-callingconvention-stack-1.cfg | 29 + .../procedure-callingconvention-stack-1.log | 449 +++++++++++++++ .../procedure-callingconvention-stack-1.sym | 24 + .../procedure-callingconvention-stack-2.asm | 53 ++ .../procedure-callingconvention-stack-2.cfg | 29 + .../procedure-callingconvention-stack-2.log | 515 ++++++++++++++++++ .../procedure-callingconvention-stack-2.sym | 23 + 31 files changed, 1287 insertions(+), 37 deletions(-) create mode 100644 src/main/fragment/mos6502-common/_stackpullbyte_1.asm create mode 100644 src/main/fragment/mos6502-common/_stackpullbyte_2.asm create mode 100644 src/main/fragment/mos6502-common/_stackpullbyte_3.asm create mode 100644 src/main/fragment/mos6502-common/_stackpullbyte_4.asm create mode 100644 src/main/fragment/mos6502-common/_stackpullbyte_5.asm create mode 100644 src/main/fragment/mos6502-common/_stackpullbyte_6.asm create mode 100644 src/main/fragment/mos6502-common/_stackpullbyte_7.asm create mode 100644 src/main/fragment/mos6502-common/_stackpullbyte_8.asm rename src/main/fragment/mos6502-common/{_pushbyte_=vbuaa.asm => _stackpushbyte_=vbuaa.asm} (100%) create mode 100644 src/main/fragment/mos6502-common/_stackpushword_=vwuc1.asm rename src/main/fragment/mos6502-common/{vbuaa=_stackbyte_vbuc1.asm => vbuaa=_stackgetbyte_vbuc1.asm} (100%) create mode 100644 src/main/fragment/mos6502-common/vwuz1=_stackgetword_vbuc1.asm create mode 100644 src/main/fragment/mos6502-undoc/_stackpullbyte_3.asm create mode 100644 src/main/fragment/mos6502-undoc/_stackpullbyte_4.asm create mode 100644 src/main/fragment/mos6502-undoc/_stackpullbyte_5.asm create mode 100644 src/main/fragment/mos6502-undoc/_stackpullbyte_6.asm create mode 100644 src/main/fragment/mos6502-undoc/_stackpullbyte_7.asm create mode 100644 src/main/fragment/mos6502-undoc/_stackpullbyte_8.asm create mode 100644 src/test/kc/procedure-callingconvention-stack-2.kc create mode 100644 src/test/ref/procedure-callingconvention-stack-1.asm create mode 100644 src/test/ref/procedure-callingconvention-stack-1.cfg create mode 100644 src/test/ref/procedure-callingconvention-stack-1.log create mode 100644 src/test/ref/procedure-callingconvention-stack-1.sym create mode 100644 src/test/ref/procedure-callingconvention-stack-2.asm create mode 100644 src/test/ref/procedure-callingconvention-stack-2.cfg create mode 100644 src/test/ref/procedure-callingconvention-stack-2.log create mode 100644 src/test/ref/procedure-callingconvention-stack-2.sym diff --git a/src/main/fragment/mos6502-common/_stackpullbyte_1.asm b/src/main/fragment/mos6502-common/_stackpullbyte_1.asm new file mode 100644 index 000000000..7a15a8b2d --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpullbyte_1.asm @@ -0,0 +1 @@ +pla \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/_stackpullbyte_2.asm b/src/main/fragment/mos6502-common/_stackpullbyte_2.asm new file mode 100644 index 000000000..611163716 --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpullbyte_2.asm @@ -0,0 +1,2 @@ +pla +pla \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/_stackpullbyte_3.asm b/src/main/fragment/mos6502-common/_stackpullbyte_3.asm new file mode 100644 index 000000000..45ec634e7 --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpullbyte_3.asm @@ -0,0 +1,3 @@ +pla +pla +pla \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/_stackpullbyte_4.asm b/src/main/fragment/mos6502-common/_stackpullbyte_4.asm new file mode 100644 index 000000000..ec0c5c7a0 --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpullbyte_4.asm @@ -0,0 +1,4 @@ +pla +pla +pla +pla \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/_stackpullbyte_5.asm b/src/main/fragment/mos6502-common/_stackpullbyte_5.asm new file mode 100644 index 000000000..5d2cd08eb --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpullbyte_5.asm @@ -0,0 +1,5 @@ +pla +pla +pla +pla +pla \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/_stackpullbyte_6.asm b/src/main/fragment/mos6502-common/_stackpullbyte_6.asm new file mode 100644 index 000000000..16cd7b578 --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpullbyte_6.asm @@ -0,0 +1,6 @@ +pla +pla +pla +pla +pla +pla \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/_stackpullbyte_7.asm b/src/main/fragment/mos6502-common/_stackpullbyte_7.asm new file mode 100644 index 000000000..077a27e07 --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpullbyte_7.asm @@ -0,0 +1,7 @@ +pla +pla +pla +pla +pla +pla +pla \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/_stackpullbyte_8.asm b/src/main/fragment/mos6502-common/_stackpullbyte_8.asm new file mode 100644 index 000000000..ca68e4755 --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpullbyte_8.asm @@ -0,0 +1,8 @@ +pla +pla +pla +pla +pla +pla +pla +pla \ No newline at end of file diff --git a/src/main/fragment/mos6502-common/_pushbyte_=vbuaa.asm b/src/main/fragment/mos6502-common/_stackpushbyte_=vbuaa.asm similarity index 100% rename from src/main/fragment/mos6502-common/_pushbyte_=vbuaa.asm rename to src/main/fragment/mos6502-common/_stackpushbyte_=vbuaa.asm diff --git a/src/main/fragment/mos6502-common/_stackpushword_=vwuc1.asm b/src/main/fragment/mos6502-common/_stackpushword_=vwuc1.asm new file mode 100644 index 000000000..aea37787e --- /dev/null +++ b/src/main/fragment/mos6502-common/_stackpushword_=vwuc1.asm @@ -0,0 +1,4 @@ +lda #>{c1} +pha +lda #<{c1} +pha diff --git a/src/main/fragment/mos6502-common/vbuaa=_stackbyte_vbuc1.asm b/src/main/fragment/mos6502-common/vbuaa=_stackgetbyte_vbuc1.asm similarity index 100% rename from src/main/fragment/mos6502-common/vbuaa=_stackbyte_vbuc1.asm rename to src/main/fragment/mos6502-common/vbuaa=_stackgetbyte_vbuc1.asm diff --git a/src/main/fragment/mos6502-common/vwuz1=_stackgetword_vbuc1.asm b/src/main/fragment/mos6502-common/vwuz1=_stackgetword_vbuc1.asm new file mode 100644 index 000000000..768a3a655 --- /dev/null +++ b/src/main/fragment/mos6502-common/vwuz1=_stackgetword_vbuc1.asm @@ -0,0 +1,5 @@ +tsx +lda STACK_BASE+{c1},x +sta {z1} +lda STACK_BASE+{c1}+1,x +sta {z1}+1 diff --git a/src/main/fragment/mos6502-undoc/_stackpullbyte_3.asm b/src/main/fragment/mos6502-undoc/_stackpullbyte_3.asm new file mode 100644 index 000000000..4a4ee9516 --- /dev/null +++ b/src/main/fragment/mos6502-undoc/_stackpullbyte_3.asm @@ -0,0 +1,4 @@ +tsx +txa +axs #-3 +txs \ No newline at end of file diff --git a/src/main/fragment/mos6502-undoc/_stackpullbyte_4.asm b/src/main/fragment/mos6502-undoc/_stackpullbyte_4.asm new file mode 100644 index 000000000..b278a5340 --- /dev/null +++ b/src/main/fragment/mos6502-undoc/_stackpullbyte_4.asm @@ -0,0 +1,4 @@ +tsx +txa +axs #-4 +txs \ No newline at end of file diff --git a/src/main/fragment/mos6502-undoc/_stackpullbyte_5.asm b/src/main/fragment/mos6502-undoc/_stackpullbyte_5.asm new file mode 100644 index 000000000..46d48eee7 --- /dev/null +++ b/src/main/fragment/mos6502-undoc/_stackpullbyte_5.asm @@ -0,0 +1,4 @@ +tsx +txa +axs #-5 +txs \ No newline at end of file diff --git a/src/main/fragment/mos6502-undoc/_stackpullbyte_6.asm b/src/main/fragment/mos6502-undoc/_stackpullbyte_6.asm new file mode 100644 index 000000000..1f3722fc4 --- /dev/null +++ b/src/main/fragment/mos6502-undoc/_stackpullbyte_6.asm @@ -0,0 +1,4 @@ +tsx +txa +axs #-6 +txs \ No newline at end of file diff --git a/src/main/fragment/mos6502-undoc/_stackpullbyte_7.asm b/src/main/fragment/mos6502-undoc/_stackpullbyte_7.asm new file mode 100644 index 000000000..f61d87ea8 --- /dev/null +++ b/src/main/fragment/mos6502-undoc/_stackpullbyte_7.asm @@ -0,0 +1,4 @@ +tsx +txa +axs #-7 +txs \ No newline at end of file diff --git a/src/main/fragment/mos6502-undoc/_stackpullbyte_8.asm b/src/main/fragment/mos6502-undoc/_stackpullbyte_8.asm new file mode 100644 index 000000000..e1e2bdb0f --- /dev/null +++ b/src/main/fragment/mos6502-undoc/_stackpullbyte_8.asm @@ -0,0 +1,4 @@ +tsx +txa +axs #-8 +txs \ 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 e5f2e4adf..7e223b6b0 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java @@ -358,9 +358,9 @@ public class AsmFragmentInstanceSpecFactory { return name; } else if(value instanceof ParamStackValue) { ParamStackValue paramStackValue = (ParamStackValue) value; - return "_stack"+paramStackValue.getValueType().getTypeName()+"_"+bind(paramStackValue.getStackOffset()); + return "_stackget"+paramStackValue.getValueType().getTypeName()+"_"+bind(paramStackValue.getStackOffset()); } else if(value instanceof ParamStackPush) { - return "_push"+((ParamStackPush) value).getType().getTypeName()+"_"; + return "_stackpush"+((ParamStackPush) value).getType().getTypeName()+"_"; } throw new RuntimeException("Binding of value type not supported " + value.toString(program)); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 6e9d7f232..9b82280d6 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -699,7 +699,7 @@ public class Pass4CodeGeneration { StatementAssignment assignment = (StatementAssignment) statement; AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(assignment, assignmentAlu, program); ensureEncoding(asm, asmFragmentInstanceSpecFactory); - generateAsm(asm, asmFragmentInstanceSpecFactory); + generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); aluState.clear(); return; } @@ -725,13 +725,13 @@ public class Pass4CodeGeneration { } else { AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(assignment, program); ensureEncoding(asm, asmFragmentInstanceSpecFactory); - generateAsm(asm, asmFragmentInstanceSpecFactory); + generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); } } } else if(statement instanceof StatementConditionalJump) { AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory((StatementConditionalJump) statement, block, program, getGraph()); ensureEncoding(asm, asmFragmentInstanceSpecFactory); - generateAsm(asm, asmFragmentInstanceSpecFactory); + generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); } else if(statement instanceof StatementCall) { StatementCall call = (StatementCall) statement; @@ -753,9 +753,12 @@ public class Pass4CodeGeneration { for(RValue parameter : call.getParameters()) { SymbolType parameterType = SymbolTypeInference.inferType(program.getScope(), parameter); AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(new ParamStackPush(parameterType), parameter, program, block.getScope()); + asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); ensureEncoding(asm, asmFragmentInstanceSpecFactory); - generateAsm(asm, asmFragmentInstanceSpecFactory); + generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); } + asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); + asm.getCurrentChunk().setFragment("jsr"); } asm.addInstruction("jsr", AsmAddressingMode.ABS, call.getProcedure().getFullName(), false); // Clean up the stack @@ -765,10 +768,10 @@ public class Pass4CodeGeneration { SymbolType parameterType = SymbolTypeInference.inferType(program.getScope(), parameter); parameterBytes += parameterType.getSizeBytes(); } - // TODO: Replace with fragment - to allow hand-coded handling of the stack pointer modifications - eg. using TSX, TXA, AXS #{}, TXS - for(int i = 0; i < parameterBytes; i++) { - asm.addInstruction("pla", AsmAddressingMode.NON, null, false); - } + String pullSignature = "_stackpullbyte_" + Integer.toString(parameterBytes); + AsmFragmentInstanceSpec pullFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, pullSignature, new LinkedHashMap<>(), block.getScope()); + asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo)); + generateAsm(asm, pullFragmentInstanceSpec); } } else if(statement instanceof StatementReturn) { Procedure.InterruptType interruptType = null; @@ -866,24 +869,23 @@ public class Pass4CodeGeneration { * Generate ASM code for an ASM fragment instance * * @param asm The ASM program to generate into - * @param asmFragmentInstanceSpecFactory The ASM fragment instance specification factory + * @param fragmentInstanceSpec The ASM fragment instance specification */ - private void generateAsm(AsmProgram asm, AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory) { - String initialSignature = asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec().getSignature(); - AsmFragmentInstanceSpec asmFragmentInstanceSpec = asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec(); - AsmFragmentInstance asmFragmentInstance = null; - StringBuffer fragmentVariationsTried = new StringBuffer(); - while(asmFragmentInstance == null) { + private void generateAsm(AsmProgram asm, AsmFragmentInstanceSpec fragmentInstanceSpec) { + String initialSignature = fragmentInstanceSpec.getSignature(); + AsmFragmentInstance fragmentInstance = null; + StringBuilder fragmentVariationsTried = new StringBuilder(); + while(fragmentInstance == null) { try { - asmFragmentInstance = program.getAsmFragmentSynthesizer().getFragmentInstance(asmFragmentInstanceSpec, program.getLog()); + fragmentInstance = program.getAsmFragmentSynthesizer().getFragmentInstance(fragmentInstanceSpec, program.getLog()); } catch(AsmFragmentTemplateSynthesizer.UnknownFragmentException e) { // Unknown fragment - keep looking through alternative ASM fragment instance specs until we have tried them all - String signature = asmFragmentInstanceSpec.getSignature(); + String signature = fragmentInstanceSpec.getSignature(); fragmentVariationsTried.append(signature).append(" "); - if(asmFragmentInstanceSpec.hasNextVariation()) { - asmFragmentInstanceSpec.nextVariation(); + if(fragmentInstanceSpec.hasNextVariation()) { + fragmentInstanceSpec.nextVariation(); if(program.getLog().isVerboseFragmentLog()) { - program.getLog().append("Fragment not found " + signature + ". Attempting another variation " + asmFragmentInstanceSpec.getSignature()); + program.getLog().append("Fragment not found " + signature + ". Attempting another variation " + fragmentInstanceSpec.getSignature()); } } else { // No more variations available - fail with an error @@ -891,8 +893,8 @@ public class Pass4CodeGeneration { } } } - asm.getCurrentChunk().setFragment(asmFragmentInstance.getFragmentName()); - asmFragmentInstance.generate(asm); + asm.getCurrentChunk().setFragment(fragmentInstance.getFragmentName()); + fragmentInstance.generate(asm); } /** @@ -1047,7 +1049,7 @@ public class Pass4CodeGeneration { } else { AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(lValue, rValue, program, scope); ensureEncoding(asm, asmFragmentInstanceSpecFactory); - generateAsm(asm, asmFragmentInstanceSpecFactory); + generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec()); } } transitionSetGenerated(transition); diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index fc742062c..fda2131f4 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -35,12 +35,15 @@ public class TestPrograms { public TestPrograms() { } - /* + @Test + public void testProcedureCallingConventionStack2() throws IOException, URISyntaxException { + compileAndCompare("procedure-callingconvention-stack-2"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence()); + } + @Test public void testProcedureCallingConventionStack1() throws IOException, URISyntaxException { - compileAndCompare("procedure-callingconvention-stack-1", log()); + compileAndCompare("procedure-callingconvention-stack-1"); } - */ @Test public void testProcedureCallingConventionStack0() throws IOException, URISyntaxException { diff --git a/src/test/kc/procedure-callingconvention-stack-2.kc b/src/test/kc/procedure-callingconvention-stack-2.kc new file mode 100644 index 000000000..5003c879f --- /dev/null +++ b/src/test/kc/procedure-callingconvention-stack-2.kc @@ -0,0 +1,11 @@ +// Test a procedure with calling convention stack - and enough parameters to use fast ASM for cleaning stack + +const word* SCREEN = 0x0400; + +void main(void) { + SCREEN[0] = plus(0x1234, 0x2345); +} + +word __stackcall plus(word a, word b) { + return a+b; +} \ No newline at end of file diff --git a/src/test/ref/procedure-callingconvention-stack-0.log b/src/test/ref/procedure-callingconvention-stack-0.log index 759e8ceae..9b58777fc 100644 --- a/src/test/ref/procedure-callingconvention-stack-0.log +++ b/src/test/ref/procedure-callingconvention-stack-0.log @@ -181,12 +181,16 @@ bend: // main main: { .label _0 = 2 - // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _pushbyte_=vbuc1 + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 lda #'0' pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 lda #7 pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr jsr plus + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2 pla pla // [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1 @@ -206,11 +210,11 @@ plus: { .label a = 3 .label b = 4 .label return = 5 - // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1 + // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1 tsx lda STACK_BASE+OFFSET_STACK_A,x sta.z a - // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackbyte_vbuc1 + // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackgetbyte_vbuc1 tsx lda STACK_BASE+OFFSET_STACK_B,x sta.z b @@ -285,12 +289,16 @@ bend_from_b1: bend: // main main: { - // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _pushbyte_=vbuc1 + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 lda #'0' pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 lda #7 pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr jsr plus + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2 pla pla // [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy @@ -307,11 +315,11 @@ plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 .label a = 2 - // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1 + // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1 tsx lda STACK_BASE+OFFSET_STACK_A,x sta.z a - // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackbyte_vbuc1 + // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1 tsx lda STACK_BASE+OFFSET_STACK_B,x // [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa @@ -395,12 +403,16 @@ Score: 60 // main main: { // plus('0', 7) - // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _pushbyte_=vbuc1 + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 lda #'0' pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 lda #7 pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr jsr plus + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2 pla pla // SCREEN[0] = plus('0', 7) @@ -417,11 +429,11 @@ plus: { .const OFFSET_STACK_A = 0 .const OFFSET_STACK_B = 1 .label a = 2 - // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackbyte_vbuc1 + // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1 tsx lda STACK_BASE+OFFSET_STACK_A,x sta.z a - // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackbyte_vbuc1 + // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1 tsx lda STACK_BASE+OFFSET_STACK_B,x // return a+b; diff --git a/src/test/ref/procedure-callingconvention-stack-1.asm b/src/test/ref/procedure-callingconvention-stack-1.asm new file mode 100644 index 000000000..8ebcf3476 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-1.asm @@ -0,0 +1,31 @@ +// Test a procedure with calling convention stack +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 + .const STACK_BASE = $103 +main: { + lda #'0' + pha + lda #7 + pha + jsr plus + pla + pla + sty SCREEN + rts +} +// plus(byte zeropage(2) a, byte register(A) b) +plus: { + .const OFFSET_STACK_A = 0 + .const OFFSET_STACK_B = 1 + .label a = 2 + tsx + lda STACK_BASE+OFFSET_STACK_A,x + sta.z a + tsx + lda STACK_BASE+OFFSET_STACK_B,x + clc + adc.z a + rts +} diff --git a/src/test/ref/procedure-callingconvention-stack-1.cfg b/src/test/ref/procedure-callingconvention-stack-1.cfg new file mode 100644 index 000000000..592ca8b0f --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-1.cfg @@ -0,0 +1,29 @@ +@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] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 + [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 + to:main::@return +main::@return: scope:[main] from main + [7] return + to:@return + +__stackcall (byte()) plus((byte) plus::a , (byte) plus::b) +plus: scope:[plus] from + [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) + [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) + [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 + to:plus::@return +plus::@return: scope:[plus] from plus + [11] return (byte) plus::return#0 + to:@return diff --git a/src/test/ref/procedure-callingconvention-stack-1.log b/src/test/ref/procedure-callingconvention-stack-1.log new file mode 100644 index 000000000..9b58777fc --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-1.log @@ -0,0 +1,449 @@ +Culled Empty Block (label) @1 +Culled Empty Block (label) plus::@1 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte*) SCREEN#0 ← ((byte*)) (number) $400 + to:@2 + +(void()) main() +main: scope:[main] from @2 + (byte~) main::$0 ← call plus (byte) '0' (number) 7 + *((byte*) SCREEN#0 + (number) 0) ← (byte~) main::$0 + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +__stackcall (byte()) plus((byte) plus::a , (byte) plus::b) +plus: scope:[plus] from + (byte) plus::a#0 ← param((byte) plus::a) + (byte) plus::b#0 ← param((byte) plus::b) + (byte~) plus::$0 ← (byte) plus::a#0 + (byte) plus::b#0 + (byte) plus::return#0 ← (byte~) plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus + (byte) plus::return#1 ← phi( plus/(byte) plus::return#0 ) + return (byte) plus::return#1 + to:@return +@2: scope:[] from @begin + call main + to:@3 +@3: scope:[] from @2 + to:@end +@end: scope:[] from @3 + +SYMBOL TABLE SSA +(label) @2 +(label) @3 +(label) @begin +(label) @end +(byte*) SCREEN +(byte*) SCREEN#0 +(void()) main() +(byte~) main::$0 +(label) main::@return +__stackcall (byte()) plus((byte) plus::a , (byte) plus::b) +(byte~) plus::$0 +(label) plus::@return +(byte) plus::a +(byte) plus::a#0 +(byte) plus::b +(byte) plus::b#0 +(byte) plus::return +(byte) plus::return#0 +(byte) plus::return#1 + +Adding number conversion cast (unumber) 7 in (byte~) main::$0 ← call plus (byte) '0' (number) 7 +Adding number conversion cast (unumber) 0 in *((byte*) SCREEN#0 + (number) 0) ← (byte~) main::$0 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) plus::return#0 = (byte~) plus::$0 (byte) plus::return#1 +Successful SSA optimization Pass2AliasElimination +Constant (const byte*) SCREEN#0 = (byte*) 1024 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero SCREEN#0 in [2] *((const byte*) SCREEN#0 + (byte) 0) ← (byte~) main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Adding NOP phi() at start of @begin +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:2 +Calls in [main] to plus:6 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +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 replacing param((byte) plus::a) with paramstack(byte,plus::OFFSET_STACK_A) +Calling convention STACK_CALL replacing param((byte) plus::b) with paramstack(byte,plus::OFFSET_STACK_B) + +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] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 + [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 + to:main::@return +main::@return: scope:[main] from main + [7] return + to:@return + +__stackcall (byte()) plus((byte) plus::a , (byte) plus::b) +plus: scope:[plus] from + [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) + [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) + [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 + to:plus::@return +plus::@return: scope:[plus] from plus + [11] return (byte) plus::return#0 + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte*) SCREEN +(void()) main() +(byte~) main::$0 0.5 +__stackcall (byte()) plus((byte) plus::a , (byte) plus::b) +(byte) plus::a +(byte) plus::a#0 2.0 +(byte) plus::b +(byte) plus::b#0 4.0 +(byte) plus::return +(byte) plus::return#0 2.0 + +Initial phi equivalence classes +Added variable main::$0 to zero page equivalence class [ main::$0 ] +Added variable plus::a#0 to zero page equivalence class [ plus::a#0 ] +Added variable plus::b#0 to zero page equivalence class [ plus::b#0 ] +Added variable plus::return#0 to zero page equivalence class [ plus::return#0 ] +Complete equivalence classes +[ main::$0 ] +[ plus::a#0 ] +[ plus::b#0 ] +[ plus::return#0 ] +Allocated zp ZP_BYTE:2 [ main::$0 ] +Allocated zp ZP_BYTE:3 [ plus::a#0 ] +Allocated zp ZP_BYTE:4 [ plus::b#0 ] +Allocated zp ZP_BYTE:5 [ plus::return#0 ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Test a procedure with calling convention stack + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .const STACK_BASE = $103 + // @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 + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 + lda #'0' + pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 + lda #7 + pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr + jsr plus + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2 + pla + pla + // [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1 + lda.z _0 + sta SCREEN + jmp breturn + // main::@return + breturn: + // [7] return + rts +} + // plus +// plus(byte zeropage(3) a, byte zeropage(4) b) +plus: { + .const OFFSET_STACK_A = 0 + .const OFFSET_STACK_B = 1 + .label a = 3 + .label b = 4 + .label return = 5 + // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_A,x + sta.z a + // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackgetbyte_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_B,x + sta.z b + // [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuz1=vbuz2_plus_vbuz3 + lda.z a + clc + adc.z b + sta.z return + jmp breturn + // plus::@return + breturn: + // [11] return (byte) plus::return#0 + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a +Statement [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 ] ) always clobbers reg byte a reg byte x +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::$0 ] +Removing always clobbered register reg byte x as potential for zp ZP_BYTE:2 [ main::$0 ] +Statement [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:3 [ plus::a#0 ] +Removing always clobbered register reg byte x as potential for zp ZP_BYTE:3 [ plus::a#0 ] +Statement [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:5 [ main::$0 plus::return#0 ] ) always clobbers reg byte a +Statement [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a +Statement [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 ] ) always clobbers reg byte a reg byte x +Statement [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x +Statement [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:5 [ main::$0 plus::return#0 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::$0 ] : zp ZP_BYTE:2 , reg byte y , +Potential registers zp ZP_BYTE:3 [ plus::a#0 ] : zp ZP_BYTE:3 , reg byte y , +Potential registers zp ZP_BYTE:4 [ plus::b#0 ] : zp ZP_BYTE:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:5 [ plus::return#0 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [plus] 4: zp ZP_BYTE:4 [ plus::b#0 ] 2: zp ZP_BYTE:3 [ plus::a#0 ] 2: zp ZP_BYTE:5 [ plus::return#0 ] +Uplift Scope [main] 0.5: zp ZP_BYTE:2 [ main::$0 ] +Uplift Scope [] + +Uplifting [plus] best 81 combination reg byte a [ plus::b#0 ] zp ZP_BYTE:3 [ plus::a#0 ] reg byte a [ plus::return#0 ] +Uplifting [main] best 78 combination reg byte y [ main::$0 ] +Uplifting [] best 78 combination +Attempting to uplift remaining variables inzp ZP_BYTE:3 [ plus::a#0 ] +Uplifting [plus] best 78 combination zp ZP_BYTE:3 [ plus::a#0 ] +Allocated (was zp ZP_BYTE:3) zp ZP_BYTE:2 [ plus::a#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test a procedure with calling convention stack + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .const STACK_BASE = $103 + // @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: { + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 + lda #'0' + pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 + lda #7 + pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr + jsr plus + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2 + pla + pla + // [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy + sty SCREEN + jmp breturn + // main::@return + breturn: + // [7] return + rts +} + // plus +// plus(byte zeropage(2) a, byte register(A) b) +plus: { + .const OFFSET_STACK_A = 0 + .const OFFSET_STACK_B = 1 + .label a = 2 + // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_A,x + sta.z a + // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_B,x + // [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + jmp breturn + // plus::@return + breturn: + // [11] return (byte) plus::return#0 + 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 +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(const word) STACK_BASE STACK_BASE = (word) $103 +(void()) main() +(byte~) main::$0 reg byte y 0.5 +(label) main::@return +__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 +(byte) plus::a +(byte) plus::a#0 a zp ZP_BYTE:2 2.0 +(byte) plus::b +(byte) plus::b#0 reg byte a 4.0 +(byte) plus::return +(byte) plus::return#0 reg byte a 2.0 + +reg byte y [ main::$0 ] +zp ZP_BYTE:2 [ plus::a#0 ] +reg byte a [ plus::b#0 ] +reg byte a [ plus::return#0 ] + + +FINAL ASSEMBLER +Score: 60 + + // File Comments +// Test a procedure with calling convention stack + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .const STACK_BASE = $103 + // @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: { + // plus('0', 7) + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 + lda #'0' + pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1 + lda #7 + pha + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr + jsr plus + // [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2 + pla + pla + // SCREEN[0] = plus('0', 7) + // [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy + sty SCREEN + // main::@return + // } + // [7] return + rts +} + // plus +// plus(byte zeropage(2) a, byte register(A) b) +plus: { + .const OFFSET_STACK_A = 0 + .const OFFSET_STACK_B = 1 + .label a = 2 + // [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_A,x + sta.z a + // [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_B,x + // return a+b; + // [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa + clc + adc.z a + // plus::@return + // } + // [11] return (byte) plus::return#0 + rts +} + // File Data + diff --git a/src/test/ref/procedure-callingconvention-stack-1.sym b/src/test/ref/procedure-callingconvention-stack-1.sym new file mode 100644 index 000000000..3c6e8b7b2 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-1.sym @@ -0,0 +1,24 @@ +(label) @1 +(label) @begin +(label) @end +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(const word) STACK_BASE STACK_BASE = (word) $103 +(void()) main() +(byte~) main::$0 reg byte y 0.5 +(label) main::@return +__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 +(byte) plus::a +(byte) plus::a#0 a zp ZP_BYTE:2 2.0 +(byte) plus::b +(byte) plus::b#0 reg byte a 4.0 +(byte) plus::return +(byte) plus::return#0 reg byte a 2.0 + +reg byte y [ main::$0 ] +zp ZP_BYTE:2 [ plus::a#0 ] +reg byte a [ plus::b#0 ] +reg byte a [ plus::return#0 ] diff --git a/src/test/ref/procedure-callingconvention-stack-2.asm b/src/test/ref/procedure-callingconvention-stack-2.asm new file mode 100644 index 000000000..728d0bfb0 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-2.asm @@ -0,0 +1,53 @@ +// Test a procedure with calling convention stack - and enough parameters to use fast ASM for cleaning stack +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 + .const STACK_BASE = $103 +main: { + .label _0 = 2 + lda #>$1234 + pha + lda #<$1234 + pha + lda #>$2345 + pha + lda #<$2345 + pha + jsr plus + tsx + txa + axs #-4 + txs + lda.z _0 + sta SCREEN + lda.z _0+1 + sta SCREEN+1 + rts +} +// plus(word zeropage(4) a, word zeropage(6) b) +plus: { + .const OFFSET_STACK_A = 0 + .const OFFSET_STACK_B = 2 + .label a = 4 + .label b = 6 + .label return = 4 + tsx + lda STACK_BASE+OFFSET_STACK_A,x + sta.z a + lda STACK_BASE+OFFSET_STACK_A+1,x + sta.z a+1 + tsx + lda STACK_BASE+OFFSET_STACK_B,x + sta.z b + lda STACK_BASE+OFFSET_STACK_B+1,x + sta.z b+1 + lda.z return + clc + adc.z b + sta.z return + lda.z return+1 + adc.z b+1 + sta.z return+1 + rts +} diff --git a/src/test/ref/procedure-callingconvention-stack-2.cfg b/src/test/ref/procedure-callingconvention-stack-2.cfg new file mode 100644 index 000000000..fe551d0d4 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-2.cfg @@ -0,0 +1,29 @@ +@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] (word~) main::$0 ← call plus (word) $1234 (word) $2345 + [6] *((const word*) SCREEN#0) ← (word~) main::$0 + to:main::@return +main::@return: scope:[main] from main + [7] return + to:@return + +__stackcall (word()) plus((word) plus::a , (word) plus::b) +plus: scope:[plus] from + [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) + [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) + [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 + to:plus::@return +plus::@return: scope:[plus] from plus + [11] return (word) plus::return#0 + to:@return diff --git a/src/test/ref/procedure-callingconvention-stack-2.log b/src/test/ref/procedure-callingconvention-stack-2.log new file mode 100644 index 000000000..b1bb43e1d --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-2.log @@ -0,0 +1,515 @@ +Fixing pointer array-indexing *((word*) SCREEN + (number) 0) +Culled Empty Block (label) @1 +Culled Empty Block (label) plus::@1 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (word*) SCREEN#0 ← ((word*)) (number) $400 + to:@2 + +(void()) main() +main: scope:[main] from @2 + (word~) main::$0 ← call plus (number) $1234 (number) $2345 + (number~) main::$1 ← (number) 0 * (const byte) SIZEOF_WORD + *((word*) SCREEN#0 + (number~) main::$1) ← (word~) main::$0 + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +__stackcall (word()) plus((word) plus::a , (word) plus::b) +plus: scope:[plus] from + (word) plus::a#0 ← param((word) plus::a) + (word) plus::b#0 ← param((word) plus::b) + (word~) plus::$0 ← (word) plus::a#0 + (word) plus::b#0 + (word) plus::return#0 ← (word~) plus::$0 + to:plus::@return +plus::@return: scope:[plus] from plus + (word) plus::return#1 ← phi( plus/(word) plus::return#0 ) + return (word) plus::return#1 + to:@return +@2: scope:[] from @begin + call main + to:@3 +@3: scope:[] from @2 + to:@end +@end: scope:[] from @3 + +SYMBOL TABLE SSA +(label) @2 +(label) @3 +(label) @begin +(label) @end +(word*) SCREEN +(word*) SCREEN#0 +(const byte) SIZEOF_WORD = (byte) 2 +(void()) main() +(word~) main::$0 +(number~) main::$1 +(label) main::@return +__stackcall (word()) plus((word) plus::a , (word) plus::b) +(word~) plus::$0 +(label) plus::@return +(word) plus::a +(word) plus::a#0 +(word) plus::b +(word) plus::b#0 +(word) plus::return +(word) plus::return#0 +(word) plus::return#1 + +Adding number conversion cast (unumber) $1234 in (word~) main::$0 ← call plus (number) $1234 (number) $2345 +Adding number conversion cast (unumber) $2345 in (word~) main::$0 ← call plus (unumber)(number) $1234 (number) $2345 +Adding number conversion cast (unumber) 0 in (number~) main::$1 ← (number) 0 * (const byte) SIZEOF_WORD +Adding number conversion cast (unumber) main::$1 in (number~) main::$1 ← (unumber)(number) 0 * (const byte) SIZEOF_WORD +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (word*) SCREEN#0 ← (word*)(number) $400 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (word*) 1024 +Simplifying constant integer cast $1234 +Simplifying constant integer cast $2345 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (word) $1234 +Finalized unsigned number type (word) $2345 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inferred type updated to byte in (unumber~) main::$1 ← (byte) 0 * (const byte) SIZEOF_WORD +Alias (word) plus::return#0 = (word~) plus::$0 (word) plus::return#1 +Successful SSA optimization Pass2AliasElimination +Constant right-side identified [2] (byte~) main::$1 ← (byte) 0 * (const byte) SIZEOF_WORD +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const word*) SCREEN#0 = (word*) 1024 +Constant (const byte) main::$1 = 0*SIZEOF_WORD +Successful SSA optimization Pass2ConstantIdentification +Simplifying constant evaluating to zero (byte) 0*(const byte) SIZEOF_WORD in +Successful SSA optimization PassNSimplifyConstantZero +Simplifying expression containing zero SCREEN#0 in [3] *((const word*) SCREEN#0 + (const byte) main::$1) ← (word~) main::$0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant (const byte) main::$1 +Eliminating unused constant (const byte) SIZEOF_WORD +Successful SSA optimization PassNEliminateUnusedVars +Adding NOP phi() at start of @begin +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:2 +Calls in [main] to plus:6 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +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 replacing param((word) plus::a) with paramstack(word,plus::OFFSET_STACK_A) +Calling convention STACK_CALL replacing param((word) plus::b) with paramstack(word,plus::OFFSET_STACK_B) + +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] (word~) main::$0 ← call plus (word) $1234 (word) $2345 + [6] *((const word*) SCREEN#0) ← (word~) main::$0 + to:main::@return +main::@return: scope:[main] from main + [7] return + to:@return + +__stackcall (word()) plus((word) plus::a , (word) plus::b) +plus: scope:[plus] from + [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) + [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) + [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 + to:plus::@return +plus::@return: scope:[plus] from plus + [11] return (word) plus::return#0 + to:@return + + +VARIABLE REGISTER WEIGHTS +(word*) SCREEN +(void()) main() +(word~) main::$0 0.5 +__stackcall (word()) plus((word) plus::a , (word) plus::b) +(word) plus::a +(word) plus::a#0 2.0 +(word) plus::b +(word) plus::b#0 4.0 +(word) plus::return +(word) plus::return#0 2.0 + +Initial phi equivalence classes +Added variable main::$0 to zero page equivalence class [ main::$0 ] +Added variable plus::a#0 to zero page equivalence class [ plus::a#0 ] +Added variable plus::b#0 to zero page equivalence class [ plus::b#0 ] +Added variable plus::return#0 to zero page equivalence class [ plus::return#0 ] +Complete equivalence classes +[ main::$0 ] +[ plus::a#0 ] +[ plus::b#0 ] +[ plus::return#0 ] +Allocated zp ZP_WORD:2 [ main::$0 ] +Allocated zp ZP_WORD:4 [ plus::a#0 ] +Allocated zp ZP_WORD:6 [ plus::b#0 ] +Allocated zp ZP_WORD:8 [ plus::return#0 ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Test a procedure with calling convention stack - and enough parameters to use fast ASM for cleaning stack + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .const STACK_BASE = $103 + // @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 + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1 + lda #>$1234 + pha + lda #<$1234 + pha + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1 + lda #>$2345 + pha + lda #<$2345 + pha + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- jsr + jsr plus + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpullbyte_4 + tsx + txa + axs #-4 + txs + // [6] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1 + lda.z _0 + sta SCREEN + lda.z _0+1 + sta SCREEN+1 + jmp breturn + // main::@return + breturn: + // [7] return + rts +} + // plus +// plus(word zeropage(4) a, word zeropage(6) b) +plus: { + .const OFFSET_STACK_A = 0 + .const OFFSET_STACK_B = 2 + .label a = 4 + .label b = 6 + .label return = 8 + // [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_A,x + sta.z a + lda STACK_BASE+OFFSET_STACK_A+1,x + sta.z a+1 + // [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_B,x + sta.z b + lda STACK_BASE+OFFSET_STACK_B+1,x + sta.z b+1 + // [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz2_plus_vwuz3 + lda.z a + clc + adc.z b + sta.z return + lda.z a+1 + adc.z b+1 + sta.z return+1 + jmp breturn + // plus::@return + breturn: + // [11] return (word) plus::return#0 + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a reg byte x +Statement [6] *((const word*) SCREEN#0) ← (word~) main::$0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 ] ) always clobbers reg byte a reg byte x +Statement [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x +Statement [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 [ plus::return#0 ] ( main:2::plus:5 [ main::$0 plus::return#0 ] ) always clobbers reg byte a +Potential registers zp ZP_WORD:2 [ main::$0 ] : zp ZP_WORD:2 , +Potential registers zp ZP_WORD:4 [ plus::a#0 ] : zp ZP_WORD:4 , +Potential registers zp ZP_WORD:6 [ plus::b#0 ] : zp ZP_WORD:6 , +Potential registers zp ZP_WORD:8 [ plus::return#0 ] : zp ZP_WORD:8 , + +REGISTER UPLIFT SCOPES +Uplift Scope [plus] 4: zp ZP_WORD:6 [ plus::b#0 ] 2: zp ZP_WORD:4 [ plus::a#0 ] 2: zp ZP_WORD:8 [ plus::return#0 ] +Uplift Scope [main] 0.5: zp ZP_WORD:2 [ main::$0 ] +Uplift Scope [] + +Uplifting [plus] best 132 combination zp ZP_WORD:6 [ plus::b#0 ] zp ZP_WORD:4 [ plus::a#0 ] zp ZP_WORD:8 [ plus::return#0 ] +Uplifting [main] best 132 combination zp ZP_WORD:2 [ main::$0 ] +Uplifting [] best 132 combination +Coalescing zero page register [ zp ZP_WORD:4 [ plus::a#0 ] ] with [ zp ZP_WORD:8 [ plus::return#0 ] ] - score: 1 + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test a procedure with calling convention stack - and enough parameters to use fast ASM for cleaning stack + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .const STACK_BASE = $103 + // @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 + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1 + lda #>$1234 + pha + lda #<$1234 + pha + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1 + lda #>$2345 + pha + lda #<$2345 + pha + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- jsr + jsr plus + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpullbyte_4 + tsx + txa + axs #-4 + txs + // [6] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1 + lda.z _0 + sta SCREEN + lda.z _0+1 + sta SCREEN+1 + jmp breturn + // main::@return + breturn: + // [7] return + rts +} + // plus +// plus(word zeropage(4) a, word zeropage(6) b) +plus: { + .const OFFSET_STACK_A = 0 + .const OFFSET_STACK_B = 2 + .label a = 4 + .label b = 6 + .label return = 4 + // [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_A,x + sta.z a + lda STACK_BASE+OFFSET_STACK_A+1,x + sta.z a+1 + // [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_B,x + sta.z b + lda STACK_BASE+OFFSET_STACK_B+1,x + sta.z b+1 + // [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz1_plus_vwuz2 + lda.z return + clc + adc.z b + sta.z return + lda.z return+1 + adc.z b+1 + sta.z return+1 + jmp breturn + // plus::@return + breturn: + // [11] return (word) plus::return#0 + 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 +(word*) SCREEN +(const word*) SCREEN#0 SCREEN = (word*) 1024 +(const word) STACK_BASE STACK_BASE = (word) $103 +(void()) main() +(word~) main::$0 $0 zp ZP_WORD:2 0.5 +(label) main::@return +__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 +(word) plus::a +(word) plus::a#0 a zp ZP_WORD:4 2.0 +(word) plus::b +(word) plus::b#0 b zp ZP_WORD:6 4.0 +(word) plus::return +(word) plus::return#0 return zp ZP_WORD:4 2.0 + +zp ZP_WORD:2 [ main::$0 ] +zp ZP_WORD:4 [ plus::a#0 plus::return#0 ] +zp ZP_WORD:6 [ plus::b#0 ] + + +FINAL ASSEMBLER +Score: 114 + + // File Comments +// Test a procedure with calling convention stack - and enough parameters to use fast ASM for cleaning stack + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .const STACK_BASE = $103 + // @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 + // plus(0x1234, 0x2345) + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1 + lda #>$1234 + pha + lda #<$1234 + pha + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1 + lda #>$2345 + pha + lda #<$2345 + pha + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- jsr + jsr plus + // [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpullbyte_4 + tsx + txa + axs #-4 + txs + // SCREEN[0] = plus(0x1234, 0x2345) + // [6] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1 + lda.z _0 + sta SCREEN + lda.z _0+1 + sta SCREEN+1 + // main::@return + // } + // [7] return + rts +} + // plus +// plus(word zeropage(4) a, word zeropage(6) b) +plus: { + .const OFFSET_STACK_A = 0 + .const OFFSET_STACK_B = 2 + .label a = 4 + .label b = 6 + .label return = 4 + // [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_A,x + sta.z a + lda STACK_BASE+OFFSET_STACK_A+1,x + sta.z a+1 + // [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1 + tsx + lda STACK_BASE+OFFSET_STACK_B,x + sta.z b + lda STACK_BASE+OFFSET_STACK_B+1,x + sta.z b+1 + // return a+b; + // [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz1_plus_vwuz2 + lda.z return + clc + adc.z b + sta.z return + lda.z return+1 + adc.z b+1 + sta.z return+1 + // plus::@return + // } + // [11] return (word) plus::return#0 + rts +} + // File Data + diff --git a/src/test/ref/procedure-callingconvention-stack-2.sym b/src/test/ref/procedure-callingconvention-stack-2.sym new file mode 100644 index 000000000..e5a9f29ae --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-2.sym @@ -0,0 +1,23 @@ +(label) @1 +(label) @begin +(label) @end +(word*) SCREEN +(const word*) SCREEN#0 SCREEN = (word*) 1024 +(const word) STACK_BASE STACK_BASE = (word) $103 +(void()) main() +(word~) main::$0 $0 zp ZP_WORD:2 0.5 +(label) main::@return +__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 +(word) plus::a +(word) plus::a#0 a zp ZP_WORD:4 2.0 +(word) plus::b +(word) plus::b#0 b zp ZP_WORD:6 4.0 +(word) plus::return +(word) plus::return#0 return zp ZP_WORD:4 2.0 + +zp ZP_WORD:2 [ main::$0 ] +zp ZP_WORD:4 [ plus::a#0 plus::return#0 ] +zp ZP_WORD:6 [ plus::b#0 ]