diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cbfe3cfd6..af5bc68ca 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,6 +17,7 @@ release: - ./kickc/jar - ./kickc/include - ./kickc/lib + - ./kickc/target - ./kickc/fragment - ./kickc/examples - ./kickc/LICENSE* diff --git a/src/main/doc/KickC Reference Manual.pdf b/src/main/doc/KickC Reference Manual.pdf index 2de5b3063..b3f000ee0 100644 Binary files a/src/main/doc/KickC Reference Manual.pdf and b/src/main/doc/KickC Reference Manual.pdf differ diff --git a/src/main/fragment/cache/fragment-cache-mos6502x.asm b/src/main/fragment/cache/fragment-cache-mos6502x.asm index 0b224df2e..3aa602817 100644 --- a/src/main/fragment/cache/fragment-cache-mos6502x.asm +++ b/src/main/fragment/cache/fragment-cache-mos6502x.asm @@ -17688,3 +17688,47 @@ sbc #2 ldx {z1} dex dex +//FRAGMENT vbuz1=_hi_pvoz2 +lda {z2}+1 +sta {z1} +//FRAGMENT vbuz1=_lo_pvoz2 +lda {z2} +sta {z1} +//FRAGMENT vbuaa=_hi_pvoz1 +lda {z1}+1 +//FRAGMENT vbuxx=_hi_pvoz1 +ldx {z1}+1 +//FRAGMENT vbuaa=_lo_pvoz1 +lda {z1} +//FRAGMENT vbuxx=_lo_pvoz1 +ldx {z1} +//FRAGMENT vbuyy=_hi_pvoz1 +ldy {z1}+1 +//FRAGMENT vbuyy=_lo_pvoz1 +ldy {z1} +//FRAGMENT vwuz1=vwuz2_rol_5 +lda {z2} +asl +sta {z1} +lda {z2}+1 +rol +sta {z1}+1 +asl {z1} +rol {z1}+1 +asl {z1} +rol {z1}+1 +asl {z1} +rol {z1}+1 +asl {z1} +rol {z1}+1 +//FRAGMENT vwuz1=vwuz1_rol_5 +asl {z1} +rol {z1}+1 +asl {z1} +rol {z1}+1 +asl {z1} +rol {z1}+1 +asl {z1} +rol {z1}+1 +asl {z1} +rol {z1}+1 diff --git a/src/main/java/dk/camelot64/kickc/model/StatementSequence.java b/src/main/java/dk/camelot64/kickc/model/StatementSequence.java index 754286877..3c406df9d 100644 --- a/src/main/java/dk/camelot64/kickc/model/StatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/model/StatementSequence.java @@ -9,7 +9,7 @@ import dk.camelot64.kickc.model.values.LabelRef; import java.util.ArrayList; import java.util.List; -/** A sequence of Statements */ +/** A sequence of statements used before pass1*/ public class StatementSequence { private ArrayList statements; diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index c8b212212..30450165b 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -42,10 +42,15 @@ public class TestPrograms { public TestPrograms() { } - //@Test - //public void testStaticInitCode() throws IOException, URISyntaxException { - // compileAndCompare("static-init-code.c", log()); - //} + @Test + public void testStrcpy0() throws IOException, URISyntaxException { + compileAndCompare("strcpy-0.c"); + } + + @Test + public void testStaticInitCode() throws IOException, URISyntaxException { + compileAndCompare("static-init-code.c"); + } @Test public void testConstParenthesis() throws IOException, URISyntaxException { @@ -79,7 +84,7 @@ public class TestPrograms { @Test public void testNesDemo() throws IOException, URISyntaxException { - compileAndCompare("examples/nes/nes-demo.c"); + compileAndCompare("examples/nes-demo/nes-demo.c"); } @Test diff --git a/src/test/kc/.vscode/cc65-x64sc.sh b/src/test/kc/.vscode/cc65-x64sc.sh new file mode 100755 index 000000000..0f5111496 --- /dev/null +++ b/src/test/kc/.vscode/cc65-x64sc.sh @@ -0,0 +1,9 @@ +#!/bin/bash +export C_FILE=$1 +export ASM_FILE=${C_FILE%.*}.s +export O_FILE=${C_FILE%.*}.o +export PRG_FILE=${C_FILE%.*}.prg +cc65 -t c64 -O -o $ASM_FILE $C_FILE +ca65 -t c64 -o $O_FILE $ASM_FILE +ld65 -t c64 -o $PRG_FILE $O_FILE c64.lib +x64sc $PRG_FILE \ No newline at end of file diff --git a/src/test/kc/.vscode/tasks.json b/src/test/kc/.vscode/tasks.json index 8896e66cd..1128741e0 100644 --- a/src/test/kc/.vscode/tasks.json +++ b/src/test/kc/.vscode/tasks.json @@ -44,6 +44,14 @@ "command": ".vscode/kickass-nes.sh", "args": [ "${file}" ] }, + { + "label": "CC65 Build & Run (C64)", + "group": "build", + "type": "process", + "problemMatcher": [], + "command": ".vscode/cc65-x64sc.sh", + "args": [ "${file}" ] + }, { "label": "KickAsm Build & Debug (C64)", "type": "shell", diff --git a/src/test/kc/examples/nes/nes-demo.c b/src/test/kc/examples/nes-demo/nes-demo.c similarity index 100% rename from src/test/kc/examples/nes/nes-demo.c rename to src/test/kc/examples/nes-demo/nes-demo.c diff --git a/src/test/kc/examples/nes/smb1_chr.bin b/src/test/kc/examples/nes-demo/smb1_chr.bin similarity index 100% rename from src/test/kc/examples/nes/smb1_chr.bin rename to src/test/kc/examples/nes-demo/smb1_chr.bin diff --git a/src/test/kc/static-init-code.c b/src/test/kc/static-init-code.c index 7a194abb2..596db897e 100644 --- a/src/test/kc/static-init-code.c +++ b/src/test/kc/static-init-code.c @@ -13,4 +13,4 @@ void main() { } // Initialize another volatile ZP-variable (will be done in the initializer) -volatile char c2 = 'y'; +volatile char c2 = SCREEN>1000?'s':'m'; 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/examples/nes/nes-demo.asm b/src/test/ref/examples/nes-demo/nes-demo.asm similarity index 100% rename from src/test/ref/examples/nes/nes-demo.asm rename to src/test/ref/examples/nes-demo/nes-demo.asm diff --git a/src/test/ref/examples/nes/nes-demo.cfg b/src/test/ref/examples/nes-demo/nes-demo.cfg similarity index 100% rename from src/test/ref/examples/nes/nes-demo.cfg rename to src/test/ref/examples/nes-demo/nes-demo.cfg diff --git a/src/test/ref/examples/nes/nes-demo.log b/src/test/ref/examples/nes-demo/nes-demo.log similarity index 100% rename from src/test/ref/examples/nes/nes-demo.log rename to src/test/ref/examples/nes-demo/nes-demo.log diff --git a/src/test/ref/examples/nes/nes-demo.sym b/src/test/ref/examples/nes-demo/nes-demo.sym similarity index 100% rename from src/test/ref/examples/nes/nes-demo.sym rename to src/test/ref/examples/nes-demo/nes-demo.sym diff --git a/src/test/ref/static-init-code.asm b/src/test/ref/static-init-code.asm new file mode 100644 index 000000000..66c3ce44a --- /dev/null +++ b/src/test/ref/static-init-code.asm @@ -0,0 +1,30 @@ +// Tests static initialization code +// Currently placed outside any function scope and pushed into @begin block. +// To be put into an initializer function. +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + .label SCREEN = $400 + .label c1 = 2 + .label c2 = 3 +__bbegin: + // c1 = 'x' + // Initialize a volatile ZP-variable (will be done in the initializer) + lda #'x' + sta.z c1 + // c2 = SCREEN>1000?'s':'m' + // Initialize another volatile ZP-variable (will be done in the initializer) + lda #'s' + sta.z c2 + jsr main + rts +main: { + // SCREEN[0] = c1 + lda.z c1 + sta SCREEN + // SCREEN[0] = c2 + lda.z c2 + sta SCREEN + // } + rts +} diff --git a/src/test/ref/static-init-code.cfg b/src/test/ref/static-init-code.cfg new file mode 100644 index 000000000..d6c46039a --- /dev/null +++ b/src/test/ref/static-init-code.cfg @@ -0,0 +1,18 @@ +@begin: scope:[] from + [0] (volatile byte) c1 ← (byte) 'x' + to:@1 +@1: scope:[] from @begin + [1] (volatile byte) c2 ← (byte) 's' + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 + [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return diff --git a/src/test/ref/static-init-code.log b/src/test/ref/static-init-code.log new file mode 100644 index 000000000..a3fa9f8f3 --- /dev/null +++ b/src/test/ref/static-init-code.log @@ -0,0 +1,328 @@ +Resolved forward reference c2 to (volatile byte) c2 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (volatile byte) c1 ← (byte) 'x' + to:@4 + +(void()) main() +main: scope:[main] from @3 + *((const nomodify byte*) SCREEN + (number) 0) ← (volatile byte) c1 + *((const nomodify byte*) SCREEN + (number) 0) ← (volatile byte) c2 + to:main::@return +main::@return: scope:[main] from main + return + to:@return +@4: scope:[] from @begin + if((const nomodify byte*) SCREEN>(number) $3e8) goto @1 + to:@2 +@1: scope:[] from @4 + (byte~) $1 ← (byte) 's' + to:@3 +@2: scope:[] from @4 + (byte~) $0 ← (byte) 'm' + to:@3 +@3: scope:[] from @1 @2 + (byte~) $2 ← phi( @1/(byte~) $1 @2/(byte~) $0 ) + (volatile byte) c2 ← (byte~) $2 + call main + to:@5 +@5: scope:[] from @3 + to:@end +@end: scope:[] from @5 + +SYMBOL TABLE SSA +(byte~) $0 +(byte~) $1 +(byte~) $2 +(label) @1 +(label) @2 +(label) @3 +(label) @4 +(label) @5 +(label) @begin +(label) @end +(const nomodify byte*) SCREEN = (byte*)(number) $400 +(volatile byte) c1 loadstore +(volatile byte) c2 loadstore +(void()) main() +(label) main::@return + +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) SCREEN + (number) 0) ← (volatile byte) c1 +Adding number conversion cast (unumber) 0 in *((const nomodify byte*) SCREEN + (number) 0) ← (volatile byte) c2 +Adding number conversion cast (unumber) $3e8 in if((const nomodify byte*) SCREEN>(number) $3e8) goto @1 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Simplifying constant integer cast $3e8 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (word) $3e8 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias candidate removed (volatile)c2 = $2 +Constant (const byte) $1 = 's' +Constant (const byte) $0 = 'm' +Successful SSA optimization Pass2ConstantIdentification +if() condition always true - replacing block destination [4] if((const nomodify byte*) SCREEN>(word) $3e8) goto @1 +Successful SSA optimization Pass2ConstantIfs +Simplifying expression containing zero SCREEN in [1] *((const nomodify byte*) SCREEN + (byte) 0) ← (volatile byte) c1 +Simplifying expression containing zero SCREEN in [2] *((const nomodify byte*) SCREEN + (byte) 0) ← (volatile byte) c2 +Successful SSA optimization PassNSimplifyExpressionWithZero +Removing PHI-reference to removed block (@2) in block @3 +Removing unused block @2 +Successful SSA optimization Pass2EliminateUnusedBlocks +Alias candidate removed (volatile)c2 = $2 +Identical Phi Values (byte~) $2 (const byte) $1 +Successful SSA optimization Pass2IdenticalPhiElimination +Eliminating unused constant (const byte) $0 +Successful SSA optimization PassNEliminateUnusedVars +Constant inlined $1 = (byte) 's' +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of @4 +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @5 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:4 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Culled Empty Block (label) @4 +Culled Empty Block (label) @1 +Culled Empty Block (label) @5 +Renumbering block @3 to @1 +Adding NOP phi() at start of @end + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] (volatile byte) c1 ← (byte) 'x' + to:@1 +@1: scope:[] from @begin + [1] (volatile byte) c2 ← (byte) 's' + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 + [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(volatile byte) c1 loadstore 6.5 +(volatile byte) c2 loadstore 6.5 +(void()) main() + +Initial phi equivalence classes +Added variable c1 to live range equivalence class [ c1 ] +Added variable c2 to live range equivalence class [ c2 ] +Complete equivalence classes +[ c1 ] +[ c2 ] +Allocated zp[1]:2 [ c1 ] +Allocated zp[1]:3 [ c2 ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Tests static initialization code +// Currently placed outside any function scope and pushed into @begin block. +// To be put into an initializer function. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label c1 = 2 + .label c2 = 3 + // @begin +__bbegin: + // [0] (volatile byte) c1 ← (byte) 'x' -- vbuz1=vbuc1 + // Initialize a volatile ZP-variable (will be done in the initializer) + lda #'x' + sta.z c1 + jmp __b1 + // @1 +__b1: + // [1] (volatile byte) c2 ← (byte) 's' -- vbuz1=vbuc1 + // Initialize another volatile ZP-variable (will be done in the initializer) + lda #'s' + sta.z c2 + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // main +main: { + // [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 -- _deref_pbuc1=vbuz1 + lda.z c1 + sta SCREEN + // [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 -- _deref_pbuc1=vbuz1 + lda.z c2 + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [6] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] (volatile byte) c1 ← (byte) 'x' [ c1 ] ( [ c1 ] { } ) always clobbers reg byte a +Statement [1] (volatile byte) c2 ← (byte) 's' [ c1 c2 ] ( [ c1 c2 ] { } ) always clobbers reg byte a +Statement [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 [ c2 ] ( main:2 [ c2 ] { } ) always clobbers reg byte a +Statement [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 [ ] ( main:2 [ ] { } ) always clobbers reg byte a +Potential registers zp[1]:2 [ c1 ] : zp[1]:2 , +Potential registers zp[1]:3 [ c2 ] : zp[1]:3 , + +REGISTER UPLIFT SCOPES +Uplift Scope [] 6.5: zp[1]:2 [ c1 ] 6.5: zp[1]:3 [ c2 ] +Uplift Scope [main] + +Uplifting [] best 45 combination zp[1]:2 [ c1 ] zp[1]:3 [ c2 ] +Uplifting [main] best 45 combination +Attempting to uplift remaining variables inzp[1]:2 [ c1 ] +Uplifting [] best 45 combination zp[1]:2 [ c1 ] +Attempting to uplift remaining variables inzp[1]:3 [ c2 ] +Uplifting [] best 45 combination zp[1]:3 [ c2 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Tests static initialization code +// Currently placed outside any function scope and pushed into @begin block. +// To be put into an initializer function. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label c1 = 2 + .label c2 = 3 + // @begin +__bbegin: + // [0] (volatile byte) c1 ← (byte) 'x' -- vbuz1=vbuc1 + // Initialize a volatile ZP-variable (will be done in the initializer) + lda #'x' + sta.z c1 + jmp __b1 + // @1 +__b1: + // [1] (volatile byte) c2 ← (byte) 's' -- vbuz1=vbuc1 + // Initialize another volatile ZP-variable (will be done in the initializer) + lda #'s' + sta.z c2 + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +__bend_from___b1: + jmp __bend + // @end +__bend: + // main +main: { + // [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 -- _deref_pbuc1=vbuz1 + lda.z c1 + sta SCREEN + // [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 -- _deref_pbuc1=vbuz1 + lda.z c2 + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [6] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __bend_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __b1: +Removing instruction __bend: +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 nomodify byte*) SCREEN = (byte*) 1024 +(volatile byte) c1 loadstore zp[1]:2 6.5 +(volatile byte) c2 loadstore zp[1]:3 6.5 +(void()) main() +(label) main::@return + +zp[1]:2 [ c1 ] +zp[1]:3 [ c2 ] + + +FINAL ASSEMBLER +Score: 42 + + // File Comments +// Tests static initialization code +// Currently placed outside any function scope and pushed into @begin block. +// To be put into an initializer function. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label c1 = 2 + .label c2 = 3 + // @begin +__bbegin: + // c1 = 'x' + // [0] (volatile byte) c1 ← (byte) 'x' -- vbuz1=vbuc1 + // Initialize a volatile ZP-variable (will be done in the initializer) + lda #'x' + sta.z c1 + // @1 + // c2 = SCREEN>1000?'s':'m' + // [1] (volatile byte) c2 ← (byte) 's' -- vbuz1=vbuc1 + // Initialize another volatile ZP-variable (will be done in the initializer) + lda #'s' + sta.z c2 + // [2] call main + jsr main + rts + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + // SCREEN[0] = c1 + // [4] *((const nomodify byte*) SCREEN) ← (volatile byte) c1 -- _deref_pbuc1=vbuz1 + lda.z c1 + sta SCREEN + // SCREEN[0] = c2 + // [5] *((const nomodify byte*) SCREEN) ← (volatile byte) c2 -- _deref_pbuc1=vbuz1 + lda.z c2 + sta SCREEN + // main::@return + // } + // [6] return + rts +} + // File Data + diff --git a/src/test/ref/static-init-code.sym b/src/test/ref/static-init-code.sym new file mode 100644 index 000000000..d1611ec31 --- /dev/null +++ b/src/test/ref/static-init-code.sym @@ -0,0 +1,11 @@ +(label) @1 +(label) @begin +(label) @end +(const nomodify byte*) SCREEN = (byte*) 1024 +(volatile byte) c1 loadstore zp[1]:2 6.5 +(volatile byte) c2 loadstore zp[1]:3 6.5 +(void()) main() +(label) main::@return + +zp[1]:2 [ c1 ] +zp[1]:3 [ c2 ] 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 ]