From 64b3a07ee658a7796e32e9bbc504eb2f07e5555f Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Thu, 4 Apr 2019 20:45:52 +0200 Subject: [PATCH] Added all functions that become pointers as separate thread heads. --- .../kickc/passes/Pass4ZeroPageCoalesce.java | 13 +- .../Pass4ZeroPageCoalesceAssignment.java | 2 +- .../dk/camelot64/kickc/test/TestPrograms.java | 5 + src/test/kc/function-pointer-noarg-call-6.kc | 16 + .../ref/function-pointer-noarg-call-6.asm | 57 ++ .../ref/function-pointer-noarg-call-6.cfg | 34 + .../ref/function-pointer-noarg-call-6.log | 591 ++++++++++++++++++ .../ref/function-pointer-noarg-call-6.sym | 21 + 8 files changed, 732 insertions(+), 7 deletions(-) create mode 100644 src/test/kc/function-pointer-noarg-call-6.kc create mode 100644 src/test/ref/function-pointer-noarg-call-6.asm create mode 100644 src/test/ref/function-pointer-noarg-call-6.cfg create mode 100644 src/test/ref/function-pointer-noarg-call-6.log create mode 100644 src/test/ref/function-pointer-noarg-call-6.sym diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesce.java b/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesce.java index bef9b3f22..7b3206e03 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesce.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesce.java @@ -26,7 +26,7 @@ public class Pass4ZeroPageCoalesce extends Pass2Base { public void coalesce() { LinkedHashSet unknownFragments = new LinkedHashSet<>(); LiveRangeEquivalenceClassSet liveRangeEquivalenceClassSet = getProgram().getLiveRangeEquivalenceClassSet(); - Collection threads = getThreadHeads(getSymbols()); + Collection threads = getThreadHeads(getProgram()); boolean change; do { change = coalesce(liveRangeEquivalenceClassSet, threads, unknownFragments); @@ -47,15 +47,16 @@ public class Pass4ZeroPageCoalesce extends Pass2Base { * * @return The threads. */ - public static Collection getThreadHeads(ProgramScope programScope) { + public static Collection getThreadHeads(Program program) { ArrayList threadHeads = new ArrayList<>(); - Collection procedures = programScope.getAllProcedures(true); + Collection procedures = program.getScope().getAllProcedures(true); for(Procedure procedure : procedures) { - if(procedure.getInterruptType() != null) { - threadHeads.add(procedure.getRef()); - } if(procedure.getFullName().equals(SymbolRef.MAIN_PROC_NAME)) { threadHeads.add(procedure.getRef()); + continue; + } + if(Pass2ConstantIdentification.isAddressOfUsed(procedure.getRef(), program)) { + threadHeads.add(procedure.getRef()); } } return threadHeads; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesceAssignment.java b/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesceAssignment.java index 325d20615..2122411c0 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesceAssignment.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4ZeroPageCoalesceAssignment.java @@ -20,7 +20,7 @@ public class Pass4ZeroPageCoalesceAssignment extends Pass2Base { public void coalesce() { CoalesceVarScores coalesceVarScores = new CoalesceVarScores(getProgram()); LinkedHashSet unknownFragments = new LinkedHashSet<>(); - Collection threadHeads = Pass4ZeroPageCoalesce.getThreadHeads(getSymbols()); + Collection threadHeads = Pass4ZeroPageCoalesce.getThreadHeads(getProgram()); boolean change; do { diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 7d5c2bf3c..503f6914e 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -32,6 +32,11 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testFunctionPointerNoargCall6() throws IOException, URISyntaxException { + compileAndCompare("function-pointer-noarg-call-6"); + } + @Test public void testFunctionPointerNoargCall5() throws IOException, URISyntaxException { compileAndCompare("function-pointer-noarg-call-5"); diff --git a/src/test/kc/function-pointer-noarg-call-6.kc b/src/test/kc/function-pointer-noarg-call-6.kc new file mode 100644 index 000000000..1ccdb60ac --- /dev/null +++ b/src/test/kc/function-pointer-noarg-call-6.kc @@ -0,0 +1,16 @@ +// Tests calling into a function pointer with local variables + +void main() { + void()* cls = &fn1; + for(byte* cols = $d800; cols<$d800+1000;cols++) { + (*cls)(); + cols++; + } +} + +void fn1() { + for(byte* screen=$400;screen<$400+1000;screen++) + (*screen)++; +} + + diff --git a/src/test/ref/function-pointer-noarg-call-6.asm b/src/test/ref/function-pointer-noarg-call-6.asm new file mode 100644 index 000000000..2b6df0d98 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-6.asm @@ -0,0 +1,57 @@ +// Tests calling into a function pointer with local variables +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +main: { + .label cls = fn1 + .label cols = 2 + lda #<$d800 + sta cols + lda #>$d800 + sta cols+1 + b1: + jsr cls + inc cols + bne !+ + inc cols+1 + !: + inc cols + bne !+ + inc cols+1 + !: + lda cols+1 + cmp #>$d800+$3e8 + bcc b1 + bne !+ + lda cols + cmp #<$d800+$3e8 + bcc b1 + !: + rts +} +fn1: { + .label screen = 4 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + b1: + ldy #0 + lda (screen),y + clc + adc #1 + sta (screen),y + inc screen + bne !+ + inc screen+1 + !: + lda screen+1 + cmp #>$400+$3e8 + bcc b1 + bne !+ + lda screen + cmp #<$400+$3e8 + bcc b1 + !: + rts +} diff --git a/src/test/ref/function-pointer-noarg-call-6.cfg b/src/test/ref/function-pointer-noarg-call-6.cfg new file mode 100644 index 000000000..04306d760 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-6.cfg @@ -0,0 +1,34 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@1 + [5] (byte*) main::cols#3 ← phi( main/((byte*))(word/dword/signed dword) $d800 main::@1/(byte*) main::cols#2 ) + [6] call *((const void()*) main::cls#0) + [7] (byte*) main::cols#1 ← ++ (byte*) main::cols#3 + [8] (byte*) main::cols#2 ← ++ (byte*) main::cols#1 + [9] if((byte*) main::cols#2<(word/dword/signed dword) $d800+(word/signed word/dword/signed dword) $3e8) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 + [10] return + to:@return +fn1: scope:[fn1] from + [11] phi() + to:fn1::@1 +fn1::@1: scope:[fn1] from fn1 fn1::@1 + [12] (byte*) fn1::screen#2 ← phi( fn1/((byte*))(word/signed word/dword/signed dword) $400 fn1::@1/(byte*) fn1::screen#1 ) + [13] *((byte*) fn1::screen#2) ← ++ *((byte*) fn1::screen#2) + [14] (byte*) fn1::screen#1 ← ++ (byte*) fn1::screen#2 + [15] if((byte*) fn1::screen#1<(word/signed word/dword/signed dword) $400+(word/signed word/dword/signed dword) $3e8) goto fn1::@1 + to:fn1::@return +fn1::@return: scope:[fn1] from fn1::@1 + [16] return + to:@return diff --git a/src/test/ref/function-pointer-noarg-call-6.log b/src/test/ref/function-pointer-noarg-call-6.log new file mode 100644 index 000000000..43b37285a --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-6.log @@ -0,0 +1,591 @@ +Resolved forward reference fn1 to (void()) fn1() + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@2 +main: scope:[main] from @2 + (void()*~) main::$0 ← & (void()) fn1() + (void()*) main::cls#0 ← (void()*~) main::$0 + (byte*) main::cols#0 ← ((byte*)) (word/dword/signed dword) $d800 + to:main::@1 +main::@1: scope:[main] from main main::@1 + (byte*) main::cols#3 ← phi( main/(byte*) main::cols#0 main::@1/(byte*) main::cols#2 ) + (void()*) main::cls#1 ← phi( main/(void()*) main::cls#0 main::@1/(void()*) main::cls#1 ) + call *((void()*) main::cls#1) + (byte*) main::cols#1 ← ++ (byte*) main::cols#3 + (byte*) main::cols#2 ← ++ (byte*) main::cols#1 + (word/dword/signed dword~) main::$2 ← (word/dword/signed dword) $d800 + (word/signed word/dword/signed dword) $3e8 + (bool~) main::$3 ← (byte*) main::cols#2 < (word/dword/signed dword~) main::$2 + if((bool~) main::$3) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 + return + to:@return +fn1: scope:[fn1] from + (byte*) fn1::screen#0 ← ((byte*)) (word/signed word/dword/signed dword) $400 + to:fn1::@1 +fn1::@1: scope:[fn1] from fn1 fn1::@1 + (byte*) fn1::screen#2 ← phi( fn1/(byte*) fn1::screen#0 fn1::@1/(byte*) fn1::screen#1 ) + *((byte*) fn1::screen#2) ← ++ *((byte*) fn1::screen#2) + (byte*) fn1::screen#1 ← ++ (byte*) fn1::screen#2 + (word/signed word/dword/signed dword~) fn1::$0 ← (word/signed word/dword/signed dword) $400 + (word/signed word/dword/signed dword) $3e8 + (bool~) fn1::$1 ← (byte*) fn1::screen#1 < (word/signed word/dword/signed dword~) fn1::$0 + if((bool~) fn1::$1) goto fn1::@1 + to:fn1::@return +fn1::@return: scope:[fn1] from fn1::@1 + return + 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 +(void()) fn1() +(word/signed word/dword/signed dword~) fn1::$0 +(bool~) fn1::$1 +(label) fn1::@1 +(label) fn1::@return +(byte*) fn1::screen +(byte*) fn1::screen#0 +(byte*) fn1::screen#1 +(byte*) fn1::screen#2 +(void()) main() +(void()*~) main::$0 +(word/dword/signed dword~) main::$2 +(bool~) main::$3 +(label) main::@1 +(label) main::@return +(void()*) main::cls +(void()*) main::cls#0 +(void()*) main::cls#1 +(byte*) main::cols +(byte*) main::cols#0 +(byte*) main::cols#1 +(byte*) main::cols#2 +(byte*) main::cols#3 + +Culled Empty Block (label) @3 +Successful SSA optimization Pass2CullEmptyBlocks +Alias (void()*) main::cls#0 = (void()*~) main::$0 +Successful SSA optimization Pass2AliasElimination +Self Phi Eliminated (void()*) main::cls#1 +Successful SSA optimization Pass2SelfPhiElimination +Redundant Phi (void()*) main::cls#1 (void()*) main::cls#0 +Successful SSA optimization Pass2RedundantPhiElimination +Simple Condition (bool~) main::$3 [9] if((byte*) main::cols#2<(word/dword/signed dword~) main::$2) goto main::@1 +Simple Condition (bool~) fn1::$1 [17] if((byte*) fn1::screen#1<(word/signed word/dword/signed dword~) fn1::$0) goto fn1::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const void()*) main::cls#0 = &fn1 +Constant (const byte*) main::cols#0 = ((byte*))$d800 +Constant (const word/dword/signed dword) main::$2 = $d800+$3e8 +Constant (const byte*) fn1::screen#0 = ((byte*))$400 +Constant (const word/signed word/dword/signed dword) fn1::$0 = $400+$3e8 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings (const byte*) main::cols#0 +Inlining constant with var siblings (const byte*) fn1::screen#0 +Constant inlined fn1::screen#0 = ((byte*))(word/signed word/dword/signed dword) $400 +Constant inlined fn1::$0 = (word/signed word/dword/signed dword) $400+(word/signed word/dword/signed dword) $3e8 +Constant inlined main::$2 = (word/dword/signed dword) $d800+(word/signed word/dword/signed dword) $3e8 +Constant inlined main::cols#0 = ((byte*))(word/dword/signed dword) $d800 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@3(between main::@1 and main::@1) +Added new block during phi lifting fn1::@3(between fn1::@1 and fn1::@1) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of fn1 +CALL GRAPH +Calls in [] to main:2 + +Created 2 initial phi equivalence classes +Coalesced [11] main::cols#4 ← main::cols#2 +Coalesced [18] fn1::screen#3 ← fn1::screen#1 +Coalesced down to 2 phi equivalence classes +Culled Empty Block (label) main::@3 +Culled Empty Block (label) fn1::@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 +Adding NOP phi() at start of fn1 + +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() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@1 + [5] (byte*) main::cols#3 ← phi( main/((byte*))(word/dword/signed dword) $d800 main::@1/(byte*) main::cols#2 ) + [6] call *((const void()*) main::cls#0) + [7] (byte*) main::cols#1 ← ++ (byte*) main::cols#3 + [8] (byte*) main::cols#2 ← ++ (byte*) main::cols#1 + [9] if((byte*) main::cols#2<(word/dword/signed dword) $d800+(word/signed word/dword/signed dword) $3e8) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 + [10] return + to:@return +fn1: scope:[fn1] from + [11] phi() + to:fn1::@1 +fn1::@1: scope:[fn1] from fn1 fn1::@1 + [12] (byte*) fn1::screen#2 ← phi( fn1/((byte*))(word/signed word/dword/signed dword) $400 fn1::@1/(byte*) fn1::screen#1 ) + [13] *((byte*) fn1::screen#2) ← ++ *((byte*) fn1::screen#2) + [14] (byte*) fn1::screen#1 ← ++ (byte*) fn1::screen#2 + [15] if((byte*) fn1::screen#1<(word/signed word/dword/signed dword) $400+(word/signed word/dword/signed dword) $3e8) goto fn1::@1 + to:fn1::@return +fn1::@return: scope:[fn1] from fn1::@1 + [16] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(void()) fn1() +(byte*) fn1::screen +(byte*) fn1::screen#1 16.5 +(byte*) fn1::screen#2 22.0 +(void()) main() +(void()*) main::cls +(byte*) main::cols +(byte*) main::cols#1 22.0 +(byte*) main::cols#2 16.5 +(byte*) main::cols#3 11.0 + +Initial phi equivalence classes +[ main::cols#3 main::cols#2 ] +[ fn1::screen#2 fn1::screen#1 ] +Added variable main::cols#1 to zero page equivalence class [ main::cols#1 ] +Complete equivalence classes +[ main::cols#3 main::cols#2 ] +[ fn1::screen#2 fn1::screen#1 ] +[ main::cols#1 ] +Allocated zp ZP_WORD:2 [ main::cols#3 main::cols#2 ] +Allocated zp ZP_WORD:4 [ fn1::screen#2 fn1::screen#1 ] +Allocated zp ZP_WORD:6 [ main::cols#1 ] + +INITIAL ASM +//SEG0 File Comments +// Tests calling into a function pointer with local variables +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG9 @end +bend: +//SEG10 main +main: { + .label cls = fn1 + .label cols = 6 + .label cols_2 = 2 + .label cols_3 = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte*) main::cols#3 = ((byte*))(word/dword/signed dword) $d800 [phi:main->main::@1#0] -- pbuz1=pbuc1 + lda #<$d800 + sta cols_3 + lda #>$d800 + sta cols_3+1 + jmp b1 + //SEG13 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + //SEG14 [5] phi (byte*) main::cols#3 = (byte*) main::cols#2 [phi:main::@1->main::@1#0] -- register_copy + jmp b1 + //SEG15 main::@1 + b1: + //SEG16 [6] call *((const void()*) main::cls#0) + jsr cls + //SEG17 [7] (byte*) main::cols#1 ← ++ (byte*) main::cols#3 -- pbuz1=_inc_pbuz2 + lda cols_3 + clc + adc #1 + sta cols + lda cols_3+1 + adc #0 + sta cols+1 + //SEG18 [8] (byte*) main::cols#2 ← ++ (byte*) main::cols#1 -- pbuz1=_inc_pbuz2 + lda cols + clc + adc #1 + sta cols_2 + lda cols+1 + adc #0 + sta cols_2+1 + //SEG19 [9] if((byte*) main::cols#2<(word/dword/signed dword) $d800+(word/signed word/dword/signed dword) $3e8) goto main::@1 -- pbuz1_lt_vwuc1_then_la1 + lda cols_2+1 + cmp #>$d800+$3e8 + bcc b1_from_b1 + bne !+ + lda cols_2 + cmp #<$d800+$3e8 + bcc b1_from_b1 + !: + jmp breturn + //SEG20 main::@return + breturn: + //SEG21 [10] return + rts +} +//SEG22 fn1 +fn1: { + .label screen = 4 + //SEG23 [12] phi from fn1 to fn1::@1 [phi:fn1->fn1::@1] + b1_from_fn1: + //SEG24 [12] phi (byte*) fn1::screen#2 = ((byte*))(word/signed word/dword/signed dword) $400 [phi:fn1->fn1::@1#0] -- pbuz1=pbuc1 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + jmp b1 + //SEG25 [12] phi from fn1::@1 to fn1::@1 [phi:fn1::@1->fn1::@1] + b1_from_b1: + //SEG26 [12] phi (byte*) fn1::screen#2 = (byte*) fn1::screen#1 [phi:fn1::@1->fn1::@1#0] -- register_copy + jmp b1 + //SEG27 fn1::@1 + b1: + //SEG28 [13] *((byte*) fn1::screen#2) ← ++ *((byte*) fn1::screen#2) -- _deref_pbuz1=_inc__deref_pbuz1 + ldy #0 + lda (screen),y + clc + adc #1 + ldy #0 + sta (screen),y + //SEG29 [14] (byte*) fn1::screen#1 ← ++ (byte*) fn1::screen#2 -- pbuz1=_inc_pbuz1 + inc screen + bne !+ + inc screen+1 + !: + //SEG30 [15] if((byte*) fn1::screen#1<(word/signed word/dword/signed dword) $400+(word/signed word/dword/signed dword) $3e8) goto fn1::@1 -- pbuz1_lt_vwuc1_then_la1 + lda screen+1 + cmp #>$400+$3e8 + bcc b1_from_b1 + bne !+ + lda screen + cmp #<$400+$3e8 + bcc b1_from_b1 + !: + jmp breturn + //SEG31 fn1::@return + breturn: + //SEG32 [16] return + rts +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [7] (byte*) main::cols#1 ← ++ (byte*) main::cols#3 [ main::cols#1 ] ( main:2 [ main::cols#1 ] ) always clobbers reg byte a +Statement [8] (byte*) main::cols#2 ← ++ (byte*) main::cols#1 [ main::cols#2 ] ( main:2 [ main::cols#2 ] ) always clobbers reg byte a +Statement [9] if((byte*) main::cols#2<(word/dword/signed dword) $d800+(word/signed word/dword/signed dword) $3e8) goto main::@1 [ main::cols#2 ] ( main:2 [ main::cols#2 ] ) always clobbers reg byte a +Statement [13] *((byte*) fn1::screen#2) ← ++ *((byte*) fn1::screen#2) [ fn1::screen#2 ] ( ) always clobbers reg byte a reg byte y +Statement [15] if((byte*) fn1::screen#1<(word/signed word/dword/signed dword) $400+(word/signed word/dword/signed dword) $3e8) goto fn1::@1 [ fn1::screen#1 ] ( ) always clobbers reg byte a +Potential registers zp ZP_WORD:2 [ main::cols#3 main::cols#2 ] : zp ZP_WORD:2 , +Potential registers zp ZP_WORD:4 [ fn1::screen#2 fn1::screen#1 ] : zp ZP_WORD:4 , +Potential registers zp ZP_WORD:6 [ main::cols#1 ] : zp ZP_WORD:6 , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 27.5: zp ZP_WORD:2 [ main::cols#3 main::cols#2 ] 22: zp ZP_WORD:6 [ main::cols#1 ] +Uplift Scope [fn1] 38.5: zp ZP_WORD:4 [ fn1::screen#2 fn1::screen#1 ] +Uplift Scope [] + +Uplifting [main] best 1494 combination zp ZP_WORD:2 [ main::cols#3 main::cols#2 ] zp ZP_WORD:6 [ main::cols#1 ] +Uplifting [fn1] best 1494 combination zp ZP_WORD:4 [ fn1::screen#2 fn1::screen#1 ] +Uplifting [] best 1494 combination +Coalescing zero page register with common assignment [ zp ZP_WORD:2 [ main::cols#3 main::cols#2 ] ] with [ zp ZP_WORD:6 [ main::cols#1 ] ] - score: 2 + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Tests calling into a function pointer with local variables +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG9 @end +bend: +//SEG10 main +main: { + .label cls = fn1 + .label cols = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte*) main::cols#3 = ((byte*))(word/dword/signed dword) $d800 [phi:main->main::@1#0] -- pbuz1=pbuc1 + lda #<$d800 + sta cols + lda #>$d800 + sta cols+1 + jmp b1 + //SEG13 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + //SEG14 [5] phi (byte*) main::cols#3 = (byte*) main::cols#2 [phi:main::@1->main::@1#0] -- register_copy + jmp b1 + //SEG15 main::@1 + b1: + //SEG16 [6] call *((const void()*) main::cls#0) + jsr cls + //SEG17 [7] (byte*) main::cols#1 ← ++ (byte*) main::cols#3 -- pbuz1=_inc_pbuz1 + inc cols + bne !+ + inc cols+1 + !: + //SEG18 [8] (byte*) main::cols#2 ← ++ (byte*) main::cols#1 -- pbuz1=_inc_pbuz1 + inc cols + bne !+ + inc cols+1 + !: + //SEG19 [9] if((byte*) main::cols#2<(word/dword/signed dword) $d800+(word/signed word/dword/signed dword) $3e8) goto main::@1 -- pbuz1_lt_vwuc1_then_la1 + lda cols+1 + cmp #>$d800+$3e8 + bcc b1_from_b1 + bne !+ + lda cols + cmp #<$d800+$3e8 + bcc b1_from_b1 + !: + jmp breturn + //SEG20 main::@return + breturn: + //SEG21 [10] return + rts +} +//SEG22 fn1 +fn1: { + .label screen = 4 + //SEG23 [12] phi from fn1 to fn1::@1 [phi:fn1->fn1::@1] + b1_from_fn1: + //SEG24 [12] phi (byte*) fn1::screen#2 = ((byte*))(word/signed word/dword/signed dword) $400 [phi:fn1->fn1::@1#0] -- pbuz1=pbuc1 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + jmp b1 + //SEG25 [12] phi from fn1::@1 to fn1::@1 [phi:fn1::@1->fn1::@1] + b1_from_b1: + //SEG26 [12] phi (byte*) fn1::screen#2 = (byte*) fn1::screen#1 [phi:fn1::@1->fn1::@1#0] -- register_copy + jmp b1 + //SEG27 fn1::@1 + b1: + //SEG28 [13] *((byte*) fn1::screen#2) ← ++ *((byte*) fn1::screen#2) -- _deref_pbuz1=_inc__deref_pbuz1 + ldy #0 + lda (screen),y + clc + adc #1 + ldy #0 + sta (screen),y + //SEG29 [14] (byte*) fn1::screen#1 ← ++ (byte*) fn1::screen#2 -- pbuz1=_inc_pbuz1 + inc screen + bne !+ + inc screen+1 + !: + //SEG30 [15] if((byte*) fn1::screen#1<(word/signed word/dword/signed dword) $400+(word/signed word/dword/signed dword) $3e8) goto fn1::@1 -- pbuz1_lt_vwuc1_then_la1 + lda screen+1 + cmp #>$400+$3e8 + bcc b1_from_b1 + bne !+ + lda screen + cmp #<$400+$3e8 + bcc b1_from_b1 + !: + jmp breturn + //SEG31 fn1::@return + breturn: + //SEG32 [16] return + rts +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp breturn +Removing instruction jmp b1 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction ldy #0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Replacing label b1_from_b1 with b1 +Replacing label b1_from_b1 with b1 +Replacing label b1_from_b1 with b1 +Replacing label b1_from_b1 with b1 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b1: +Removing instruction b1_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction breturn: +Removing instruction b1_from_fn1: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction jmp b1 +Removing instruction jmp b1 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@1 +(label) fn1::@return +(byte*) fn1::screen +(byte*) fn1::screen#1 screen zp ZP_WORD:4 16.5 +(byte*) fn1::screen#2 screen zp ZP_WORD:4 22.0 +(void()) main() +(label) main::@1 +(label) main::@return +(void()*) main::cls +(const void()*) main::cls#0 cls = &(void()) fn1() +(byte*) main::cols +(byte*) main::cols#1 cols zp ZP_WORD:2 22.0 +(byte*) main::cols#2 cols zp ZP_WORD:2 16.5 +(byte*) main::cols#3 cols zp ZP_WORD:2 11.0 + +zp ZP_WORD:2 [ main::cols#3 main::cols#2 main::cols#1 ] +zp ZP_WORD:4 [ fn1::screen#2 fn1::screen#1 ] + + +FINAL ASSEMBLER +Score: 1172 + +//SEG0 File Comments +// Tests calling into a function pointer with local variables +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels +//SEG3 @begin +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +//SEG5 @1 +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +//SEG9 @end +//SEG10 main +main: { + .label cls = fn1 + .label cols = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + //SEG12 [5] phi (byte*) main::cols#3 = ((byte*))(word/dword/signed dword) $d800 [phi:main->main::@1#0] -- pbuz1=pbuc1 + lda #<$d800 + sta cols + lda #>$d800 + sta cols+1 + //SEG13 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + //SEG14 [5] phi (byte*) main::cols#3 = (byte*) main::cols#2 [phi:main::@1->main::@1#0] -- register_copy + //SEG15 main::@1 + b1: + //SEG16 [6] call *((const void()*) main::cls#0) + jsr cls + //SEG17 [7] (byte*) main::cols#1 ← ++ (byte*) main::cols#3 -- pbuz1=_inc_pbuz1 + inc cols + bne !+ + inc cols+1 + !: + //SEG18 [8] (byte*) main::cols#2 ← ++ (byte*) main::cols#1 -- pbuz1=_inc_pbuz1 + inc cols + bne !+ + inc cols+1 + !: + //SEG19 [9] if((byte*) main::cols#2<(word/dword/signed dword) $d800+(word/signed word/dword/signed dword) $3e8) goto main::@1 -- pbuz1_lt_vwuc1_then_la1 + lda cols+1 + cmp #>$d800+$3e8 + bcc b1 + bne !+ + lda cols + cmp #<$d800+$3e8 + bcc b1 + !: + //SEG20 main::@return + //SEG21 [10] return + rts +} +//SEG22 fn1 +fn1: { + .label screen = 4 + //SEG23 [12] phi from fn1 to fn1::@1 [phi:fn1->fn1::@1] + //SEG24 [12] phi (byte*) fn1::screen#2 = ((byte*))(word/signed word/dword/signed dword) $400 [phi:fn1->fn1::@1#0] -- pbuz1=pbuc1 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + //SEG25 [12] phi from fn1::@1 to fn1::@1 [phi:fn1::@1->fn1::@1] + //SEG26 [12] phi (byte*) fn1::screen#2 = (byte*) fn1::screen#1 [phi:fn1::@1->fn1::@1#0] -- register_copy + //SEG27 fn1::@1 + b1: + //SEG28 [13] *((byte*) fn1::screen#2) ← ++ *((byte*) fn1::screen#2) -- _deref_pbuz1=_inc__deref_pbuz1 + ldy #0 + lda (screen),y + clc + adc #1 + sta (screen),y + //SEG29 [14] (byte*) fn1::screen#1 ← ++ (byte*) fn1::screen#2 -- pbuz1=_inc_pbuz1 + inc screen + bne !+ + inc screen+1 + !: + //SEG30 [15] if((byte*) fn1::screen#1<(word/signed word/dword/signed dword) $400+(word/signed word/dword/signed dword) $3e8) goto fn1::@1 -- pbuz1_lt_vwuc1_then_la1 + lda screen+1 + cmp #>$400+$3e8 + bcc b1 + bne !+ + lda screen + cmp #<$400+$3e8 + bcc b1 + !: + //SEG31 fn1::@return + //SEG32 [16] return + rts +} + diff --git a/src/test/ref/function-pointer-noarg-call-6.sym b/src/test/ref/function-pointer-noarg-call-6.sym new file mode 100644 index 000000000..48e38ebea --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-6.sym @@ -0,0 +1,21 @@ +(label) @1 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@1 +(label) fn1::@return +(byte*) fn1::screen +(byte*) fn1::screen#1 screen zp ZP_WORD:4 16.5 +(byte*) fn1::screen#2 screen zp ZP_WORD:4 22.0 +(void()) main() +(label) main::@1 +(label) main::@return +(void()*) main::cls +(const void()*) main::cls#0 cls = &(void()) fn1() +(byte*) main::cols +(byte*) main::cols#1 cols zp ZP_WORD:2 22.0 +(byte*) main::cols#2 cols zp ZP_WORD:2 16.5 +(byte*) main::cols#3 cols zp ZP_WORD:2 11.0 + +zp ZP_WORD:2 [ main::cols#3 main::cols#2 main::cols#1 ] +zp ZP_WORD:4 [ fn1::screen#2 fn1::screen#1 ]