diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 0a2a00403..30450165b 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -42,6 +42,11 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testStrcpy0() throws IOException, URISyntaxException { + compileAndCompare("strcpy-0.c"); + } + @Test public void testStaticInitCode() throws IOException, URISyntaxException { compileAndCompare("static-init-code.c"); diff --git a/src/test/kc/strcpy-0.c b/src/test/kc/strcpy-0.c new file mode 100644 index 000000000..ad3a188b5 --- /dev/null +++ b/src/test/kc/strcpy-0.c @@ -0,0 +1,13 @@ + +char* dst1 = (char*)0x0400; +char* dst2 = (char*)0x0428; + +void str_cpy( char *dst, char const *src ) { + while ( *dst++ = *src++ ) {} +} + +void main() { + str_cpy(dst1, "hello"); + str_cpy(dst2, "world"); +} + diff --git a/src/test/ref/strcpy-0.asm b/src/test/ref/strcpy-0.asm new file mode 100644 index 000000000..863fe0a95 --- /dev/null +++ b/src/test/ref/strcpy-0.asm @@ -0,0 +1,57 @@ +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label dst1 = $400 + .label dst2 = $428 +main: { + // str_cpy(dst1, "hello") + lda #dst1 + sta.z str_cpy.dst+1 + lda #src + sta.z str_cpy.src+1 + jsr str_cpy + // str_cpy(dst2, "world") + lda #dst2 + sta.z str_cpy.dst+1 + lda #src1 + sta.z str_cpy.src+1 + jsr str_cpy + // } + rts + src: .text "hello" + .byte 0 + src1: .text "world" + .byte 0 +} +// str_cpy(byte* zp(4) dst, byte* zp(2) src) +str_cpy: { + .label dst = 4 + .label src = 2 + __b1: + // *dst++ = *src++ + ldy #0 + lda (src),y + sta (dst),y + // while ( *dst++ = *src++ ) + lda (dst),y + inc.z dst + bne !+ + inc.z dst+1 + !: + inc.z src + bne !+ + inc.z src+1 + !: + cmp #0 + bne __b1 + // } + rts +} diff --git a/src/test/ref/strcpy-0.cfg b/src/test/ref/strcpy-0.cfg new file mode 100644 index 000000000..a34ee0f58 --- /dev/null +++ b/src/test/ref/strcpy-0.cfg @@ -0,0 +1,40 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] phi() + [5] call str_cpy + to:main::@1 +main::@1: scope:[main] from main + [6] phi() + [7] call str_cpy + to:main::@return +main::@return: scope:[main] from main::@1 + [8] return + to:@return + +(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src) +str_cpy: scope:[str_cpy] from main main::@1 + [9] (byte*) str_cpy::dst#4 ← phi( main/(const byte*) dst1 main::@1/(const byte*) dst2 ) + [9] (to_nomodify byte*) str_cpy::src#4 ← phi( main/(const byte*) main::src main::@1/(const byte*) main::src1 ) + to:str_cpy::@1 +str_cpy::@1: scope:[str_cpy] from str_cpy str_cpy::@1 + [10] (byte*) str_cpy::dst#3 ← phi( str_cpy/(byte*) str_cpy::dst#4 str_cpy::@1/(byte*) str_cpy::dst#0 ) + [10] (to_nomodify byte*) str_cpy::src#3 ← phi( str_cpy/(to_nomodify byte*) str_cpy::src#4 str_cpy::@1/(to_nomodify byte*) str_cpy::src#0 ) + [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3) + [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3) + [13] (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3 + [14] (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3 + [15] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1 + to:str_cpy::@return +str_cpy::@return: scope:[str_cpy] from str_cpy::@1 + [16] return + to:@return diff --git a/src/test/ref/strcpy-0.log b/src/test/ref/strcpy-0.log new file mode 100644 index 000000000..f326ccc6e --- /dev/null +++ b/src/test/ref/strcpy-0.log @@ -0,0 +1,598 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 + +(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src) +str_cpy: scope:[str_cpy] from main main::@1 + (byte*) str_cpy::dst#4 ← phi( main/(byte*) str_cpy::dst#1 main::@1/(byte*) str_cpy::dst#2 ) + (to_nomodify byte*) str_cpy::src#4 ← phi( main/(to_nomodify byte*) str_cpy::src#1 main::@1/(to_nomodify byte*) str_cpy::src#2 ) + to:str_cpy::@1 +str_cpy::@1: scope:[str_cpy] from str_cpy str_cpy::@1 + (byte*) str_cpy::dst#3 ← phi( str_cpy/(byte*) str_cpy::dst#4 str_cpy::@1/(byte*) str_cpy::dst#0 ) + (to_nomodify byte*) str_cpy::src#3 ← phi( str_cpy/(to_nomodify byte*) str_cpy::src#4 str_cpy::@1/(to_nomodify byte*) str_cpy::src#0 ) + *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3) + (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3) + (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3 + (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3 + (bool~) str_cpy::$1 ← (number) 0 != (byte~) str_cpy::$0 + if((bool~) str_cpy::$1) goto str_cpy::@1 + to:str_cpy::@return +str_cpy::@return: scope:[str_cpy] from str_cpy::@1 + return + to:@return + +(void()) main() +main: scope:[main] from @1 + (byte*) str_cpy::dst#1 ← (const byte*) dst1 + (to_nomodify byte*) str_cpy::src#1 ← (const byte*) main::src + call str_cpy + to:main::@1 +main::@1: scope:[main] from main + (byte*) str_cpy::dst#2 ← (const byte*) dst2 + (to_nomodify byte*) str_cpy::src#2 ← (const byte*) main::src1 + call str_cpy + to:main::@2 +main::@2: scope:[main] from main::@1 + to:main::@return +main::@return: scope:[main] from main::@2 + return + to:@return +@1: scope:[] from @begin + call main + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(const byte*) dst1 = (byte*)(number) $400 +(const byte*) dst2 = (byte*)(number) $428 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(const byte*) main::src[(byte) 6] = (byte*) "hello" +(const byte*) main::src1[(byte) 6] = (byte*) "world" +(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src) +(byte~) str_cpy::$0 +(bool~) str_cpy::$1 +(label) str_cpy::@1 +(label) str_cpy::@return +(byte*) str_cpy::dst +(byte*) str_cpy::dst#0 +(byte*) str_cpy::dst#1 +(byte*) str_cpy::dst#2 +(byte*) str_cpy::dst#3 +(byte*) str_cpy::dst#4 +(to_nomodify byte*) str_cpy::src +(to_nomodify byte*) str_cpy::src#0 +(to_nomodify byte*) str_cpy::src#1 +(to_nomodify byte*) str_cpy::src#2 +(to_nomodify byte*) str_cpy::src#3 +(to_nomodify byte*) str_cpy::src#4 + +Adding number conversion cast (unumber) 0 in (bool~) str_cpy::$1 ← (number) 0 != (byte~) str_cpy::$0 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant pointer cast (byte*) 1064 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simple Condition (bool~) str_cpy::$1 [7] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) str_cpy::dst#1 = dst1 +Constant (const to_nomodify byte*) str_cpy::src#1 = main::src +Constant (const byte*) str_cpy::dst#2 = dst2 +Constant (const to_nomodify byte*) str_cpy::src#2 = main::src1 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings (const byte*) str_cpy::dst#1 +Inlining constant with var siblings (const to_nomodify byte*) str_cpy::src#1 +Inlining constant with var siblings (const byte*) str_cpy::dst#2 +Inlining constant with var siblings (const to_nomodify byte*) str_cpy::src#2 +Constant inlined str_cpy::src#1 = (const byte*) main::src +Constant inlined str_cpy::src#2 = (const byte*) main::src1 +Constant inlined str_cpy::dst#1 = (const byte*) dst1 +Constant inlined str_cpy::dst#2 = (const byte*) dst2 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting str_cpy::@2(between str_cpy::@1 and str_cpy::@1) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@1 +Adding NOP phi() at start of main::@2 +CALL GRAPH +Calls in [] to main:2 +Calls in [main] to str_cpy:6 str_cpy:8 + +Created 4 initial phi equivalence classes +Coalesced [12] str_cpy::src#5 ← str_cpy::src#4 +Coalesced [13] str_cpy::dst#5 ← str_cpy::dst#4 +Coalesced [21] str_cpy::src#6 ← str_cpy::src#0 +Coalesced [22] str_cpy::dst#6 ← str_cpy::dst#0 +Coalesced down to 2 phi equivalence classes +Culled Empty Block (label) @2 +Culled Empty Block (label) main::@2 +Culled Empty Block (label) str_cpy::@2 +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 main::@1 + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] phi() + [5] call str_cpy + to:main::@1 +main::@1: scope:[main] from main + [6] phi() + [7] call str_cpy + to:main::@return +main::@return: scope:[main] from main::@1 + [8] return + to:@return + +(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src) +str_cpy: scope:[str_cpy] from main main::@1 + [9] (byte*) str_cpy::dst#4 ← phi( main/(const byte*) dst1 main::@1/(const byte*) dst2 ) + [9] (to_nomodify byte*) str_cpy::src#4 ← phi( main/(const byte*) main::src main::@1/(const byte*) main::src1 ) + to:str_cpy::@1 +str_cpy::@1: scope:[str_cpy] from str_cpy str_cpy::@1 + [10] (byte*) str_cpy::dst#3 ← phi( str_cpy/(byte*) str_cpy::dst#4 str_cpy::@1/(byte*) str_cpy::dst#0 ) + [10] (to_nomodify byte*) str_cpy::src#3 ← phi( str_cpy/(to_nomodify byte*) str_cpy::src#4 str_cpy::@1/(to_nomodify byte*) str_cpy::src#0 ) + [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3) + [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3) + [13] (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3 + [14] (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3 + [15] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1 + to:str_cpy::@return +str_cpy::@return: scope:[str_cpy] from str_cpy::@1 + [16] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src) +(byte~) str_cpy::$0 667.3333333333334 +(byte*) str_cpy::dst +(byte*) str_cpy::dst#0 667.3333333333334 +(byte*) str_cpy::dst#3 1368.3333333333335 +(byte*) str_cpy::dst#4 101.0 +(to_nomodify byte*) str_cpy::src +(to_nomodify byte*) str_cpy::src#0 1001.0 +(to_nomodify byte*) str_cpy::src#3 776.0 +(to_nomodify byte*) str_cpy::src#4 101.0 + +Initial phi equivalence classes +[ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ] +[ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ] +Added variable str_cpy::$0 to live range equivalence class [ str_cpy::$0 ] +Complete equivalence classes +[ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ] +[ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ] +[ str_cpy::$0 ] +Allocated zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ] +Allocated zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ] +Allocated zp[1]:6 [ str_cpy::$0 ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label dst1 = $400 + .label dst2 = $428 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from___b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // main +main: { + // [5] call str_cpy + // [9] phi from main to str_cpy [phi:main->str_cpy] + str_cpy_from_main: + // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst1 [phi:main->str_cpy#0] -- pbuz1=pbuc1 + lda #dst1 + sta.z str_cpy.dst+1 + // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src [phi:main->str_cpy#1] -- pbuz1=pbuc1 + lda #src + sta.z str_cpy.src+1 + jsr str_cpy + // [6] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + jmp __b1 + // main::@1 + __b1: + // [7] call str_cpy + // [9] phi from main::@1 to str_cpy [phi:main::@1->str_cpy] + str_cpy_from___b1: + // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst2 [phi:main::@1->str_cpy#0] -- pbuz1=pbuc1 + lda #dst2 + sta.z str_cpy.dst+1 + // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src1 [phi:main::@1->str_cpy#1] -- pbuz1=pbuc1 + lda #src1 + sta.z str_cpy.src+1 + jsr str_cpy + jmp __breturn + // main::@return + __breturn: + // [8] return + rts + src: .text "hello" + .byte 0 + src1: .text "world" + .byte 0 +} + // str_cpy +// str_cpy(byte* zp(4) dst, byte* zp(2) src) +str_cpy: { + .label __0 = 6 + .label dst = 4 + .label src = 2 + // [10] phi from str_cpy str_cpy::@1 to str_cpy::@1 [phi:str_cpy/str_cpy::@1->str_cpy::@1] + __b1_from_str_cpy: + __b1_from___b1: + // [10] phi (byte*) str_cpy::dst#3 = (byte*) str_cpy::dst#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#0] -- register_copy + // [10] phi (to_nomodify byte*) str_cpy::src#3 = (to_nomodify byte*) str_cpy::src#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#1] -- register_copy + jmp __b1 + // str_cpy::@1 + __b1: + // [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (src),y + ldy #0 + sta (dst),y + // [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3) -- vbuz1=_deref_pbuz2 + ldy #0 + lda (dst),y + sta.z __0 + // [13] (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3 -- pbuz1=_inc_pbuz1 + inc.z dst + bne !+ + inc.z dst+1 + !: + // [14] (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3 -- pbuz1=_inc_pbuz1 + inc.z src + bne !+ + inc.z src+1 + !: + // [15] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1 -- vbuc1_neq_vbuz1_then_la1 + lda #0 + cmp.z __0 + bne __b1_from___b1 + jmp __breturn + // str_cpy::@return + __breturn: + // [16] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3) [ str_cpy::src#3 str_cpy::dst#3 ] ( main:2::str_cpy:5 [ str_cpy::src#3 str_cpy::dst#3 ] { } main:2::str_cpy:7 [ str_cpy::src#3 str_cpy::dst#3 ] { } ) always clobbers reg byte a reg byte y +Statement [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3) [ str_cpy::src#3 str_cpy::dst#3 str_cpy::$0 ] ( main:2::str_cpy:5 [ str_cpy::src#3 str_cpy::dst#3 str_cpy::$0 ] { } main:2::str_cpy:7 [ str_cpy::src#3 str_cpy::dst#3 str_cpy::$0 ] { } ) always clobbers reg byte a reg byte y +Potential registers zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ] : zp[2]:2 , +Potential registers zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ] : zp[2]:4 , +Potential registers zp[1]:6 [ str_cpy::$0 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [str_cpy] 2,136.67: zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ] 1,878: zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ] 667.33: zp[1]:6 [ str_cpy::$0 ] +Uplift Scope [main] +Uplift Scope [] + +Uplifting [str_cpy] best 667 combination zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ] zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ] reg byte a [ str_cpy::$0 ] +Uplifting [main] best 667 combination +Uplifting [] best 667 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label dst1 = $400 + .label dst2 = $428 + // @begin +__bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +__b1_from___bbegin: + jmp __b1 + // @1 +__b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from___b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // main +main: { + // [5] call str_cpy + // [9] phi from main to str_cpy [phi:main->str_cpy] + str_cpy_from_main: + // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst1 [phi:main->str_cpy#0] -- pbuz1=pbuc1 + lda #dst1 + sta.z str_cpy.dst+1 + // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src [phi:main->str_cpy#1] -- pbuz1=pbuc1 + lda #src + sta.z str_cpy.src+1 + jsr str_cpy + // [6] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + jmp __b1 + // main::@1 + __b1: + // [7] call str_cpy + // [9] phi from main::@1 to str_cpy [phi:main::@1->str_cpy] + str_cpy_from___b1: + // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst2 [phi:main::@1->str_cpy#0] -- pbuz1=pbuc1 + lda #dst2 + sta.z str_cpy.dst+1 + // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src1 [phi:main::@1->str_cpy#1] -- pbuz1=pbuc1 + lda #src1 + sta.z str_cpy.src+1 + jsr str_cpy + jmp __breturn + // main::@return + __breturn: + // [8] return + rts + src: .text "hello" + .byte 0 + src1: .text "world" + .byte 0 +} + // str_cpy +// str_cpy(byte* zp(4) dst, byte* zp(2) src) +str_cpy: { + .label dst = 4 + .label src = 2 + // [10] phi from str_cpy str_cpy::@1 to str_cpy::@1 [phi:str_cpy/str_cpy::@1->str_cpy::@1] + __b1_from_str_cpy: + __b1_from___b1: + // [10] phi (byte*) str_cpy::dst#3 = (byte*) str_cpy::dst#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#0] -- register_copy + // [10] phi (to_nomodify byte*) str_cpy::src#3 = (to_nomodify byte*) str_cpy::src#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#1] -- register_copy + jmp __b1 + // str_cpy::@1 + __b1: + // [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (src),y + ldy #0 + sta (dst),y + // [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3) -- vbuaa=_deref_pbuz1 + ldy #0 + lda (dst),y + // [13] (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3 -- pbuz1=_inc_pbuz1 + inc.z dst + bne !+ + inc.z dst+1 + !: + // [14] (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3 -- pbuz1=_inc_pbuz1 + inc.z src + bne !+ + inc.z src+1 + !: + // [15] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1 -- vbuc1_neq_vbuaa_then_la1 + cmp #0 + bne __b1_from___b1 + jmp __breturn + // str_cpy::@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 __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction ldy #0 +Removing instruction ldy #0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +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_main: +Removing instruction str_cpy_from___b1: +Removing instruction __b1_from_str_cpy: +Removing instruction __b1_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __bbegin: +Removing instruction __bend: +Removing instruction str_cpy_from_main: +Removing instruction __b1: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const byte*) dst1 = (byte*) 1024 +(const byte*) dst2 = (byte*) 1064 +(void()) main() +(label) main::@1 +(label) main::@return +(const byte*) main::src[(byte) 6] = (byte*) "hello" +(const byte*) main::src1[(byte) 6] = (byte*) "world" +(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src) +(byte~) str_cpy::$0 reg byte a 667.3333333333334 +(label) str_cpy::@1 +(label) str_cpy::@return +(byte*) str_cpy::dst +(byte*) str_cpy::dst#0 dst zp[2]:4 667.3333333333334 +(byte*) str_cpy::dst#3 dst zp[2]:4 1368.3333333333335 +(byte*) str_cpy::dst#4 dst zp[2]:4 101.0 +(to_nomodify byte*) str_cpy::src +(to_nomodify byte*) str_cpy::src#0 src zp[2]:2 1001.0 +(to_nomodify byte*) str_cpy::src#3 src zp[2]:2 776.0 +(to_nomodify byte*) str_cpy::src#4 src zp[2]:2 101.0 + +zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ] +zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ] +reg byte a [ str_cpy::$0 ] + + +FINAL ASSEMBLER +Score: 549 + + // File Comments + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label dst1 = $400 + .label dst2 = $428 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + // str_cpy(dst1, "hello") + // [5] call str_cpy + // [9] phi from main to str_cpy [phi:main->str_cpy] + // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst1 [phi:main->str_cpy#0] -- pbuz1=pbuc1 + lda #dst1 + sta.z str_cpy.dst+1 + // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src [phi:main->str_cpy#1] -- pbuz1=pbuc1 + lda #src + sta.z str_cpy.src+1 + jsr str_cpy + // [6] phi from main to main::@1 [phi:main->main::@1] + // main::@1 + // str_cpy(dst2, "world") + // [7] call str_cpy + // [9] phi from main::@1 to str_cpy [phi:main::@1->str_cpy] + // [9] phi (byte*) str_cpy::dst#4 = (const byte*) dst2 [phi:main::@1->str_cpy#0] -- pbuz1=pbuc1 + lda #dst2 + sta.z str_cpy.dst+1 + // [9] phi (to_nomodify byte*) str_cpy::src#4 = (const byte*) main::src1 [phi:main::@1->str_cpy#1] -- pbuz1=pbuc1 + lda #src1 + sta.z str_cpy.src+1 + jsr str_cpy + // main::@return + // } + // [8] return + rts + src: .text "hello" + .byte 0 + src1: .text "world" + .byte 0 +} + // str_cpy +// str_cpy(byte* zp(4) dst, byte* zp(2) src) +str_cpy: { + .label dst = 4 + .label src = 2 + // [10] phi from str_cpy str_cpy::@1 to str_cpy::@1 [phi:str_cpy/str_cpy::@1->str_cpy::@1] + // [10] phi (byte*) str_cpy::dst#3 = (byte*) str_cpy::dst#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#0] -- register_copy + // [10] phi (to_nomodify byte*) str_cpy::src#3 = (to_nomodify byte*) str_cpy::src#4 [phi:str_cpy/str_cpy::@1->str_cpy::@1#1] -- register_copy + // str_cpy::@1 + __b1: + // *dst++ = *src++ + // [11] *((byte*) str_cpy::dst#3) ← *((to_nomodify byte*) str_cpy::src#3) -- _deref_pbuz1=_deref_pbuz2 + ldy #0 + lda (src),y + sta (dst),y + // while ( *dst++ = *src++ ) + // [12] (byte~) str_cpy::$0 ← *((byte*) str_cpy::dst#3) -- vbuaa=_deref_pbuz1 + lda (dst),y + // [13] (byte*) str_cpy::dst#0 ← ++ (byte*) str_cpy::dst#3 -- pbuz1=_inc_pbuz1 + inc.z dst + bne !+ + inc.z dst+1 + !: + // [14] (to_nomodify byte*) str_cpy::src#0 ← ++ (to_nomodify byte*) str_cpy::src#3 -- pbuz1=_inc_pbuz1 + inc.z src + bne !+ + inc.z src+1 + !: + // [15] if((byte) 0!=(byte~) str_cpy::$0) goto str_cpy::@1 -- vbuc1_neq_vbuaa_then_la1 + cmp #0 + bne __b1 + // str_cpy::@return + // } + // [16] return + rts +} + // File Data + diff --git a/src/test/ref/strcpy-0.sym b/src/test/ref/strcpy-0.sym new file mode 100644 index 000000000..906eefdff --- /dev/null +++ b/src/test/ref/strcpy-0.sym @@ -0,0 +1,26 @@ +(label) @1 +(label) @begin +(label) @end +(const byte*) dst1 = (byte*) 1024 +(const byte*) dst2 = (byte*) 1064 +(void()) main() +(label) main::@1 +(label) main::@return +(const byte*) main::src[(byte) 6] = (byte*) "hello" +(const byte*) main::src1[(byte) 6] = (byte*) "world" +(void()) str_cpy((byte*) str_cpy::dst , (to_nomodify byte*) str_cpy::src) +(byte~) str_cpy::$0 reg byte a 667.3333333333334 +(label) str_cpy::@1 +(label) str_cpy::@return +(byte*) str_cpy::dst +(byte*) str_cpy::dst#0 dst zp[2]:4 667.3333333333334 +(byte*) str_cpy::dst#3 dst zp[2]:4 1368.3333333333335 +(byte*) str_cpy::dst#4 dst zp[2]:4 101.0 +(to_nomodify byte*) str_cpy::src +(to_nomodify byte*) str_cpy::src#0 src zp[2]:2 1001.0 +(to_nomodify byte*) str_cpy::src#3 src zp[2]:2 776.0 +(to_nomodify byte*) str_cpy::src#4 src zp[2]:2 101.0 + +zp[2]:2 [ str_cpy::src#3 str_cpy::src#4 str_cpy::src#0 ] +zp[2]:4 [ str_cpy::dst#3 str_cpy::dst#4 str_cpy::dst#0 ] +reg byte a [ str_cpy::$0 ]