diff --git a/src/main/assembly/assembly.xml b/src/main/assembly/assembly.xml index 26a71ba4c..0c1a6209d 100644 --- a/src/main/assembly/assembly.xml +++ b/src/main/assembly/assembly.xml @@ -31,6 +31,7 @@ */*.kc */*.png + */*.sid diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index d69a4c141..04dc2d3b0 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 testLoopBreakContinue() throws IOException, URISyntaxException { + compileAndCompare("loop-break-continue"); + } + @Test public void testLoopWhileContinue() throws IOException, URISyntaxException { compileAndCompare("loop-while-continue"); diff --git a/src/test/kc/loop-break-continue.kc b/src/test/kc/loop-break-continue.kc new file mode 100644 index 000000000..4405f2c72 --- /dev/null +++ b/src/test/kc/loop-break-continue.kc @@ -0,0 +1,11 @@ +// Illustrates both break & continue statements in a loop +// Prints a message ending at '@' skipping all spaces +void main() { + byte* screen = $400; + byte[] str = "hello brave new world@"; + for( byte i: 0..255) { + if(str[i]=='@') break; + if(str[i]==' ') continue; + *screen++ = str[i]; + } +} diff --git a/src/test/ref/loop-break-continue.asm b/src/test/ref/loop-break-continue.asm new file mode 100644 index 000000000..497bb9879 --- /dev/null +++ b/src/test/ref/loop-break-continue.asm @@ -0,0 +1,38 @@ +// Illustrates both break & continue statements in a loop +// Prints a message ending at '@' skipping all spaces +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +main: { + .label screen = 2 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + ldx #0 + b1: + lda str,x + cmp #'@' + bne b2 + breturn: + rts + b2: + lda str,x + cmp #' ' + bne b3 + b4: + inx + cpx #0 + bne b1 + jmp breturn + b3: + lda str,x + ldy #0 + sta (screen),y + inc screen + bne !+ + inc screen+1 + !: + jmp b4 + str: .text "hello brave new world@" +} diff --git a/src/test/ref/loop-break-continue.cfg b/src/test/ref/loop-break-continue.cfg new file mode 100644 index 000000000..eedba7931 --- /dev/null +++ b/src/test/ref/loop-break-continue.cfg @@ -0,0 +1,32 @@ +@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::@4 + [5] (byte*) main::screen#2 ← phi( main/((byte*))(word/signed word/dword/signed dword) $400 main::@4/(byte*) main::screen#5 ) + [5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@4/(byte) main::i#1 ) + [6] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) '@') goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 main::@4 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) ' ') goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@2 main::@3 + [9] (byte*) main::screen#5 ← phi( main::@2/(byte*) main::screen#2 main::@3/(byte*) main::screen#1 ) + [10] (byte) main::i#1 ← ++ (byte) main::i#2 + [11] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@1 + to:main::@return +main::@3: scope:[main] from main::@2 + [12] *((byte*) main::screen#2) ← *((const byte[]) main::str#0 + (byte) main::i#2) + [13] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 + to:main::@4 diff --git a/src/test/ref/loop-break-continue.log b/src/test/ref/loop-break-continue.log new file mode 100644 index 000000000..9c84fa171 --- /dev/null +++ b/src/test/ref/loop-break-continue.log @@ -0,0 +1,512 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 +main: scope:[main] from @1 + (byte*) main::screen#0 ← ((byte*)) (word/signed word/dword/signed dword) $400 + (byte[]) main::str#0 ← (const string) main::$5 + (byte) main::i#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + to:main::@1 +main::@1: scope:[main] from main main::@5 + (byte*) main::screen#4 ← phi( main/(byte*) main::screen#0 main::@5/(byte*) main::screen#5 ) + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@5/(byte) main::i#1 ) + (bool~) main::$0 ← *((byte[]) main::str#0 + (byte) main::i#2) == (byte) '@' + (bool~) main::$1 ← ! (bool~) main::$0 + if((bool~) main::$1) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte*) main::screen#3 ← phi( main::@1/(byte*) main::screen#4 ) + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + (bool~) main::$2 ← *((byte[]) main::str#0 + (byte) main::i#3) == (byte) ' ' + (bool~) main::$3 ← ! (bool~) main::$2 + if((bool~) main::$3) goto main::@4 + to:main::@5 +main::@4: scope:[main] from main::@2 + (byte*) main::screen#2 ← phi( main::@2/(byte*) main::screen#3 ) + (byte) main::i#4 ← phi( main::@2/(byte) main::i#3 ) + *((byte*) main::screen#2) ← *((byte[]) main::str#0 + (byte) main::i#4) + (byte*) main::screen#1 ← ++ (byte*) main::screen#2 + to:main::@5 +main::@5: scope:[main] from main::@2 main::@4 + (byte*) main::screen#5 ← phi( main::@2/(byte*) main::screen#3 main::@4/(byte*) main::screen#1 ) + (byte) main::i#5 ← phi( main::@2/(byte) main::i#3 main::@4/(byte) main::i#4 ) + (byte) main::i#1 ← (byte) main::i#5 + rangenext(0,$ff) + (bool~) main::$4 ← (byte) main::i#1 != rangelast(0,$ff) + if((bool~) main::$4) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 main::@5 + 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 +(void()) main() +(bool~) main::$0 +(bool~) main::$1 +(bool~) main::$2 +(bool~) main::$3 +(bool~) main::$4 +(const string) main::$5 = (string) "hello brave new world@" +(label) main::@1 +(label) main::@2 +(label) main::@4 +(label) main::@5 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(byte) main::i#4 +(byte) main::i#5 +(byte*) main::screen +(byte*) main::screen#0 +(byte*) main::screen#1 +(byte*) main::screen#2 +(byte*) main::screen#3 +(byte*) main::screen#4 +(byte*) main::screen#5 +(byte[]) main::str +(byte[]) main::str#0 + +Culled Empty Block (label) @2 +Successful SSA optimization Pass2CullEmptyBlocks +Inversing boolean not [5] (bool~) main::$1 ← *((byte[]) main::str#0 + (byte) main::i#2) != (byte) '@' from [4] (bool~) main::$0 ← *((byte[]) main::str#0 + (byte) main::i#2) == (byte) '@' +Inversing boolean not [9] (bool~) main::$3 ← *((byte[]) main::str#0 + (byte) main::i#3) != (byte) ' ' from [8] (bool~) main::$2 ← *((byte[]) main::str#0 + (byte) main::i#3) == (byte) ' ' +Successful SSA optimization Pass2UnaryNotSimplification +Alias (byte) main::i#2 = (byte) main::i#3 (byte) main::i#4 +Alias (byte*) main::screen#2 = (byte*) main::screen#3 (byte*) main::screen#4 +Successful SSA optimization Pass2AliasElimination +Alias (byte) main::i#2 = (byte) main::i#5 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$1 [6] if(*((byte[]) main::str#0 + (byte) main::i#2)!=(byte) '@') goto main::@2 +Simple Condition (bool~) main::$3 [10] if(*((byte[]) main::str#0 + (byte) main::i#2)!=(byte) ' ') goto main::@4 +Simple Condition (bool~) main::$4 [17] if((byte) main::i#1!=rangelast(0,$ff)) goto main::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) main::screen#0 = ((byte*))$400 +Constant (const byte[]) main::str#0 = main::$5 +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Resolved ranged next value main::i#1 ← ++ main::i#2 to ++ +Resolved ranged comparison value if(main::i#1!=rangelast(0,$ff)) goto main::@1 to (byte/signed byte/word/signed word/dword/signed dword) 0 +Inlining constant with var siblings (const byte*) main::screen#0 +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::$5 = (const byte[]) main::str#0 +Constant inlined main::screen#0 = ((byte*))(word/signed word/dword/signed dword) $400 +Constant inlined main::i#0 = (byte/signed byte/word/signed word/dword/signed dword) 0 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@11(between main::@5 and main::@1) +Added new block during phi lifting main::@12(between main::@2 and main::@5) +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 +CALL GRAPH +Calls in [] to main:2 + +Created 3 initial phi equivalence classes +Coalesced [9] main::screen#7 ← main::screen#2 +Coalesced [13] main::i#6 ← main::i#1 +Coalesced (already) [14] main::screen#6 ← main::screen#5 +Coalesced [17] main::screen#8 ← main::screen#1 +Coalesced down to 2 phi equivalence classes +Culled Empty Block (label) main::@12 +Culled Empty Block (label) main::@11 +Renumbering block main::@4 to main::@3 +Renumbering block main::@5 to main::@4 +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::@4 + [5] (byte*) main::screen#2 ← phi( main/((byte*))(word/signed word/dword/signed dword) $400 main::@4/(byte*) main::screen#5 ) + [5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@4/(byte) main::i#1 ) + [6] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) '@') goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 main::@4 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) ' ') goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@2 main::@3 + [9] (byte*) main::screen#5 ← phi( main::@2/(byte*) main::screen#2 main::@3/(byte*) main::screen#1 ) + [10] (byte) main::i#1 ← ++ (byte) main::i#2 + [11] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@1 + to:main::@return +main::@3: scope:[main] from main::@2 + [12] *((byte*) main::screen#2) ← *((const byte[]) main::str#0 + (byte) main::i#2) + [13] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 + to:main::@4 + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#2 9.166666666666666 +(byte*) main::screen +(byte*) main::screen#1 22.0 +(byte*) main::screen#2 11.0 +(byte*) main::screen#5 11.0 +(byte[]) main::str + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ main::screen#2 main::screen#5 main::screen#1 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ main::screen#2 main::screen#5 main::screen#1 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Allocated zp ZP_WORD:3 [ main::screen#2 main::screen#5 main::screen#1 ] + +INITIAL ASM +//SEG0 File Comments +// Illustrates both break & continue statements in a loop +// Prints a message ending at '@' skipping all spaces +//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 screen = 3 + .label i = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte*) main::screen#2 = ((byte*))(word/signed word/dword/signed dword) $400 [phi:main->main::@1#0] -- pbuz1=pbuc1 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + //SEG13 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#1] -- vbuz1=vbuc1 + lda #0 + sta i + jmp b1 + //SEG14 [5] phi from main::@4 to main::@1 [phi:main::@4->main::@1] + b1_from_b4: + //SEG15 [5] phi (byte*) main::screen#2 = (byte*) main::screen#5 [phi:main::@4->main::@1#0] -- register_copy + //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@4->main::@1#1] -- register_copy + jmp b1 + //SEG17 main::@1 + b1: + //SEG18 [6] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) '@') goto main::@2 -- pbuc1_derefidx_vbuz1_neq_vbuc2_then_la1 + lda #'@' + ldy i + cmp str,y + bne b2 + jmp breturn + //SEG19 main::@return + breturn: + //SEG20 [7] return + rts + //SEG21 main::@2 + b2: + //SEG22 [8] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) ' ') goto main::@3 -- pbuc1_derefidx_vbuz1_neq_vbuc2_then_la1 + lda #' ' + ldy i + cmp str,y + bne b3 + //SEG23 [9] phi from main::@2 main::@3 to main::@4 [phi:main::@2/main::@3->main::@4] + b4_from_b2: + b4_from_b3: + //SEG24 [9] phi (byte*) main::screen#5 = (byte*) main::screen#2 [phi:main::@2/main::@3->main::@4#0] -- register_copy + jmp b4 + //SEG25 main::@4 + b4: + //SEG26 [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + //SEG27 [11] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@1 -- vbuz1_neq_0_then_la1 + lda i + cmp #0 + bne b1_from_b4 + jmp breturn + //SEG28 main::@3 + b3: + //SEG29 [12] *((byte*) main::screen#2) ← *((const byte[]) main::str#0 + (byte) main::i#2) -- _deref_pbuz1=pbuc1_derefidx_vbuz2 + ldy i + lda str,y + ldy #0 + sta (screen),y + //SEG30 [13] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 -- pbuz1=_inc_pbuz1 + inc screen + bne !+ + inc screen+1 + !: + jmp b4_from_b3 + str: .text "hello brave new world@" +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) '@') goto main::@2 [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) 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 [8] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) ' ') goto main::@3 [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) always clobbers reg byte a +Statement [12] *((byte*) main::screen#2) ← *((const byte[]) main::str#0 + (byte) main::i#2) [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) always clobbers reg byte a reg byte y +Removing always clobbered register reg byte y as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Statement [6] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) '@') goto main::@2 [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) always clobbers reg byte a +Statement [8] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) ' ') goto main::@3 [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) always clobbers reg byte a +Statement [12] *((byte*) main::screen#2) ← *((const byte[]) main::str#0 + (byte) main::i#2) [ main::i#2 main::screen#2 ] ( main:2 [ main::i#2 main::screen#2 ] ) always clobbers reg byte a reg byte y +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , +Potential registers zp ZP_WORD:3 [ main::screen#2 main::screen#5 main::screen#1 ] : zp ZP_WORD:3 , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 44: zp ZP_WORD:3 [ main::screen#2 main::screen#5 main::screen#1 ] 25.67: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 813 combination zp ZP_WORD:3 [ main::screen#2 main::screen#5 main::screen#1 ] reg byte x [ main::i#2 main::i#1 ] +Uplifting [] best 813 combination +Allocated (was zp ZP_WORD:3) zp ZP_WORD:2 [ main::screen#2 main::screen#5 main::screen#1 ] + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Illustrates both break & continue statements in a loop +// Prints a message ending at '@' skipping all spaces +//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 screen = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte*) main::screen#2 = ((byte*))(word/signed word/dword/signed dword) $400 [phi:main->main::@1#0] -- pbuz1=pbuc1 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + //SEG13 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + //SEG14 [5] phi from main::@4 to main::@1 [phi:main::@4->main::@1] + b1_from_b4: + //SEG15 [5] phi (byte*) main::screen#2 = (byte*) main::screen#5 [phi:main::@4->main::@1#0] -- register_copy + //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@4->main::@1#1] -- register_copy + jmp b1 + //SEG17 main::@1 + b1: + //SEG18 [6] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) '@') goto main::@2 -- pbuc1_derefidx_vbuxx_neq_vbuc2_then_la1 + lda str,x + cmp #'@' + bne b2 + jmp breturn + //SEG19 main::@return + breturn: + //SEG20 [7] return + rts + //SEG21 main::@2 + b2: + //SEG22 [8] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) ' ') goto main::@3 -- pbuc1_derefidx_vbuxx_neq_vbuc2_then_la1 + lda str,x + cmp #' ' + bne b3 + //SEG23 [9] phi from main::@2 main::@3 to main::@4 [phi:main::@2/main::@3->main::@4] + b4_from_b2: + b4_from_b3: + //SEG24 [9] phi (byte*) main::screen#5 = (byte*) main::screen#2 [phi:main::@2/main::@3->main::@4#0] -- register_copy + jmp b4 + //SEG25 main::@4 + b4: + //SEG26 [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG27 [11] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@1 -- vbuxx_neq_0_then_la1 + cpx #0 + bne b1_from_b4 + jmp breturn + //SEG28 main::@3 + b3: + //SEG29 [12] *((byte*) main::screen#2) ← *((const byte[]) main::str#0 + (byte) main::i#2) -- _deref_pbuz1=pbuc1_derefidx_vbuxx + lda str,x + ldy #0 + sta (screen),y + //SEG30 [13] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 -- pbuz1=_inc_pbuz1 + inc screen + bne !+ + inc screen+1 + !: + jmp b4_from_b3 + str: .text "hello brave new world@" +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp breturn +Removing instruction jmp b4 +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b1_from_b4 with b1 +Replacing label b4_from_b3 with b4 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b4: +Removing instruction b4_from_b2: +Removing instruction b4_from_b3: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +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()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 9.166666666666666 +(byte*) main::screen +(byte*) main::screen#1 screen zp ZP_WORD:2 22.0 +(byte*) main::screen#2 screen zp ZP_WORD:2 11.0 +(byte*) main::screen#5 screen zp ZP_WORD:2 11.0 +(byte[]) main::str +(const byte[]) main::str#0 str = (string) "hello brave new world@" + +reg byte x [ main::i#2 main::i#1 ] +zp ZP_WORD:2 [ main::screen#2 main::screen#5 main::screen#1 ] + + +FINAL ASSEMBLER +Score: 681 + +//SEG0 File Comments +// Illustrates both break & continue statements in a loop +// Prints a message ending at '@' skipping all spaces +//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 screen = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + //SEG12 [5] phi (byte*) main::screen#2 = ((byte*))(word/signed word/dword/signed dword) $400 [phi:main->main::@1#0] -- pbuz1=pbuc1 + lda #<$400 + sta screen + lda #>$400 + sta screen+1 + //SEG13 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 + ldx #0 + //SEG14 [5] phi from main::@4 to main::@1 [phi:main::@4->main::@1] + //SEG15 [5] phi (byte*) main::screen#2 = (byte*) main::screen#5 [phi:main::@4->main::@1#0] -- register_copy + //SEG16 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@4->main::@1#1] -- register_copy + //SEG17 main::@1 + b1: + //SEG18 [6] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) '@') goto main::@2 -- pbuc1_derefidx_vbuxx_neq_vbuc2_then_la1 + lda str,x + cmp #'@' + bne b2 + //SEG19 main::@return + breturn: + //SEG20 [7] return + rts + //SEG21 main::@2 + b2: + //SEG22 [8] if(*((const byte[]) main::str#0 + (byte) main::i#2)!=(byte) ' ') goto main::@3 -- pbuc1_derefidx_vbuxx_neq_vbuc2_then_la1 + lda str,x + cmp #' ' + bne b3 + //SEG23 [9] phi from main::@2 main::@3 to main::@4 [phi:main::@2/main::@3->main::@4] + //SEG24 [9] phi (byte*) main::screen#5 = (byte*) main::screen#2 [phi:main::@2/main::@3->main::@4#0] -- register_copy + //SEG25 main::@4 + b4: + //SEG26 [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG27 [11] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@1 -- vbuxx_neq_0_then_la1 + cpx #0 + bne b1 + jmp breturn + //SEG28 main::@3 + b3: + //SEG29 [12] *((byte*) main::screen#2) ← *((const byte[]) main::str#0 + (byte) main::i#2) -- _deref_pbuz1=pbuc1_derefidx_vbuxx + lda str,x + ldy #0 + sta (screen),y + //SEG30 [13] (byte*) main::screen#1 ← ++ (byte*) main::screen#2 -- pbuz1=_inc_pbuz1 + inc screen + bne !+ + inc screen+1 + !: + jmp b4 + str: .text "hello brave new world@" +} + diff --git a/src/test/ref/loop-break-continue.sym b/src/test/ref/loop-break-continue.sym new file mode 100644 index 000000000..74a5c3d8d --- /dev/null +++ b/src/test/ref/loop-break-continue.sym @@ -0,0 +1,21 @@ +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 9.166666666666666 +(byte*) main::screen +(byte*) main::screen#1 screen zp ZP_WORD:2 22.0 +(byte*) main::screen#2 screen zp ZP_WORD:2 11.0 +(byte*) main::screen#5 screen zp ZP_WORD:2 11.0 +(byte[]) main::str +(const byte[]) main::str#0 str = (string) "hello brave new world@" + +reg byte x [ main::i#2 main::i#1 ] +zp ZP_WORD:2 [ main::screen#2 main::screen#5 main::screen#1 ]