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 Replacing jump to rts with rts in jmp breturn Succesful ASM optimization Pass5DoubleJumpElimination Removing instruction jmp b1 Succesful ASM optimization Pass5NextJumpElimination Removing instruction bbegin: Removing instruction breturn: 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: 711 //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 //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 rts //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@" }