diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 06d66914b..ae5f3de5c 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -513,12 +513,8 @@ public class Compiler { program.clearStatementInfos(); program.clearVariableReferenceInfos(); - //getLog().append("CONTROL FLOW GRAPH - LIVE RANGES FOUND"); - //getLog().append(program.getGraph().toString(program)); pass2AssertSSA(); - //getLog().setVerboseLiveRanges(false); - // Phi mem coalesce removes as many variables introduced by phi lifting as possible - as long as their live ranges do not overlap new Pass3PhiMemCoalesce(program).step(); new PassNCullEmptyBlocks(program).step(); @@ -539,6 +535,12 @@ public class Compiler { new PassNStatementIndices(program).execute(); pass2AssertSSA(); + // program.getLiveRangeVariablesEffective(); +// getLog().append("CONTROL FLOW GRAPH - LIVE RANGES FOUND"); +// getLog().append(program.getGraph().toString(program)); + + program.getLiveRangeVariablesEffective(); + getLog().append("\nFINAL CONTROL FLOW GRAPH"); getLog().append(program.getGraph().toString(program)); diff --git a/src/main/java/dk/camelot64/kickc/model/Program.java b/src/main/java/dk/camelot64/kickc/model/Program.java index bb385c016..ef8bc79ef 100644 --- a/src/main/java/dk/camelot64/kickc/model/Program.java +++ b/src/main/java/dk/camelot64/kickc/model/Program.java @@ -381,9 +381,14 @@ public class Program { return symbolInfos; } + public boolean hasLiveRangeVariablesEffective() { + return liveRangeVariablesEffective!=null; + } + public LiveRangeVariablesEffective getLiveRangeVariablesEffective() { - if(liveRangeVariablesEffective==null) + if(liveRangeVariablesEffective==null) { this.liveRangeVariablesEffective = new PassNCalcLiveRangesEffective(this).calculate(); + } return liveRangeVariablesEffective; } diff --git a/src/main/java/dk/camelot64/kickc/model/statements/StatementBase.java b/src/main/java/dk/camelot64/kickc/model/statements/StatementBase.java index f3b309048..32935634c 100644 --- a/src/main/java/dk/camelot64/kickc/model/statements/StatementBase.java +++ b/src/main/java/dk/camelot64/kickc/model/statements/StatementBase.java @@ -81,16 +81,18 @@ public abstract class StatementBase implements Statement { LiveRangeVariables liveRanges = program.getLiveRangeVariables(); StringBuilder alive = new StringBuilder(); alive.append(getAliveString(liveRanges.getAlive(index))); - LiveRangeVariablesEffective liveRangeVariablesEffective = program.getLiveRangeVariablesEffective(); - if(liveRangeVariablesEffective != null) { - LiveRangeVariablesEffective.AliveCombinations aliveCombinations = liveRangeVariablesEffective.getAliveCombinations(this); - alive.append(" ( "); - for(LiveRangeVariablesEffective.CallPath callPath : aliveCombinations.getCallPaths().getCallPaths()) { - alive.append(getCallPathString(callPath.getPath())); - alive.append(getAliveString(aliveCombinations.getEffectiveAliveAtStmt(callPath))); - alive.append(" "); + if(program.hasLiveRangeVariablesEffective()) { + LiveRangeVariablesEffective liveRangeVariablesEffective = program.getLiveRangeVariablesEffective(); + if(liveRangeVariablesEffective != null) { + LiveRangeVariablesEffective.AliveCombinations aliveCombinations = liveRangeVariablesEffective.getAliveCombinations(this); + alive.append(" ( "); + for(LiveRangeVariablesEffective.CallPath callPath : aliveCombinations.getCallPaths().getCallPaths()) { + alive.append(getCallPathString(callPath.getPath())); + alive.append(getAliveString(aliveCombinations.getEffectiveAliveAtStmt(callPath))); + alive.append(" "); + } + alive.append(")"); } - alive.append(")"); } return alive.toString(); } diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index f918d7d97..6567afc2d 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -279,19 +279,20 @@ public class TestPrograms { compileAndCompare("declared-memory-var-0"); } - /* + @Test + public void testProcedureCallingConventionStack9() throws IOException, URISyntaxException { + compileAndCompare("procedure-callingconvention-stack-9"); + } + @Test public void testProcedureCallingConventionStack8() throws IOException, URISyntaxException { - compileAndCompare("procedure-callingconvention-stack-8", log().verboseLiveRanges()); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence()); + compileAndCompare("procedure-callingconvention-stack-8"); } - */ - /* @Test public void testProcedureCallingConventionStack7() throws IOException, URISyntaxException { - compileAndCompare("procedure-callingconvention-stack-7", log().verboseLiveRanges()); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence()); + compileAndCompare("procedure-callingconvention-stack-7"); } - */ /* @Test diff --git a/src/test/kc/procedure-callingconvention-stack-7.kc b/src/test/kc/procedure-callingconvention-stack-7.kc index fa6972f4a..8abccdd7f 100644 --- a/src/test/kc/procedure-callingconvention-stack-7.kc +++ b/src/test/kc/procedure-callingconvention-stack-7.kc @@ -1,5 +1,5 @@ // Test a procedure with calling convention stack -// Illustrates live range problem with variables in different functions main::val and printline::i +// Illustrates live ranges for main::val and printline::i #pragma calling(__stackcall) #pragma var_model(ma_zp) @@ -7,8 +7,8 @@ const char* SCREEN = 0x0400; void main(void) { - char val = 0; - val = '-'; + char val; + val = *SCREEN; printline(); SCREEN[80] = val; } diff --git a/src/test/kc/procedure-callingconvention-stack-8.kc b/src/test/kc/procedure-callingconvention-stack-8.kc index e58b16489..4f07b5483 100644 --- a/src/test/kc/procedure-callingconvention-stack-8.kc +++ b/src/test/kc/procedure-callingconvention-stack-8.kc @@ -1,5 +1,5 @@ // Test a procedure with calling convention stack -// Illustrates live range problem with variable function printline::i and global variable val +// Illustrates live ranges for printline::i and global variable val #pragma calling(__stackcall) #pragma var_model(ma_zp) diff --git a/src/test/kc/procedure-callingconvention-stack-9.kc b/src/test/kc/procedure-callingconvention-stack-9.kc new file mode 100644 index 000000000..eaf323837 --- /dev/null +++ b/src/test/kc/procedure-callingconvention-stack-9.kc @@ -0,0 +1,44 @@ +// Test a procedure with calling convention stack +// Illustrates live range problem with function variable printother::i and global variable val + +#pragma calling(__stackcall) +#pragma var_model(ma_zp) + +const char* SCREEN = 0x0400; + +char val = 0; + +void main(void) { + for(char i:0..5) { + pval(); + printother(); + ival(); + } +} + +void pval() { + printval(); +} + +void ival() { + incval(); +} + +void printval() { + SCREEN[0] = val; +} + +void incval() { + val++; +} + +void printother() { + for(char i:0..5) { + (SCREEN+40)[i]++; + } +} + + + + + diff --git a/src/test/ref/procedure-callingconvention-stack-7.asm b/src/test/ref/procedure-callingconvention-stack-7.asm new file mode 100644 index 000000000..0e31fe661 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-7.asm @@ -0,0 +1,46 @@ +// Test a procedure with calling convention stack +// Illustrates live ranges for main::val and printline::i +.pc = $801 "Basic" +:BasicUpstart(__b1) +.pc = $80d "Program" + .label SCREEN = $400 +__b1: + jsr main + rts +printline: { + .label i = 2 + // i=0 + lda #0 + sta.z i + __b1: + // for(char i=0; i<40; i++) + lda.z i + cmp #$28 + bcc __b2 + // } + rts + __b2: + // SCREEN[i] = '*' + lda #'*' + ldy.z i + sta SCREEN,y + // for(char i=0; i<40; i++) + inc.z i + jmp __b1 +} +main: { + .label val = 3 + // val + lda #0 + sta.z val + // val = *SCREEN + lda SCREEN + sta.z val + // printline() + jsr printline + // SCREEN[80] = val + lda.z val + sta SCREEN+$50 + // } + rts +} diff --git a/src/test/ref/procedure-callingconvention-stack-7.cfg b/src/test/ref/procedure-callingconvention-stack-7.cfg new file mode 100644 index 000000000..e22a0d1c3 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-7.cfg @@ -0,0 +1,39 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] callprepare main + [3] callexecute main + [4] callfinalize main + to:@end +@end: scope:[] from @1 + [5] phi() + +__stackcall (void()) printline() +printline: scope:[printline] from + [6] (byte) printline::i ← (byte) 0 + to:printline::@1 +printline::@1: scope:[printline] from printline printline::@2 + [7] if((byte) printline::i<(byte) $28) goto printline::@2 + to:printline::@return +printline::@return: scope:[printline] from printline::@1 + [8] return + to:@return +printline::@2: scope:[printline] from printline::@1 + [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' + [10] (byte) printline::i ← ++ (byte) printline::i + to:printline::@1 + +__stackcall (void()) main() +main: scope:[main] from + [11] (byte) main::val ← (byte) 0 + [12] (byte) main::val ← *((const byte*) SCREEN) + [13] callprepare printline + [14] callexecute printline + [15] callfinalize printline + [16] *((const byte*) SCREEN+(byte) $50) ← (byte) main::val + to:main::@return +main::@return: scope:[main] from main + [17] return + to:@return diff --git a/src/test/ref/procedure-callingconvention-stack-7.log b/src/test/ref/procedure-callingconvention-stack-7.log new file mode 100644 index 000000000..da8e314dc --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-7.log @@ -0,0 +1,440 @@ +Culled Empty Block (label) @1 +Culled Empty Block (label) printline::@4 +Culled Empty Block (label) printline::@3 +Culled Empty Block (label) printline::@5 +Culled Empty Block (label) printline::@6 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@2 + +__stackcall (void()) main() +main: scope:[main] from + (byte) main::val ← (byte) 0 + (byte) main::val ← *((const byte*) SCREEN) + call printline + *((const byte*) SCREEN + (number) $50) ← (byte) main::val + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +__stackcall (void()) printline() +printline: scope:[printline] from + (byte) printline::i ← (byte) 0 + to:printline::@1 +printline::@1: scope:[printline] from printline printline::@2 + (bool~) printline::$0 ← (byte) printline::i < (number) $28 + if((bool~) printline::$0) goto printline::@2 + to:printline::@return +printline::@2: scope:[printline] from printline::@1 + *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' + (byte) printline::i ← ++ (byte) printline::i + to:printline::@1 +printline::@return: scope:[printline] from printline::@1 + return + to:@return +@2: scope:[] from @begin + call main + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @2 +(label) @begin +(label) @end +(const byte*) SCREEN = (byte*)(number) $400 +__stackcall (void()) main() +(label) main::@return +(byte) main::val loadstore +__stackcall (void()) printline() +(bool~) printline::$0 +(label) printline::@1 +(label) printline::@2 +(label) printline::@return +(byte) printline::i loadstore + +Adding number conversion cast (unumber) $50 in *((const byte*) SCREEN + (number) $50) ← (byte) main::val +Adding number conversion cast (unumber) $28 in (bool~) printline::$0 ← (byte) printline::i < (number) $28 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast $50 +Simplifying constant integer cast $28 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) $50 +Finalized unsigned number type (byte) $28 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simple Condition (bool~) printline::$0 [7] if((byte) printline::i<(byte) $28) goto printline::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Consolidated array index constant in *(SCREEN+$50) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:2 +Calls in [main] to printline:11 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +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 +Calling convention STACK_CALL adding prepare/execute/finalize for [2] call main +Calling convention STACK_CALL adding prepare/execute/finalize for [11] call printline + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] callprepare main + [3] callexecute main + [4] callfinalize main + to:@end +@end: scope:[] from @1 + [5] phi() + +__stackcall (void()) printline() +printline: scope:[printline] from + [6] (byte) printline::i ← (byte) 0 + to:printline::@1 +printline::@1: scope:[printline] from printline printline::@2 + [7] if((byte) printline::i<(byte) $28) goto printline::@2 + to:printline::@return +printline::@return: scope:[printline] from printline::@1 + [8] return + to:@return +printline::@2: scope:[printline] from printline::@1 + [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' + [10] (byte) printline::i ← ++ (byte) printline::i + to:printline::@1 + +__stackcall (void()) main() +main: scope:[main] from + [11] (byte) main::val ← (byte) 0 + [12] (byte) main::val ← *((const byte*) SCREEN) + [13] callprepare printline + [14] callexecute printline + [15] callfinalize printline + [16] *((const byte*) SCREEN+(byte) $50) ← (byte) main::val + to:main::@return +main::@return: scope:[main] from main + [17] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__stackcall (void()) main() +(byte) main::val loadstore 1.5 +__stackcall (void()) printline() +(byte) printline::i loadstore 11.5 + +Initial phi equivalence classes +Added variable printline::i to live range equivalence class [ printline::i ] +Added variable main::val to live range equivalence class [ main::val ] +Complete equivalence classes +[ printline::i ] +[ main::val ] +Allocated zp[1]:2 [ printline::i ] +Allocated zp[1]:3 [ main::val ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Test a procedure with calling convention stack +// Illustrates live ranges for main::val and printline::i + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] callprepare main + // [3] callexecute main -- jsr + jsr main + // [4] callfinalize main + // [5] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // printline +printline: { + .label i = 2 + // [6] (byte) printline::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b1 + // printline::@1 + __b1: + // [7] if((byte) printline::i<(byte) $28) goto printline::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$28 + bcc __b2 + jmp __breturn + // printline::@return + __breturn: + // [8] return + rts + // printline::@2 + __b2: + // [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' -- pbuc1_derefidx_vbuz1=vbuc2 + lda #'*' + ldy.z i + sta SCREEN,y + // [10] (byte) printline::i ← ++ (byte) printline::i -- vbuz1=_inc_vbuz1 + inc.z i + jmp __b1 +} + // main +main: { + .label val = 3 + // [11] (byte) main::val ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z val + // [12] (byte) main::val ← *((const byte*) SCREEN) -- vbuz1=_deref_pbuc1 + lda SCREEN + sta.z val + // [13] callprepare printline + // [14] callexecute printline -- jsr + jsr printline + // [15] callfinalize printline + // [16] *((const byte*) SCREEN+(byte) $50) ← (byte) main::val -- _deref_pbuc1=vbuz1 + lda.z val + sta SCREEN+$50 + jmp __breturn + // main::@return + __breturn: + // [17] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] (byte) printline::i ← (byte) 0 [ printline::i ] ( main:3::printline:14 [ main::val printline::i ] ) always clobbers reg byte a +Statement [7] if((byte) printline::i<(byte) $28) goto printline::@2 [ printline::i ] ( main:3::printline:14 [ main::val printline::i ] ) always clobbers reg byte a +Statement [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' [ printline::i ] ( main:3::printline:14 [ main::val printline::i ] ) always clobbers reg byte a reg byte y +Statement [11] (byte) main::val ← (byte) 0 [ ] ( main:3 [ ] ) always clobbers reg byte a +Statement [12] (byte) main::val ← *((const byte*) SCREEN) [ main::val ] ( main:3 [ main::val ] ) always clobbers reg byte a +Statement [16] *((const byte*) SCREEN+(byte) $50) ← (byte) main::val [ ] ( main:3 [ ] ) always clobbers reg byte a +Potential registers zp[1]:2 [ printline::i ] : zp[1]:2 , +Potential registers zp[1]:3 [ main::val ] : zp[1]:3 , + +REGISTER UPLIFT SCOPES +Uplift Scope [printline] 11.5: zp[1]:2 [ printline::i ] +Uplift Scope [main] 1.5: zp[1]:3 [ main::val ] +Uplift Scope [] + +Uplifting [printline] best 345 combination zp[1]:2 [ printline::i ] +Uplifting [main] best 345 combination zp[1]:3 [ main::val ] +Uplifting [] best 345 combination +Attempting to uplift remaining variables inzp[1]:2 [ printline::i ] +Uplifting [printline] best 345 combination zp[1]:2 [ printline::i ] +Attempting to uplift remaining variables inzp[1]:3 [ main::val ] +Uplifting [main] best 345 combination zp[1]:3 [ main::val ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test a procedure with calling convention stack +// Illustrates live ranges for main::val and printline::i + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] callprepare main + // [3] callexecute main -- jsr + jsr main + // [4] callfinalize main + // [5] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // printline +printline: { + .label i = 2 + // [6] (byte) printline::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b1 + // printline::@1 + __b1: + // [7] if((byte) printline::i<(byte) $28) goto printline::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$28 + bcc __b2 + jmp __breturn + // printline::@return + __breturn: + // [8] return + rts + // printline::@2 + __b2: + // [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' -- pbuc1_derefidx_vbuz1=vbuc2 + lda #'*' + ldy.z i + sta SCREEN,y + // [10] (byte) printline::i ← ++ (byte) printline::i -- vbuz1=_inc_vbuz1 + inc.z i + jmp __b1 +} + // main +main: { + .label val = 3 + // [11] (byte) main::val ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z val + // [12] (byte) main::val ← *((const byte*) SCREEN) -- vbuz1=_deref_pbuc1 + lda SCREEN + sta.z val + // [13] callprepare printline + // [14] callexecute printline -- jsr + jsr printline + // [15] callfinalize printline + // [16] *((const byte*) SCREEN+(byte) $50) ← (byte) main::val -- _deref_pbuc1=vbuz1 + lda.z val + sta SCREEN+$50 + jmp __breturn + // main::@return + __breturn: + // [17] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label __bbegin with __b1 +Removing instruction __bbegin: +Removing instruction __b1_from___bbegin: +Removing instruction __bend_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __bend: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Adding RTS to root block +Succesful ASM optimization Pass5AddMainRts + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const byte*) SCREEN = (byte*) 1024 +__stackcall (void()) main() +(label) main::@return +(byte) main::val loadstore zp[1]:3 1.5 +__stackcall (void()) printline() +(label) printline::@1 +(label) printline::@2 +(label) printline::@return +(byte) printline::i loadstore zp[1]:2 11.5 + +zp[1]:2 [ printline::i ] +zp[1]:3 [ main::val ] + + +FINAL ASSEMBLER +Score: 309 + + // File Comments +// Test a procedure with calling convention stack +// Illustrates live ranges for main::val and printline::i + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__b1) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 +__b1: + // [2] callprepare main + // [3] callexecute main -- jsr + jsr main + rts + // [4] callfinalize main + // [5] phi from @1 to @end [phi:@1->@end] + // @end + // printline +printline: { + .label i = 2 + // i=0 + // [6] (byte) printline::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + // printline::@1 + __b1: + // for(char i=0; i<40; i++) + // [7] if((byte) printline::i<(byte) $28) goto printline::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$28 + bcc __b2 + // printline::@return + // } + // [8] return + rts + // printline::@2 + __b2: + // SCREEN[i] = '*' + // [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' -- pbuc1_derefidx_vbuz1=vbuc2 + lda #'*' + ldy.z i + sta SCREEN,y + // for(char i=0; i<40; i++) + // [10] (byte) printline::i ← ++ (byte) printline::i -- vbuz1=_inc_vbuz1 + inc.z i + jmp __b1 +} + // main +main: { + .label val = 3 + // val + // [11] (byte) main::val ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z val + // val = *SCREEN + // [12] (byte) main::val ← *((const byte*) SCREEN) -- vbuz1=_deref_pbuc1 + lda SCREEN + sta.z val + // printline() + // [13] callprepare printline + // [14] callexecute printline -- jsr + jsr printline + // [15] callfinalize printline + // SCREEN[80] = val + // [16] *((const byte*) SCREEN+(byte) $50) ← (byte) main::val -- _deref_pbuc1=vbuz1 + lda.z val + sta SCREEN+$50 + // main::@return + // } + // [17] return + rts +} + // File Data + diff --git a/src/test/ref/procedure-callingconvention-stack-7.sym b/src/test/ref/procedure-callingconvention-stack-7.sym new file mode 100644 index 000000000..77a3a89c6 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-7.sym @@ -0,0 +1,15 @@ +(label) @1 +(label) @begin +(label) @end +(const byte*) SCREEN = (byte*) 1024 +__stackcall (void()) main() +(label) main::@return +(byte) main::val loadstore zp[1]:3 1.5 +__stackcall (void()) printline() +(label) printline::@1 +(label) printline::@2 +(label) printline::@return +(byte) printline::i loadstore zp[1]:2 11.5 + +zp[1]:2 [ printline::i ] +zp[1]:3 [ main::val ] diff --git a/src/test/ref/procedure-callingconvention-stack-8.asm b/src/test/ref/procedure-callingconvention-stack-8.asm new file mode 100644 index 000000000..f56e19964 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-8.asm @@ -0,0 +1,46 @@ +// Test a procedure with calling convention stack +// Illustrates live ranges for printline::i and global variable val +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + .label SCREEN = $400 + .label val = 2 +__bbegin: + // val = 0 + lda #0 + sta.z val + jsr main + rts +printline: { + .label i = 3 + // i=0 + lda #0 + sta.z i + __b1: + // for(char i=0; i<40; i++) + lda.z i + cmp #$28 + bcc __b2 + // } + rts + __b2: + // SCREEN[i] = '*' + lda #'*' + ldy.z i + sta SCREEN,y + // for(char i=0; i<40; i++) + inc.z i + jmp __b1 +} +main: { + // val = '-' + lda #'-' + sta.z val + // printline() + jsr printline + // SCREEN[80] = val + lda.z val + sta SCREEN+$50 + // } + rts +} diff --git a/src/test/ref/procedure-callingconvention-stack-8.cfg b/src/test/ref/procedure-callingconvention-stack-8.cfg new file mode 100644 index 000000000..05acb0255 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-8.cfg @@ -0,0 +1,38 @@ +@begin: scope:[] from + [0] (byte) val ← (byte) 0 + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] callprepare main + [3] callexecute main + [4] callfinalize main + to:@end +@end: scope:[] from @1 + [5] phi() + +__stackcall (void()) printline() +printline: scope:[printline] from + [6] (byte) printline::i ← (byte) 0 + to:printline::@1 +printline::@1: scope:[printline] from printline printline::@2 + [7] if((byte) printline::i<(byte) $28) goto printline::@2 + to:printline::@return +printline::@return: scope:[printline] from printline::@1 + [8] return + to:@return +printline::@2: scope:[printline] from printline::@1 + [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' + [10] (byte) printline::i ← ++ (byte) printline::i + to:printline::@1 + +__stackcall (void()) main() +main: scope:[main] from + [11] (byte) val ← (byte) '-' + [12] callprepare printline + [13] callexecute printline + [14] callfinalize printline + [15] *((const byte*) SCREEN+(byte) $50) ← (byte) val + to:main::@return +main::@return: scope:[main] from main + [16] return + to:@return diff --git a/src/test/ref/procedure-callingconvention-stack-8.log b/src/test/ref/procedure-callingconvention-stack-8.log new file mode 100644 index 000000000..10f3ebf9a --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-8.log @@ -0,0 +1,436 @@ +Culled Empty Block (label) @1 +Culled Empty Block (label) printline::@4 +Culled Empty Block (label) printline::@3 +Culled Empty Block (label) printline::@5 +Culled Empty Block (label) printline::@6 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte) val ← (byte) 0 + to:@2 + +__stackcall (void()) main() +main: scope:[main] from + (byte) val ← (byte) '-' + call printline + *((const byte*) SCREEN + (number) $50) ← (byte) val + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +__stackcall (void()) printline() +printline: scope:[printline] from + (byte) printline::i ← (byte) 0 + to:printline::@1 +printline::@1: scope:[printline] from printline printline::@2 + (bool~) printline::$0 ← (byte) printline::i < (number) $28 + if((bool~) printline::$0) goto printline::@2 + to:printline::@return +printline::@2: scope:[printline] from printline::@1 + *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' + (byte) printline::i ← ++ (byte) printline::i + to:printline::@1 +printline::@return: scope:[printline] from printline::@1 + return + to:@return +@2: scope:[] from @begin + call main + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @2 +(label) @begin +(label) @end +(const byte*) SCREEN = (byte*)(number) $400 +__stackcall (void()) main() +(label) main::@return +__stackcall (void()) printline() +(bool~) printline::$0 +(label) printline::@1 +(label) printline::@2 +(label) printline::@return +(byte) printline::i loadstore +(byte) val loadstore + +Adding number conversion cast (unumber) $50 in *((const byte*) SCREEN + (number) $50) ← (byte) val +Adding number conversion cast (unumber) $28 in (bool~) printline::$0 ← (byte) printline::i < (number) $28 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast $50 +Simplifying constant integer cast $28 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) $50 +Finalized unsigned number type (byte) $28 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simple Condition (bool~) printline::$0 [7] if((byte) printline::i<(byte) $28) goto printline::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Consolidated array index constant in *(SCREEN+$50) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:2 +Calls in [main] to printline:10 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Renumbering block @2 to @1 +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Calling convention STACK_CALL adding prepare/execute/finalize for [2] call main +Calling convention STACK_CALL adding prepare/execute/finalize for [10] call printline + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] (byte) val ← (byte) 0 + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] callprepare main + [3] callexecute main + [4] callfinalize main + to:@end +@end: scope:[] from @1 + [5] phi() + +__stackcall (void()) printline() +printline: scope:[printline] from + [6] (byte) printline::i ← (byte) 0 + to:printline::@1 +printline::@1: scope:[printline] from printline printline::@2 + [7] if((byte) printline::i<(byte) $28) goto printline::@2 + to:printline::@return +printline::@return: scope:[printline] from printline::@1 + [8] return + to:@return +printline::@2: scope:[printline] from printline::@1 + [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' + [10] (byte) printline::i ← ++ (byte) printline::i + to:printline::@1 + +__stackcall (void()) main() +main: scope:[main] from + [11] (byte) val ← (byte) '-' + [12] callprepare printline + [13] callexecute printline + [14] callfinalize printline + [15] *((const byte*) SCREEN+(byte) $50) ← (byte) val + to:main::@return +main::@return: scope:[main] from main + [16] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__stackcall (void()) main() +__stackcall (void()) printline() +(byte) printline::i loadstore 11.5 +(byte) val loadstore 1.5 + +Initial phi equivalence classes +Added variable val to live range equivalence class [ val ] +Added variable printline::i to live range equivalence class [ printline::i ] +Complete equivalence classes +[ val ] +[ printline::i ] +Allocated zp[1]:2 [ val ] +Allocated zp[1]:3 [ printline::i ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Test a procedure with calling convention stack +// Illustrates live ranges for printline::i and global variable val + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label val = 2 + // @begin +__bbegin: + // [0] (byte) val ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z val + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] callprepare main + // [3] callexecute main -- jsr + jsr main + // [4] callfinalize main + // [5] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // printline +printline: { + .label i = 3 + // [6] (byte) printline::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b1 + // printline::@1 + __b1: + // [7] if((byte) printline::i<(byte) $28) goto printline::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$28 + bcc __b2 + jmp __breturn + // printline::@return + __breturn: + // [8] return + rts + // printline::@2 + __b2: + // [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' -- pbuc1_derefidx_vbuz1=vbuc2 + lda #'*' + ldy.z i + sta SCREEN,y + // [10] (byte) printline::i ← ++ (byte) printline::i -- vbuz1=_inc_vbuz1 + inc.z i + jmp __b1 +} + // main +main: { + // [11] (byte) val ← (byte) '-' -- vbuz1=vbuc1 + lda #'-' + sta.z val + // [12] callprepare printline + // [13] callexecute printline -- jsr + jsr printline + // [14] callfinalize printline + // [15] *((const byte*) SCREEN+(byte) $50) ← (byte) val -- _deref_pbuc1=vbuz1 + lda.z val + sta SCREEN+$50 + jmp __breturn + // main::@return + __breturn: + // [16] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] (byte) val ← (byte) 0 [ ] ( [ ] ) always clobbers reg byte a +Statement [6] (byte) printline::i ← (byte) 0 [ printline::i ] ( main:3::printline:13 [ val printline::i ] ) always clobbers reg byte a +Statement [7] if((byte) printline::i<(byte) $28) goto printline::@2 [ printline::i ] ( main:3::printline:13 [ val printline::i ] ) always clobbers reg byte a +Statement [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' [ printline::i ] ( main:3::printline:13 [ val printline::i ] ) always clobbers reg byte a reg byte y +Statement [11] (byte) val ← (byte) '-' [ val ] ( main:3 [ val ] ) always clobbers reg byte a +Statement [15] *((const byte*) SCREEN+(byte) $50) ← (byte) val [ ] ( main:3 [ ] ) always clobbers reg byte a +Potential registers zp[1]:2 [ val ] : zp[1]:2 , +Potential registers zp[1]:3 [ printline::i ] : zp[1]:3 , + +REGISTER UPLIFT SCOPES +Uplift Scope [printline] 11.5: zp[1]:3 [ printline::i ] +Uplift Scope [] 1.5: zp[1]:2 [ val ] +Uplift Scope [main] + +Uplifting [printline] best 343 combination zp[1]:3 [ printline::i ] +Uplifting [] best 343 combination zp[1]:2 [ val ] +Uplifting [main] best 343 combination +Attempting to uplift remaining variables inzp[1]:3 [ printline::i ] +Uplifting [printline] best 343 combination zp[1]:3 [ printline::i ] +Attempting to uplift remaining variables inzp[1]:2 [ val ] +Uplifting [] best 343 combination zp[1]:2 [ val ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test a procedure with calling convention stack +// Illustrates live ranges for printline::i and global variable val + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label val = 2 + // @begin +__bbegin: + // [0] (byte) val ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z val + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] callprepare main + // [3] callexecute main -- jsr + jsr main + // [4] callfinalize main + // [5] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // printline +printline: { + .label i = 3 + // [6] (byte) printline::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b1 + // printline::@1 + __b1: + // [7] if((byte) printline::i<(byte) $28) goto printline::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$28 + bcc __b2 + jmp __breturn + // printline::@return + __breturn: + // [8] return + rts + // printline::@2 + __b2: + // [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' -- pbuc1_derefidx_vbuz1=vbuc2 + lda #'*' + ldy.z i + sta SCREEN,y + // [10] (byte) printline::i ← ++ (byte) printline::i -- vbuz1=_inc_vbuz1 + inc.z i + jmp __b1 +} + // main +main: { + // [11] (byte) val ← (byte) '-' -- vbuz1=vbuc1 + lda #'-' + sta.z val + // [12] callprepare printline + // [13] callexecute printline -- jsr + jsr printline + // [14] callfinalize printline + // [15] *((const byte*) SCREEN+(byte) $50) ← (byte) val -- _deref_pbuc1=vbuz1 + lda.z val + sta SCREEN+$50 + jmp __breturn + // main::@return + __breturn: + // [16] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b1_from___bbegin: +Removing instruction __bend_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __b1: +Removing instruction __bend: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Adding RTS to root block +Succesful ASM optimization Pass5AddMainRts + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const byte*) SCREEN = (byte*) 1024 +__stackcall (void()) main() +(label) main::@return +__stackcall (void()) printline() +(label) printline::@1 +(label) printline::@2 +(label) printline::@return +(byte) printline::i loadstore zp[1]:3 11.5 +(byte) val loadstore zp[1]:2 1.5 + +zp[1]:2 [ val ] +zp[1]:3 [ printline::i ] + + +FINAL ASSEMBLER +Score: 307 + + // File Comments +// Test a procedure with calling convention stack +// Illustrates live ranges for printline::i and global variable val + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label val = 2 + // @begin +__bbegin: + // val = 0 + // [0] (byte) val ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z val + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] callprepare main + // [3] callexecute main -- jsr + jsr main + rts + // [4] callfinalize main + // [5] phi from @1 to @end [phi:@1->@end] + // @end + // printline +printline: { + .label i = 3 + // i=0 + // [6] (byte) printline::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + // printline::@1 + __b1: + // for(char i=0; i<40; i++) + // [7] if((byte) printline::i<(byte) $28) goto printline::@2 -- vbuz1_lt_vbuc1_then_la1 + lda.z i + cmp #$28 + bcc __b2 + // printline::@return + // } + // [8] return + rts + // printline::@2 + __b2: + // SCREEN[i] = '*' + // [9] *((const byte*) SCREEN + (byte) printline::i) ← (byte) '*' -- pbuc1_derefidx_vbuz1=vbuc2 + lda #'*' + ldy.z i + sta SCREEN,y + // for(char i=0; i<40; i++) + // [10] (byte) printline::i ← ++ (byte) printline::i -- vbuz1=_inc_vbuz1 + inc.z i + jmp __b1 +} + // main +main: { + // val = '-' + // [11] (byte) val ← (byte) '-' -- vbuz1=vbuc1 + lda #'-' + sta.z val + // printline() + // [12] callprepare printline + // [13] callexecute printline -- jsr + jsr printline + // [14] callfinalize printline + // SCREEN[80] = val + // [15] *((const byte*) SCREEN+(byte) $50) ← (byte) val -- _deref_pbuc1=vbuz1 + lda.z val + sta SCREEN+$50 + // main::@return + // } + // [16] return + rts +} + // File Data + diff --git a/src/test/ref/procedure-callingconvention-stack-8.sym b/src/test/ref/procedure-callingconvention-stack-8.sym new file mode 100644 index 000000000..46f85cb55 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-8.sym @@ -0,0 +1,15 @@ +(label) @1 +(label) @begin +(label) @end +(const byte*) SCREEN = (byte*) 1024 +__stackcall (void()) main() +(label) main::@return +__stackcall (void()) printline() +(label) printline::@1 +(label) printline::@2 +(label) printline::@return +(byte) printline::i loadstore zp[1]:3 11.5 +(byte) val loadstore zp[1]:2 1.5 + +zp[1]:2 [ val ] +zp[1]:3 [ printline::i ] diff --git a/src/test/ref/procedure-callingconvention-stack-9.asm b/src/test/ref/procedure-callingconvention-stack-9.asm new file mode 100644 index 000000000..8b1322c9b --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-9.asm @@ -0,0 +1,75 @@ +// Test a procedure with calling convention stack +// Illustrates live range problem with function variable printother::i and global variable val +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + .label SCREEN = $400 + .label val = 2 +__bbegin: + // val = 0 + lda #0 + sta.z val + jsr main + rts +printother: { + .label i = 2 + // for(char i:0..5) + lda #0 + sta.z i + __b1: + // (SCREEN+40)[i]++; + ldx.z i + inc SCREEN+$28,x + // for(char i:0..5) + inc.z i + lda #6 + cmp.z i + bne __b1 + // } + rts +} +incval: { + // val++; + inc.z val + // } + rts +} +printval: { + // SCREEN[0] = val + lda.z val + sta SCREEN + // } + rts +} +ival: { + // incval() + jsr incval + // } + rts +} +pval: { + // printval() + jsr printval + // } + rts +} +main: { + .label i = 3 + // for(char i:0..5) + lda #0 + sta.z i + __b1: + // pval() + jsr pval + // printother() + jsr printother + // ival() + jsr ival + // for(char i:0..5) + inc.z i + lda #6 + cmp.z i + bne __b1 + // } + rts +} diff --git a/src/test/ref/procedure-callingconvention-stack-9.cfg b/src/test/ref/procedure-callingconvention-stack-9.cfg new file mode 100644 index 000000000..a016ad6ac --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-9.cfg @@ -0,0 +1,84 @@ +@begin: scope:[] from + [0] (byte) val ← (byte) 0 + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] callprepare main + [3] callexecute main + [4] callfinalize main + to:@end +@end: scope:[] from @1 + [5] phi() + +__stackcall (void()) printother() +printother: scope:[printother] from + [6] (byte) printother::i ← (byte) 0 + to:printother::@1 +printother::@1: scope:[printother] from printother printother::@1 + [7] *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) ← ++ *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) + [8] (byte) printother::i ← ++ (byte) printother::i + [9] if((byte) printother::i!=(byte) 6) goto printother::@1 + to:printother::@return +printother::@return: scope:[printother] from printother::@1 + [10] return + to:@return + +__stackcall (void()) incval() +incval: scope:[incval] from + [11] (byte) val ← ++ (byte) val + to:incval::@return +incval::@return: scope:[incval] from incval + [12] return + to:@return + +__stackcall (void()) printval() +printval: scope:[printval] from + [13] *((const byte*) SCREEN) ← (byte) val + to:printval::@return +printval::@return: scope:[printval] from printval + [14] return + to:@return + +__stackcall (void()) ival() +ival: scope:[ival] from + [15] phi() + [16] callprepare incval + [17] callexecute incval + [18] callfinalize incval + to:ival::@return +ival::@return: scope:[ival] from ival + [19] return + to:@return + +__stackcall (void()) pval() +pval: scope:[pval] from + [20] phi() + [21] callprepare printval + [22] callexecute printval + [23] callfinalize printval + to:pval::@return +pval::@return: scope:[pval] from pval + [24] return + to:@return + +__stackcall (void()) main() +main: scope:[main] from + [25] (byte) main::i ← (byte) 0 + to:main::@1 +main::@1: scope:[main] from main main::@1 + [26] phi() + [27] callprepare pval + [28] callexecute pval + [29] callfinalize pval + [30] callprepare printother + [31] callexecute printother + [32] callfinalize printother + [33] callprepare ival + [34] callexecute ival + [35] callfinalize ival + [36] (byte) main::i ← ++ (byte) main::i + [37] if((byte) main::i!=(byte) 6) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 + [38] return + to:@return diff --git a/src/test/ref/procedure-callingconvention-stack-9.log b/src/test/ref/procedure-callingconvention-stack-9.log new file mode 100644 index 000000000..1a94ba612 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-9.log @@ -0,0 +1,780 @@ +Culled Empty Block (label) main::@2 +Culled Empty Block (label) @1 +Culled Empty Block (label) @2 +Culled Empty Block (label) @3 +Culled Empty Block (label) @4 +Culled Empty Block (label) @5 +Culled Empty Block (label) printother::@2 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte) val ← (byte) 0 + to:@6 + +__stackcall (void()) main() +main: scope:[main] from + (byte) main::i ← (byte) 0 + to:main::@1 +main::@1: scope:[main] from main main::@1 + call pval + call printother + call ival + (byte) main::i ← (byte) main::i + rangenext(0,5) + (bool~) main::$3 ← (byte) main::i != rangelast(0,5) + if((bool~) main::$3) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 + return + to:@return + +__stackcall (void()) pval() +pval: scope:[pval] from + call printval + to:pval::@return +pval::@return: scope:[pval] from pval + return + to:@return + +__stackcall (void()) ival() +ival: scope:[ival] from + call incval + to:ival::@return +ival::@return: scope:[ival] from ival + return + to:@return + +__stackcall (void()) printval() +printval: scope:[printval] from + *((const byte*) SCREEN + (number) 0) ← (byte) val + to:printval::@return +printval::@return: scope:[printval] from printval + return + to:@return + +__stackcall (void()) incval() +incval: scope:[incval] from + (byte) val ← ++ (byte) val + to:incval::@return +incval::@return: scope:[incval] from incval + return + to:@return + +__stackcall (void()) printother() +printother: scope:[printother] from + (byte) printother::i ← (byte) 0 + to:printother::@1 +printother::@1: scope:[printother] from printother printother::@1 + *((const byte*) SCREEN+(number) $28 + (byte) printother::i) ← ++ *((const byte*) SCREEN+(number) $28 + (byte) printother::i) + (byte) printother::i ← (byte) printother::i + rangenext(0,5) + (bool~) printother::$1 ← (byte) printother::i != rangelast(0,5) + if((bool~) printother::$1) goto printother::@1 + to:printother::@return +printother::@return: scope:[printother] from printother::@1 + return + to:@return +@6: scope:[] from @begin + call main + to:@end +@end: scope:[] from @6 + +SYMBOL TABLE SSA +(label) @6 +(label) @begin +(label) @end +(const byte*) SCREEN = (byte*)(number) $400 +__stackcall (void()) incval() +(label) incval::@return +__stackcall (void()) ival() +(label) ival::@return +__stackcall (void()) main() +(bool~) main::$3 +(label) main::@1 +(label) main::@return +(byte) main::i loadstore +__stackcall (void()) printother() +(bool~) printother::$1 +(label) printother::@1 +(label) printother::@return +(byte) printother::i loadstore +__stackcall (void()) printval() +(label) printval::@return +__stackcall (void()) pval() +(label) pval::@return +(byte) val loadstore + +Adding number conversion cast (unumber) 0 in *((const byte*) SCREEN + (number) 0) ← (byte) val +Adding number conversion cast (unumber) $28 in *((const byte*) SCREEN+(number) $28 + (byte) printother::i) ← ++ *((const byte*) SCREEN+(number) $28 + (byte) printother::i) +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast $28 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $28 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simple Condition (bool~) main::$3 [7] if((byte) main::i!=rangelast(0,5)) goto main::@1 +Simple Condition (bool~) printother::$1 [21] if((byte) printother::i!=rangelast(0,5)) goto printother::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Resolved ranged next value [5] main::i ← ++ main::i to ++ +Resolved ranged comparison value [7] if(main::i!=rangelast(0,5)) goto main::@1 to (number) 6 +Resolved ranged next value [19] printother::i ← ++ printother::i to ++ +Resolved ranged comparison value [21] if(printother::i!=rangelast(0,5)) goto printother::@1 to (number) 6 +Simplifying expression containing zero SCREEN in [13] *((const byte*) SCREEN + (byte) 0) ← (byte) val +Successful SSA optimization PassNSimplifyExpressionWithZero +Adding number conversion cast (unumber) 6 in if((byte) main::i!=(number) 6) goto main::@1 +Adding number conversion cast (unumber) 6 in if((byte) printother::i!=(number) 6) goto printother::@1 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 6 +Simplifying constant integer cast 6 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 6 +Finalized unsigned number type (byte) 6 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Adding NOP phi() at start of @6 +Adding NOP phi() at start of @end +Adding NOP phi() at start of ival +Adding NOP phi() at start of pval +Adding NOP phi() at start of main::@1 +CALL GRAPH +Calls in [] to main:2 +Calls in [ival] to incval:14 +Calls in [pval] to printval:17 +Calls in [main] to pval:21 printother:22 ival:23 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Renumbering block @6 to @1 +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of ival +Adding NOP phi() at start of pval +Adding NOP phi() at start of main::@1 +Calling convention STACK_CALL adding prepare/execute/finalize for [2] call main +Calling convention STACK_CALL adding prepare/execute/finalize for [14] call incval +Calling convention STACK_CALL adding prepare/execute/finalize for [17] call printval +Calling convention STACK_CALL adding prepare/execute/finalize for [21] call pval +Calling convention STACK_CALL adding prepare/execute/finalize for [22] call printother +Calling convention STACK_CALL adding prepare/execute/finalize for [23] call ival + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] (byte) val ← (byte) 0 + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] callprepare main + [3] callexecute main + [4] callfinalize main + to:@end +@end: scope:[] from @1 + [5] phi() + +__stackcall (void()) printother() +printother: scope:[printother] from + [6] (byte) printother::i ← (byte) 0 + to:printother::@1 +printother::@1: scope:[printother] from printother printother::@1 + [7] *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) ← ++ *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) + [8] (byte) printother::i ← ++ (byte) printother::i + [9] if((byte) printother::i!=(byte) 6) goto printother::@1 + to:printother::@return +printother::@return: scope:[printother] from printother::@1 + [10] return + to:@return + +__stackcall (void()) incval() +incval: scope:[incval] from + [11] (byte) val ← ++ (byte) val + to:incval::@return +incval::@return: scope:[incval] from incval + [12] return + to:@return + +__stackcall (void()) printval() +printval: scope:[printval] from + [13] *((const byte*) SCREEN) ← (byte) val + to:printval::@return +printval::@return: scope:[printval] from printval + [14] return + to:@return + +__stackcall (void()) ival() +ival: scope:[ival] from + [15] phi() + [16] callprepare incval + [17] callexecute incval + [18] callfinalize incval + to:ival::@return +ival::@return: scope:[ival] from ival + [19] return + to:@return + +__stackcall (void()) pval() +pval: scope:[pval] from + [20] phi() + [21] callprepare printval + [22] callexecute printval + [23] callfinalize printval + to:pval::@return +pval::@return: scope:[pval] from pval + [24] return + to:@return + +__stackcall (void()) main() +main: scope:[main] from + [25] (byte) main::i ← (byte) 0 + to:main::@1 +main::@1: scope:[main] from main main::@1 + [26] phi() + [27] callprepare pval + [28] callexecute pval + [29] callfinalize pval + [30] callprepare printother + [31] callexecute printother + [32] callfinalize printother + [33] callprepare ival + [34] callexecute ival + [35] callfinalize ival + [36] (byte) main::i ← ++ (byte) main::i + [37] if((byte) main::i!=(byte) 6) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 + [38] return + to:@return + +null depth in calling loop Loop head: main::@1 tails: main::@1 blocks: main::@1 in scope printother +null depth in calling loop Loop head: main::@1 tails: main::@1 blocks: main::@1 in scope ival +null depth in calling loop Loop head: main::@1 tails: main::@1 blocks: main::@1 in scope pval +null depth in calling loop Loop head: main::@1 tails: main::@1 blocks: main::@1 in scope ival +null depth in calling loop Loop head: main::@1 tails: main::@1 blocks: main::@1 in scope pval + +VARIABLE REGISTER WEIGHTS +__stackcall (void()) incval() +__stackcall (void()) ival() +__stackcall (void()) main() +(byte) main::i loadstore 2.6923076923076925 +__stackcall (void()) printother() +(byte) printother::i loadstore 14.25 +__stackcall (void()) printval() +__stackcall (void()) pval() +(byte) val loadstore 2.0 + +Initial phi equivalence classes +Added variable val to live range equivalence class [ val ] +Added variable printother::i to live range equivalence class [ printother::i ] +Added variable main::i to live range equivalence class [ main::i ] +Complete equivalence classes +[ val ] +[ printother::i ] +[ main::i ] +Allocated zp[1]:2 [ val ] +Allocated zp[1]:3 [ printother::i ] +Allocated zp[1]:4 [ main::i ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Test a procedure with calling convention stack +// Illustrates live range problem with function variable printother::i and global variable val + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label val = 2 + // @begin +__bbegin: + // [0] (byte) val ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z val + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] callprepare main + // [3] callexecute main -- jsr + jsr main + // [4] callfinalize main + // [5] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // printother +printother: { + .label i = 3 + // [6] (byte) printother::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b1 + // printother::@1 + __b1: + // [7] *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) ← ++ *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) -- pbuc1_derefidx_vbuz1=_inc_pbuc1_derefidx_vbuz1 + ldx.z i + inc SCREEN+$28,x + // [8] (byte) printother::i ← ++ (byte) printother::i -- vbuz1=_inc_vbuz1 + inc.z i + // [9] if((byte) printother::i!=(byte) 6) goto printother::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #6 + cmp.z i + bne __b1 + jmp __breturn + // printother::@return + __breturn: + // [10] return + rts +} + // incval +incval: { + // [11] (byte) val ← ++ (byte) val -- vbuz1=_inc_vbuz1 + inc.z val + jmp __breturn + // incval::@return + __breturn: + // [12] return + rts +} + // printval +printval: { + // [13] *((const byte*) SCREEN) ← (byte) val -- _deref_pbuc1=vbuz1 + lda.z val + sta SCREEN + jmp __breturn + // printval::@return + __breturn: + // [14] return + rts +} + // ival +ival: { + // [16] callprepare incval + // [17] callexecute incval -- jsr + jsr incval + // [18] callfinalize incval + jmp __breturn + // ival::@return + __breturn: + // [19] return + rts +} + // pval +pval: { + // [21] callprepare printval + // [22] callexecute printval -- jsr + jsr printval + // [23] callfinalize printval + jmp __breturn + // pval::@return + __breturn: + // [24] return + rts +} + // main +main: { + .label i = 4 + // [25] (byte) main::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + // [26] phi from main main::@1 to main::@1 [phi:main/main::@1->main::@1] + __b1_from_main: + __b1_from___b1: + jmp __b1 + // main::@1 + __b1: + // [27] callprepare pval + // [28] callexecute pval -- jsr + jsr pval + // [29] callfinalize pval + // [30] callprepare printother + // [31] callexecute printother -- jsr + jsr printother + // [32] callfinalize printother + // [33] callprepare ival + // [34] callexecute ival -- jsr + jsr ival + // [35] callfinalize ival + // [36] (byte) main::i ← ++ (byte) main::i -- vbuz1=_inc_vbuz1 + inc.z i + // [37] if((byte) main::i!=(byte) 6) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #6 + cmp.z i + bne __b1_from___b1 + jmp __breturn + // main::@return + __breturn: + // [38] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] (byte) val ← (byte) 0 [ ] ( [ ] ) always clobbers reg byte a +Statement [6] (byte) printother::i ← (byte) 0 [ printother::i ] ( main:3::printother:31 [ main::i printother::i ] ) always clobbers reg byte a +Statement [7] *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) ← ++ *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) [ printother::i ] ( main:3::printother:31 [ main::i printother::i ] ) always clobbers reg byte x +Statement [9] if((byte) printother::i!=(byte) 6) goto printother::@1 [ printother::i ] ( main:3::printother:31 [ main::i printother::i ] ) always clobbers reg byte a +Statement [13] *((const byte*) SCREEN) ← (byte) val [ ] ( main:3::pval:28::printval:22 [ main::i ] ) always clobbers reg byte a +Statement [25] (byte) main::i ← (byte) 0 [ main::i ] ( main:3 [ main::i ] ) always clobbers reg byte a +Statement [37] if((byte) main::i!=(byte) 6) goto main::@1 [ main::i ] ( main:3 [ main::i ] ) always clobbers reg byte a +Potential registers zp[1]:2 [ val ] : zp[1]:2 , +Potential registers zp[1]:3 [ printother::i ] : zp[1]:3 , +Potential registers zp[1]:4 [ main::i ] : zp[1]:4 , + +REGISTER UPLIFT SCOPES +Uplift Scope [printother] 14.25: zp[1]:3 [ printother::i ] +Uplift Scope [main] 2.69: zp[1]:4 [ main::i ] +Uplift Scope [] 2: zp[1]:2 [ val ] +Uplift Scope [pval] +Uplift Scope [ival] +Uplift Scope [printval] +Uplift Scope [incval] + +Uplifting [printother] best 722 combination zp[1]:3 [ printother::i ] +Uplifting [main] best 722 combination zp[1]:4 [ main::i ] +Uplifting [] best 722 combination zp[1]:2 [ val ] +Uplifting [pval] best 722 combination +Uplifting [ival] best 722 combination +Uplifting [printval] best 722 combination +Uplifting [incval] best 722 combination +Attempting to uplift remaining variables inzp[1]:3 [ printother::i ] +Uplifting [printother] best 722 combination zp[1]:3 [ printother::i ] +Attempting to uplift remaining variables inzp[1]:4 [ main::i ] +Uplifting [main] best 722 combination zp[1]:4 [ main::i ] +Attempting to uplift remaining variables inzp[1]:2 [ val ] +Uplifting [] best 722 combination zp[1]:2 [ val ] +Coalescing zero page register [ zp[1]:3 [ printother::i ] ] with [ zp[1]:2 [ val ] ] +Allocated (was zp[1]:3) zp[1]:2 [ printother::i val ] +Allocated (was zp[1]:4) zp[1]:3 [ main::i ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test a procedure with calling convention stack +// Illustrates live range problem with function variable printother::i and global variable val + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label val = 2 + // @begin +__bbegin: + // [0] (byte) val ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z val + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] callprepare main + // [3] callexecute main -- jsr + jsr main + // [4] callfinalize main + // [5] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // printother +printother: { + .label i = 2 + // [6] (byte) printother::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp __b1 + // printother::@1 + __b1: + // [7] *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) ← ++ *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) -- pbuc1_derefidx_vbuz1=_inc_pbuc1_derefidx_vbuz1 + ldx.z i + inc SCREEN+$28,x + // [8] (byte) printother::i ← ++ (byte) printother::i -- vbuz1=_inc_vbuz1 + inc.z i + // [9] if((byte) printother::i!=(byte) 6) goto printother::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #6 + cmp.z i + bne __b1 + jmp __breturn + // printother::@return + __breturn: + // [10] return + rts +} + // incval +incval: { + // [11] (byte) val ← ++ (byte) val -- vbuz1=_inc_vbuz1 + inc.z val + jmp __breturn + // incval::@return + __breturn: + // [12] return + rts +} + // printval +printval: { + // [13] *((const byte*) SCREEN) ← (byte) val -- _deref_pbuc1=vbuz1 + lda.z val + sta SCREEN + jmp __breturn + // printval::@return + __breturn: + // [14] return + rts +} + // ival +ival: { + // [16] callprepare incval + // [17] callexecute incval -- jsr + jsr incval + // [18] callfinalize incval + jmp __breturn + // ival::@return + __breturn: + // [19] return + rts +} + // pval +pval: { + // [21] callprepare printval + // [22] callexecute printval -- jsr + jsr printval + // [23] callfinalize printval + jmp __breturn + // pval::@return + __breturn: + // [24] return + rts +} + // main +main: { + .label i = 3 + // [25] (byte) main::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + // [26] phi from main main::@1 to main::@1 [phi:main/main::@1->main::@1] + __b1_from_main: + __b1_from___b1: + jmp __b1 + // main::@1 + __b1: + // [27] callprepare pval + // [28] callexecute pval -- jsr + jsr pval + // [29] callfinalize pval + // [30] callprepare printother + // [31] callexecute printother -- jsr + jsr printother + // [32] callfinalize printother + // [33] callprepare ival + // [34] callexecute ival -- jsr + jsr ival + // [35] callfinalize ival + // [36] (byte) main::i ← ++ (byte) main::i -- vbuz1=_inc_vbuz1 + inc.z i + // [37] if((byte) main::i!=(byte) 6) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #6 + cmp.z i + bne __b1_from___b1 + jmp __breturn + // main::@return + __breturn: + // [38] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label __b1_from___b1 with __b1 +Removing instruction __b1_from___bbegin: +Removing instruction __bend_from___b1: +Removing instruction __b1_from_main: +Removing instruction __b1_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __b1: +Removing instruction __bend: +Removing instruction __breturn: +Removing instruction __breturn: +Removing instruction __breturn: +Removing instruction __breturn: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Adding RTS to root block +Succesful ASM optimization Pass5AddMainRts + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const byte*) SCREEN = (byte*) 1024 +__stackcall (void()) incval() +(label) incval::@return +__stackcall (void()) ival() +(label) ival::@return +__stackcall (void()) main() +(label) main::@1 +(label) main::@return +(byte) main::i loadstore zp[1]:3 2.6923076923076925 +__stackcall (void()) printother() +(label) printother::@1 +(label) printother::@return +(byte) printother::i loadstore zp[1]:2 14.25 +__stackcall (void()) printval() +(label) printval::@return +__stackcall (void()) pval() +(label) pval::@return +(byte) val loadstore zp[1]:2 2.0 + +zp[1]:2 [ printother::i val ] +zp[1]:3 [ main::i ] + + +FINAL ASSEMBLER +Score: 617 + + // File Comments +// Test a procedure with calling convention stack +// Illustrates live range problem with function variable printother::i and global variable val + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label val = 2 + // @begin +__bbegin: + // val = 0 + // [0] (byte) val ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z val + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] callprepare main + // [3] callexecute main -- jsr + jsr main + rts + // [4] callfinalize main + // [5] phi from @1 to @end [phi:@1->@end] + // @end + // printother +printother: { + .label i = 2 + // for(char i:0..5) + // [6] (byte) printother::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + // printother::@1 + __b1: + // (SCREEN+40)[i]++; + // [7] *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) ← ++ *((const byte*) SCREEN+(byte) $28 + (byte) printother::i) -- pbuc1_derefidx_vbuz1=_inc_pbuc1_derefidx_vbuz1 + ldx.z i + inc SCREEN+$28,x + // for(char i:0..5) + // [8] (byte) printother::i ← ++ (byte) printother::i -- vbuz1=_inc_vbuz1 + inc.z i + // [9] if((byte) printother::i!=(byte) 6) goto printother::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #6 + cmp.z i + bne __b1 + // printother::@return + // } + // [10] return + rts +} + // incval +incval: { + // val++; + // [11] (byte) val ← ++ (byte) val -- vbuz1=_inc_vbuz1 + inc.z val + // incval::@return + // } + // [12] return + rts +} + // printval +printval: { + // SCREEN[0] = val + // [13] *((const byte*) SCREEN) ← (byte) val -- _deref_pbuc1=vbuz1 + lda.z val + sta SCREEN + // printval::@return + // } + // [14] return + rts +} + // ival +ival: { + // incval() + // [16] callprepare incval + // [17] callexecute incval -- jsr + jsr incval + // [18] callfinalize incval + // ival::@return + // } + // [19] return + rts +} + // pval +pval: { + // printval() + // [21] callprepare printval + // [22] callexecute printval -- jsr + jsr printval + // [23] callfinalize printval + // pval::@return + // } + // [24] return + rts +} + // main +main: { + .label i = 3 + // for(char i:0..5) + // [25] (byte) main::i ← (byte) 0 -- vbuz1=vbuc1 + lda #0 + sta.z i + // [26] phi from main main::@1 to main::@1 [phi:main/main::@1->main::@1] + // main::@1 + __b1: + // pval() + // [27] callprepare pval + // [28] callexecute pval -- jsr + jsr pval + // [29] callfinalize pval + // printother() + // [30] callprepare printother + // [31] callexecute printother -- jsr + jsr printother + // [32] callfinalize printother + // ival() + // [33] callprepare ival + // [34] callexecute ival -- jsr + jsr ival + // [35] callfinalize ival + // for(char i:0..5) + // [36] (byte) main::i ← ++ (byte) main::i -- vbuz1=_inc_vbuz1 + inc.z i + // [37] if((byte) main::i!=(byte) 6) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #6 + cmp.z i + bne __b1 + // main::@return + // } + // [38] return + rts +} + // File Data + diff --git a/src/test/ref/procedure-callingconvention-stack-9.sym b/src/test/ref/procedure-callingconvention-stack-9.sym new file mode 100644 index 000000000..0a5c48127 --- /dev/null +++ b/src/test/ref/procedure-callingconvention-stack-9.sym @@ -0,0 +1,24 @@ +(label) @1 +(label) @begin +(label) @end +(const byte*) SCREEN = (byte*) 1024 +__stackcall (void()) incval() +(label) incval::@return +__stackcall (void()) ival() +(label) ival::@return +__stackcall (void()) main() +(label) main::@1 +(label) main::@return +(byte) main::i loadstore zp[1]:3 2.6923076923076925 +__stackcall (void()) printother() +(label) printother::@1 +(label) printother::@return +(byte) printother::i loadstore zp[1]:2 14.25 +__stackcall (void()) printval() +(label) printval::@return +__stackcall (void()) pval() +(label) pval::@return +(byte) val loadstore zp[1]:2 2.0 + +zp[1]:2 [ printother::i val ] +zp[1]:3 [ main::i ]