diff --git a/src/main/fragment/pprz1=_deref_pptc1.asm b/src/main/fragment/pprz1=_deref_pptc1.asm new file mode 100644 index 000000000..1f588b460 --- /dev/null +++ b/src/main/fragment/pprz1=_deref_pptc1.asm @@ -0,0 +1,4 @@ +lda {c1} +sta {z1} +lda {c1}+1 +sta {z1}+1 \ No newline at end of file diff --git a/src/main/fragment/pprz1=pptc1_derefidx_vbuxx.asm b/src/main/fragment/pprz1=pptc1_derefidx_vbuxx.asm new file mode 100644 index 000000000..77cdec92f --- /dev/null +++ b/src/main/fragment/pprz1=pptc1_derefidx_vbuxx.asm @@ -0,0 +1,4 @@ +lda {c1},x +sta {z1} +lda {c1}+1,x +sta {z1}+1 \ No newline at end of file diff --git a/src/main/fragment/pprz1=pptc1_derefidx_vbuyy.asm b/src/main/fragment/pprz1=pptc1_derefidx_vbuyy.asm new file mode 100644 index 000000000..9c735d2d0 --- /dev/null +++ b/src/main/fragment/pprz1=pptc1_derefidx_vbuyy.asm @@ -0,0 +1,4 @@ +lda {c1},y +sta {z1} +lda {c1}+1,y +sta {z1}+1 \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 4cd6e31da..4b468a027 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -379,6 +379,13 @@ public class Pass4CodeGeneration { } else if(SymbolType.isDWord(elementType) || SymbolType.isSDWord(elementType)) { asm.addDataNumeric(asmName.replace("#", "_").replace("$", "_"), AsmDataNumeric.Type.DWORD, asmElements); added.add(asmName); + } else if(elementType instanceof SymbolTypePointer) { + if(((SymbolTypePointer) elementType).getElementType() instanceof SymbolTypeProcedure) { + asm.addDataNumeric(asmName.replace("#", "_").replace("$", "_"), AsmDataNumeric.Type.WORD, asmElements); + added.add(asmName); + } else { + throw new RuntimeException("Unhandled constant array element type " + constantArrayList.toString(program)); + } } else { throw new RuntimeException("Unhandled constant array element type " + constantArrayList.toString(program)); } diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 0bd133d8b..7d5c2bf3c 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 testFunctionPointerNoargCall5() throws IOException, URISyntaxException { + compileAndCompare("function-pointer-noarg-call-5"); + } + @Test public void testFunctionPointerNoargCall4() throws IOException, URISyntaxException { compileAndCompare("function-pointer-noarg-call-4"); diff --git a/src/test/kc/function-pointer-noarg-call-5.kc b/src/test/kc/function-pointer-noarg-call-5.kc new file mode 100644 index 000000000..a2d293174 --- /dev/null +++ b/src/test/kc/function-pointer-noarg-call-5.kc @@ -0,0 +1,23 @@ +// Tests calling into arrays of pointers to non-args no-return functions + + +void()*[2] fns = { &fn1, &fn2}; + +void main() { + byte i = 0; + while(true) { + void()* f = fns[(++i&1)<<1]; + (*f)(); + } +} + +void fn1() { + const byte* BORDERCOL = $d020; + (*BORDERCOL)++; +} + +void fn2() { + const byte* BGCOL = $d021; + (*BGCOL)++; +} + diff --git a/src/test/ref/function-pointer-noarg-call-5.asm b/src/test/ref/function-pointer-noarg-call-5.asm new file mode 100644 index 000000000..a85322926 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-5.asm @@ -0,0 +1,33 @@ +// Tests calling into arrays of pointers to non-args no-return functions +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +main: { + .label f = 2 + ldx #0 + b2: + inx + txa + and #1 + asl + tay + lda fns,y + sta f + lda fns+1,y + sta f+1 + jsr bi_f + jmp b2 + bi_f: + jmp (f) +} +fn2: { + .label BGCOL = $d021 + inc BGCOL + rts +} +fn1: { + .label BORDERCOL = $d020 + inc BORDERCOL + rts +} + fns: .word fn1, fn2 diff --git a/src/test/ref/function-pointer-noarg-call-5.cfg b/src/test/ref/function-pointer-noarg-call-5.cfg new file mode 100644 index 000000000..955f5a608 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-5.cfg @@ -0,0 +1,34 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@2/(byte) main::i#1 ) + to:main::@2 +main::@2: scope:[main] from main::@1 + [6] (byte) main::i#1 ← ++ (byte) main::i#2 + [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 + [8] (byte~) main::$1 ← (byte~) main::$0 << (byte/signed byte/word/signed word/dword/signed dword) 1 + [9] (void()*) main::f#0 ← *((const void()*[2]) fns#0 + (byte~) main::$1) + [10] call *((void()*) main::f#0) + to:main::@1 +fn2: scope:[fn2] from + [11] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + [12] return + to:@return +fn1: scope:[fn1] from + [13] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + [14] return + to:@return diff --git a/src/test/ref/function-pointer-noarg-call-5.log b/src/test/ref/function-pointer-noarg-call-5.log new file mode 100644 index 000000000..c87cd2ade --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-5.log @@ -0,0 +1,505 @@ +Resolved forward reference fn1 to (void()) fn1() +Resolved forward reference fn2 to (void()) fn2() + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (void()*~) $0 ← & (void()) fn1() + (void()*~) $1 ← & (void()) fn2() + (void()*[2]) fns#0 ← { (void()*~) $0, (void()*~) $1 } + to:@3 +main: scope:[main] from @3 + (byte) main::i#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + to:main::@1 +main::@1: scope:[main] from main main::@2 + (byte) main::i#3 ← phi( main/(byte) main::i#0 main::@2/(byte) main::i#1 ) + if(true) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte) main::i#2 ← phi( main::@1/(byte) main::i#3 ) + (byte) main::i#1 ← ++ (byte) main::i#2 + (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte~) main::$1 ← (byte~) main::$0 << (byte/signed byte/word/signed word/dword/signed dword) 1 + (void()*) main::f#0 ← *((void()*[2]) fns#0 + (byte~) main::$1) + call *((void()*) main::f#0) + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return +fn1: scope:[fn1] from + (byte*) fn1::BORDERCOL#0 ← ((byte*)) (word/dword/signed dword) $d020 + *((byte*) fn1::BORDERCOL#0) ← ++ *((byte*) fn1::BORDERCOL#0) + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + return + to:@return +fn2: scope:[fn2] from + (byte*) fn2::BGCOL#0 ← ((byte*)) (word/dword/signed dword) $d021 + *((byte*) fn2::BGCOL#0) ← ++ *((byte*) fn2::BGCOL#0) + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + return + to:@return +@3: scope:[] from @begin + call main + to:@4 +@4: scope:[] from @3 + to:@end +@end: scope:[] from @4 + +SYMBOL TABLE SSA +(void()*~) $0 +(void()*~) $1 +(label) @3 +(label) @4 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@return +(byte*) fn1::BORDERCOL +(byte*) fn1::BORDERCOL#0 +(void()) fn2() +(label) fn2::@return +(byte*) fn2::BGCOL +(byte*) fn2::BGCOL#0 +(void()*[2]) fns +(void()*[2]) fns#0 +(void()) main() +(byte~) main::$0 +(byte~) main::$1 +(label) main::@1 +(label) main::@2 +(label) main::@return +(void()*) main::f +(void()*) main::f#0 +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 + +Culled Empty Block (label) @4 +Successful SSA optimization Pass2CullEmptyBlocks +Alias (byte) main::i#2 = (byte) main::i#3 +Successful SSA optimization Pass2AliasElimination +Constant (const void()*) $0 = &fn1 +Constant (const void()*) $1 = &fn2 +Constant (const byte) main::i#0 = 0 +Constant (const byte*) fn1::BORDERCOL#0 = ((byte*))$d020 +Constant (const byte*) fn2::BGCOL#0 = ((byte*))$d021 +Successful SSA optimization Pass2ConstantIdentification +Constant (const void()*[2]) fns#0 = { $0, $1 } +Successful SSA optimization Pass2ConstantIdentification +if() condition always true - replacing block destination [1] if(true) goto main::@2 +Successful SSA optimization Pass2ConstantIfs +Removing unused block main::@return +Successful SSA optimization Pass2EliminateUnusedBlocks +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined $0 = &(void()) fn1() +Constant inlined main::i#0 = (byte/signed byte/word/signed word/dword/signed dword) 0 +Constant inlined $1 = &(void()) fn2() +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @3 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Coalesced [11] main::i#4 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Renumbering block @3 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 + +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::@2 + [5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@2/(byte) main::i#1 ) + to:main::@2 +main::@2: scope:[main] from main::@1 + [6] (byte) main::i#1 ← ++ (byte) main::i#2 + [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 + [8] (byte~) main::$1 ← (byte~) main::$0 << (byte/signed byte/word/signed word/dword/signed dword) 1 + [9] (void()*) main::f#0 ← *((const void()*[2]) fns#0 + (byte~) main::$1) + [10] call *((void()*) main::f#0) + to:main::@1 +fn2: scope:[fn2] from + [11] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) + to:fn2::@return +fn2::@return: scope:[fn2] from fn2 + [12] return + to:@return +fn1: scope:[fn1] from + [13] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) + to:fn1::@return +fn1::@return: scope:[fn1] from fn1 + [14] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(void()) fn1() +(byte*) fn1::BORDERCOL +(void()) fn2() +(byte*) fn2::BGCOL +(void()*[2]) fns +(void()) main() +(byte~) main::$0 22.0 +(byte~) main::$1 22.0 +(void()*) main::f +(void()*) main::f#0 11.0 +(byte) main::i +(byte) main::i#1 6.6000000000000005 +(byte) main::i#2 22.0 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Added variable main::$0 to zero page equivalence class [ main::$0 ] +Added variable main::$1 to zero page equivalence class [ main::$1 ] +Added variable main::f#0 to zero page equivalence class [ main::f#0 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ main::$0 ] +[ main::$1 ] +[ main::f#0 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Allocated zp ZP_BYTE:3 [ main::$0 ] +Allocated zp ZP_BYTE:4 [ main::$1 ] +Allocated zp ZP_WORD:5 [ main::f#0 ] + +INITIAL ASM +//SEG0 File Comments +// Tests calling into arrays of pointers to non-args no-return functions +//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 _0 = 3 + .label _1 = 4 + .label i = 2 + .label f = 5 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta i + jmp b1 + //SEG13 main::@1 + b1: + jmp b2 + //SEG14 main::@2 + b2: + //SEG15 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + //SEG16 [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuz1=vbuz2_band_vbuc1 + lda #1 + and i + sta _0 + //SEG17 [8] (byte~) main::$1 ← (byte~) main::$0 << (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuz1=vbuz2_rol_1 + lda _0 + asl + sta _1 + //SEG18 [9] (void()*) main::f#0 ← *((const void()*[2]) fns#0 + (byte~) main::$1) -- pprz1=pptc1_derefidx_vbuz2 + ldy _1 + lda fns,y + sta f + lda fns+1,y + sta f+1 + //SEG19 [10] call *((void()*) main::f#0) + jsr bi_f + //SEG20 [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + //SEG21 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 + bi_f: + jmp (f) +} +//SEG22 fn2 +fn2: { + .label BGCOL = $d021 + //SEG23 [11] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + jmp breturn + //SEG24 fn2::@return + breturn: + //SEG25 [12] return + rts +} +//SEG26 fn1 +fn1: { + .label BORDERCOL = $d020 + //SEG27 [13] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + jmp breturn + //SEG28 fn1::@return + breturn: + //SEG29 [14] return + rts +} + fns: .word fn1, fn2 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [8] (byte~) main::$1 ← (byte~) main::$0 << (byte/signed byte/word/signed word/dword/signed dword) 1 [ main::i#1 main::$1 ] ( main:2 [ main::i#1 main::$1 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Statement [9] (void()*) main::f#0 ← *((const void()*[2]) fns#0 + (byte~) main::$1) [ main::i#1 main::f#0 ] ( main:2 [ main::i#1 main::f#0 ] ) always clobbers reg byte a +Statement [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 [ main::i#1 main::$0 ] ( main:2 [ main::i#1 main::$0 ] ) always clobbers reg byte a +Statement [8] (byte~) main::$1 ← (byte~) main::$0 << (byte/signed byte/word/signed word/dword/signed dword) 1 [ main::i#1 main::$1 ] ( main:2 [ main::i#1 main::$1 ] ) always clobbers reg byte a +Statement [9] (void()*) main::f#0 ← *((const void()*[2]) fns#0 + (byte~) main::$1) [ main::i#1 main::f#0 ] ( main:2 [ main::i#1 main::f#0 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:3 [ main::$0 ] : zp ZP_BYTE:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:4 [ main::$1 ] : zp ZP_BYTE:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_WORD:5 [ main::f#0 ] : zp ZP_WORD:5 , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 28.6: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 22: zp ZP_BYTE:3 [ main::$0 ] 22: zp ZP_BYTE:4 [ main::$1 ] 11: zp ZP_WORD:5 [ main::f#0 ] +Uplift Scope [fn1] +Uplift Scope [fn2] +Uplift Scope [] + +Uplifting [main] best 512 combination reg byte x [ main::i#2 main::i#1 ] reg byte a [ main::$0 ] reg byte a [ main::$1 ] zp ZP_WORD:5 [ main::f#0 ] +Uplifting [fn1] best 512 combination +Uplifting [fn2] best 512 combination +Uplifting [] best 512 combination +Allocated (was zp ZP_WORD:5) zp ZP_WORD:2 [ main::f#0 ] + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Tests calling into arrays of pointers to non-args no-return functions +//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 f = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + //SEG13 main::@1 + b1: + jmp b2 + //SEG14 main::@2 + b2: + //SEG15 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG16 [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuxx_band_vbuc1 + txa + and #1 + //SEG17 [8] (byte~) main::$1 ← (byte~) main::$0 << (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuaa_rol_1 + asl + //SEG18 [9] (void()*) main::f#0 ← *((const void()*[2]) fns#0 + (byte~) main::$1) -- pprz1=pptc1_derefidx_vbuaa + tay + lda fns,y + sta f + lda fns+1,y + sta f+1 + //SEG19 [10] call *((void()*) main::f#0) + jsr bi_f + //SEG20 [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + //SEG21 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 + bi_f: + jmp (f) +} +//SEG22 fn2 +fn2: { + .label BGCOL = $d021 + //SEG23 [11] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + jmp breturn + //SEG24 fn2::@return + breturn: + //SEG25 [12] return + rts +} +//SEG26 fn1 +fn1: { + .label BORDERCOL = $d020 + //SEG27 [13] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + jmp breturn + //SEG28 fn1::@return + breturn: + //SEG29 [14] return + rts +} + fns: .word fn1, fn2 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp b2 +Removing instruction jmp breturn +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b1 with b2 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction b1_from_b2: +Removing instruction breturn: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@return +(byte*) fn1::BORDERCOL +(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020 +(void()) fn2() +(label) fn2::@return +(byte*) fn2::BGCOL +(const byte*) fn2::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) $d021 +(void()*[2]) fns +(const void()*[2]) fns#0 fns = { &(void()) fn1(), &(void()) fn2() } +(void()) main() +(byte~) main::$0 reg byte a 22.0 +(byte~) main::$1 reg byte a 22.0 +(label) main::@1 +(label) main::@2 +(void()*) main::f +(void()*) main::f#0 f zp ZP_WORD:2 11.0 +(byte) main::i +(byte) main::i#1 reg byte x 6.6000000000000005 +(byte) main::i#2 reg byte x 22.0 + +reg byte x [ main::i#2 main::i#1 ] +reg byte a [ main::$0 ] +reg byte a [ main::$1 ] +zp ZP_WORD:2 [ main::f#0 ] + + +FINAL ASSEMBLER +Score: 434 + +//SEG0 File Comments +// Tests calling into arrays of pointers to non-args no-return functions +//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 f = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + //SEG13 main::@1 + //SEG14 main::@2 + b2: + //SEG15 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG16 [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuxx_band_vbuc1 + txa + and #1 + //SEG17 [8] (byte~) main::$1 ← (byte~) main::$0 << (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuaa_rol_1 + asl + //SEG18 [9] (void()*) main::f#0 ← *((const void()*[2]) fns#0 + (byte~) main::$1) -- pprz1=pptc1_derefidx_vbuaa + tay + lda fns,y + sta f + lda fns+1,y + sta f+1 + //SEG19 [10] call *((void()*) main::f#0) + jsr bi_f + //SEG20 [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + //SEG21 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b2 + bi_f: + jmp (f) +} +//SEG22 fn2 +fn2: { + .label BGCOL = $d021 + //SEG23 [11] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BGCOL + //SEG24 fn2::@return + //SEG25 [12] return + rts +} +//SEG26 fn1 +fn1: { + .label BORDERCOL = $d020 + //SEG27 [13] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1 + inc BORDERCOL + //SEG28 fn1::@return + //SEG29 [14] return + rts +} + fns: .word fn1, fn2 + diff --git a/src/test/ref/function-pointer-noarg-call-5.sym b/src/test/ref/function-pointer-noarg-call-5.sym new file mode 100644 index 000000000..cacc04798 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-5.sym @@ -0,0 +1,28 @@ +(label) @1 +(label) @begin +(label) @end +(void()) fn1() +(label) fn1::@return +(byte*) fn1::BORDERCOL +(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020 +(void()) fn2() +(label) fn2::@return +(byte*) fn2::BGCOL +(const byte*) fn2::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) $d021 +(void()*[2]) fns +(const void()*[2]) fns#0 fns = { &(void()) fn1(), &(void()) fn2() } +(void()) main() +(byte~) main::$0 reg byte a 22.0 +(byte~) main::$1 reg byte a 22.0 +(label) main::@1 +(label) main::@2 +(void()*) main::f +(void()*) main::f#0 f zp ZP_WORD:2 11.0 +(byte) main::i +(byte) main::i#1 reg byte x 6.6000000000000005 +(byte) main::i#2 reg byte x 22.0 + +reg byte x [ main::i#2 main::i#1 ] +reg byte a [ main::$0 ] +reg byte a [ main::$1 ] +zp ZP_WORD:2 [ main::f#0 ]