CONTROL FLOW GRAPH SSA void main() main: scope:[main] from __start main::j#0 = 0 main::i#0 = 0 to:main::@1 main::@1: scope:[main] from main main::@2 main::j#3 = phi( main/main::j#0, main::@2/main::j#4 ) main::i#2 = phi( main/main::i#0, main::@2/main::i#1 ) main::screen[main::i#2] = '*' main::cols[main::i#2] = main::colseq[main::j#3] main::j#1 = ++ main::j#3 main::$0 = main::j#1 == 3 main::$1 = ! main::$0 if(main::$1) goto main::@2 to:main::@3 main::@2: scope:[main] from main::@1 main::@3 main::j#4 = phi( main::@1/main::j#1, main::@3/main::j#2 ) main::i#3 = phi( main::@1/main::i#2, main::@3/main::i#4 ) main::i#1 = main::i#3 + rangenext(0,$27) main::$2 = main::i#1 != rangelast(0,$27) if(main::$2) goto main::@1 to:main::@return main::@3: scope:[main] from main::@1 main::i#4 = phi( main::@1/main::i#2 ) main::j#2 = 0 to:main::@2 main::@return: scope:[main] from main::@2 return to:@return void __start() __start: scope:[__start] from call main to:__start::@1 __start::@1: scope:[__start] from __start to:__start::@return __start::@return: scope:[__start] from __start::@1 return to:@return SYMBOL TABLE SSA __constant const char GREEN = 5 __constant const char RED = 2 __constant const char WHITE = 1 void __start() void main() bool main::$0 bool main::$1 bool main::$2 __constant char *main::cols = (char *)$d800 __constant char main::colseq[] = { WHITE, RED, GREEN } char main::i char main::i#0 char main::i#1 char main::i#2 char main::i#3 char main::i#4 char main::j char main::j#0 char main::j#1 char main::j#2 char main::j#3 char main::j#4 __constant char *main::screen = (char *)$400 Adding number conversion cast (unumber) 3 in main::$0 = main::j#1 == 3 Adding number conversion cast (unumber) 0 in main::j#2 = 0 Successful SSA optimization PassNAddNumberTypeConversions Inlining cast main::j#2 = (unumber)0 Successful SSA optimization Pass2InlineCast Simplifying constant pointer cast (char *) 1024 Simplifying constant pointer cast (char *) 55296 Simplifying constant integer cast 3 Simplifying constant integer cast 0 Successful SSA optimization PassNCastSimplification Finalized unsigned number type (char) 3 Finalized unsigned number type (char) 0 Successful SSA optimization PassNFinalizeNumberTypeConversions Inversing boolean not [7] main::$1 = main::j#1 != 3 from [6] main::$0 = main::j#1 == 3 Successful SSA optimization Pass2UnaryNotSimplification Alias main::i#2 = main::i#4 Successful SSA optimization Pass2AliasElimination Alias main::i#2 = main::i#3 Successful SSA optimization Pass2AliasElimination Simple Condition main::$1 [7] if(main::j#1!=3) goto main::@2 Simple Condition main::$2 [11] if(main::i#1!=rangelast(0,$27)) goto main::@1 Successful SSA optimization Pass2ConditionalJumpSimplification Constant main::j#0 = 0 Constant main::i#0 = 0 Constant main::j#2 = 0 Successful SSA optimization Pass2ConstantIdentification Resolved ranged next value [9] main::i#1 = ++ main::i#2 to ++ Resolved ranged comparison value [11] if(main::i#1!=rangelast(0,$27)) goto main::@1 to $28 Removing unused procedure __start Removing unused procedure block __start Removing unused procedure block __start::@1 Removing unused procedure block __start::@return Successful SSA optimization PassNEliminateEmptyStart Adding number conversion cast (unumber) $28 in [7] if(main::i#1!=$28) goto main::@1 Successful SSA optimization PassNAddNumberTypeConversions Simplifying constant integer cast $28 Successful SSA optimization PassNCastSimplification Finalized unsigned number type (char) $28 Successful SSA optimization PassNFinalizeNumberTypeConversions Inlining constant with var siblings main::j#0 Inlining constant with var siblings main::i#0 Inlining constant with var siblings main::j#2 Constant inlined main::i#0 = 0 Constant inlined main::j#2 = 0 Constant inlined main::j#0 = 0 Successful SSA optimization Pass2ConstantInlining Added new block during phi lifting main::@4(between main::@2 and main::@1) Added new block during phi lifting main::@5(between main::@1 and main::@2) Adding NOP phi() at start of main Adding NOP phi() at start of main::@3 CALL GRAPH Created 3 initial phi equivalence classes Coalesced [11] main::i#5 = main::i#1 Coalesced [12] main::j#5 = main::j#4 Coalesced [13] main::j#6 = main::j#1 Coalesced down to 2 phi equivalence classes Culled Empty Block label main::@3 Culled Empty Block label main::@4 Renumbering block main::@5 to main::@3 Adding NOP phi() at start of main Adding NOP phi() at start of main::@3 FINAL CONTROL FLOW GRAPH void main() main: scope:[main] from [0] phi() to:main::@1 main::@1: scope:[main] from main main::@2 [1] main::j#3 = phi( main/0, main::@2/main::j#4 ) [1] main::i#2 = phi( main/0, main::@2/main::i#1 ) [2] main::screen[main::i#2] = '*' [3] main::cols[main::i#2] = main::colseq[main::j#3] [4] main::j#1 = ++ main::j#3 [5] if(main::j#1!=3) goto main::@3 to:main::@2 main::@3: scope:[main] from main::@1 [6] phi() to:main::@2 main::@2: scope:[main] from main::@1 main::@3 [7] main::j#4 = phi( main::@3/main::j#1, main::@1/0 ) [8] main::i#1 = ++ main::i#2 [9] if(main::i#1!=$28) goto main::@1 to:main::@return main::@return: scope:[main] from main::@2 [10] return to:@return VARIABLE REGISTER WEIGHTS void main() char main::i char main::i#1 // 16.5 char main::i#2 // 6.285714285714286 char main::j char main::j#1 // 11.0 char main::j#3 // 11.0 char main::j#4 // 7.333333333333333 Initial phi equivalence classes [ main::i#2 main::i#1 ] [ main::j#3 main::j#4 main::j#1 ] Complete equivalence classes [ main::i#2 main::i#1 ] [ main::j#3 main::j#4 main::j#1 ] Allocated zp[1]:2 [ main::j#3 main::j#4 main::j#1 ] Allocated zp[1]:3 [ main::i#2 main::i#1 ] REGISTER UPLIFT POTENTIAL REGISTERS Statement [2] main::screen[main::i#2] = '*' [ main::i#2 main::j#3 ] ( [ main::i#2 main::j#3 ] { } ) always clobbers reg byte a Removing always clobbered register reg byte a as potential for zp[1]:3 [ main::i#2 main::i#1 ] Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::j#3 main::j#4 main::j#1 ] Statement [3] main::cols[main::i#2] = main::colseq[main::j#3] [ main::i#2 main::j#3 ] ( [ main::i#2 main::j#3 ] { } ) always clobbers reg byte a Statement [2] main::screen[main::i#2] = '*' [ main::i#2 main::j#3 ] ( [ main::i#2 main::j#3 ] { } ) always clobbers reg byte a Statement [3] main::cols[main::i#2] = main::colseq[main::j#3] [ main::i#2 main::j#3 ] ( [ main::i#2 main::j#3 ] { } ) always clobbers reg byte a Potential registers zp[1]:3 [ main::i#2 main::i#1 ] : zp[1]:3 , reg byte x , reg byte y , Potential registers zp[1]:2 [ main::j#3 main::j#4 main::j#1 ] : zp[1]:2 , reg byte x , reg byte y , REGISTER UPLIFT SCOPES Uplift Scope [main] 29.33: zp[1]:2 [ main::j#3 main::j#4 main::j#1 ] 22.79: zp[1]:3 [ main::i#2 main::i#1 ] Uplift Scope [] Uplifting [main] best 541 combination reg byte y [ main::j#3 main::j#4 main::j#1 ] reg byte x [ main::i#2 main::i#1 ] Uplifting [] best 541 combination ASSEMBLER BEFORE OPTIMIZATION // File Comments // Upstart // Commodore 64 PRG executable file .file [name="inmem-const-array.prg", type="prg", segments="Program"] .segmentdef Program [segments="Basic, Code, Data"] .segmentdef Basic [start=$0801] .segmentdef Code [start=$80d] .segmentdef Data [startAfter="Code"] .segment Basic :BasicUpstart(main) // Global Constants & labels .const WHITE = 1 .const RED = 2 .const GREEN = 5 .segment Code // main main: { .label screen = $400 .label cols = $d800 // [1] phi from main to main::@1 [phi:main->main::@1] __b1_from_main: // [1] phi main::j#3 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1 ldy #0 // [1] phi main::i#2 = 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 ldx #0 jmp __b1 // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] __b1_from___b2: // [1] phi main::j#3 = main::j#4 [phi:main::@2->main::@1#0] -- register_copy // [1] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#1] -- register_copy jmp __b1 // main::@1 __b1: // [2] main::screen[main::i#2] = '*' -- pbuc1_derefidx_vbuxx=vbuc2 lda #'*' sta screen,x // [3] main::cols[main::i#2] = main::colseq[main::j#3] -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuyy lda colseq,y sta cols,x // [4] main::j#1 = ++ main::j#3 -- vbuyy=_inc_vbuyy iny // [5] if(main::j#1!=3) goto main::@3 -- vbuyy_neq_vbuc1_then_la1 cpy #3 bne __b3_from___b1 // [7] phi from main::@1 to main::@2 [phi:main::@1->main::@2] __b2_from___b1: // [7] phi main::j#4 = 0 [phi:main::@1->main::@2#0] -- vbuyy=vbuc1 ldy #0 jmp __b2 // [6] phi from main::@1 to main::@3 [phi:main::@1->main::@3] __b3_from___b1: jmp __b3 // main::@3 __b3: // [7] phi from main::@3 to main::@2 [phi:main::@3->main::@2] __b2_from___b3: // [7] phi main::j#4 = main::j#1 [phi:main::@3->main::@2#0] -- register_copy jmp __b2 // main::@2 __b2: // [8] main::i#1 = ++ main::i#2 -- vbuxx=_inc_vbuxx inx // [9] if(main::i#1!=$28) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 cpx #$28 bne __b1_from___b2 jmp __breturn // main::@return __breturn: // [10] return rts .segment Data colseq: .byte WHITE, RED, GREEN } // File Data ASSEMBLER OPTIMIZATIONS Removing instruction jmp __b1 Removing instruction jmp __b3 Removing instruction jmp __b2 Removing instruction jmp __breturn Succesful ASM optimization Pass5NextJumpElimination Replacing label __b3_from___b1 with __b2 Replacing label __b1_from___b2 with __b1 Removing instruction __b1_from___b2: Removing instruction __b3_from___b1: Removing instruction __b3: Removing instruction __b2_from___b3: Succesful ASM optimization Pass5RedundantLabelElimination Removing instruction __b1_from_main: Removing instruction __b2_from___b1: Removing instruction __breturn: Succesful ASM optimization Pass5UnusedLabelElimination Removing instruction jmp __b1 Removing instruction jmp __b2 Succesful ASM optimization Pass5NextJumpElimination FINAL SYMBOL TABLE __constant const char GREEN = 5 __constant const char RED = 2 __constant const char WHITE = 1 void main() __constant char *main::cols = (char *) 55296 __constant char main::colseq[] = { WHITE, RED, GREEN } char main::i char main::i#1 // reg byte x 16.5 char main::i#2 // reg byte x 6.285714285714286 char main::j char main::j#1 // reg byte y 11.0 char main::j#3 // reg byte y 11.0 char main::j#4 // reg byte y 7.333333333333333 __constant char *main::screen = (char *) 1024 reg byte x [ main::i#2 main::i#1 ] reg byte y [ main::j#3 main::j#4 main::j#1 ] FINAL ASSEMBLER Score: 361 // File Comments // Upstart // Commodore 64 PRG executable file .file [name="inmem-const-array.prg", type="prg", segments="Program"] .segmentdef Program [segments="Basic, Code, Data"] .segmentdef Basic [start=$0801] .segmentdef Code [start=$80d] .segmentdef Data [startAfter="Code"] .segment Basic :BasicUpstart(main) // Global Constants & labels .const WHITE = 1 .const RED = 2 .const GREEN = 5 .segment Code // main main: { .label screen = $400 .label cols = $d800 // [1] phi from main to main::@1 [phi:main->main::@1] // [1] phi main::j#3 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1 ldy #0 // [1] phi main::i#2 = 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 ldx #0 // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] // [1] phi main::j#3 = main::j#4 [phi:main::@2->main::@1#0] -- register_copy // [1] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#1] -- register_copy // main::@1 __b1: // screen[i] = '*' // [2] main::screen[main::i#2] = '*' -- pbuc1_derefidx_vbuxx=vbuc2 lda #'*' sta screen,x // cols[i] = colseq[j] // [3] main::cols[main::i#2] = main::colseq[main::j#3] -- pbuc1_derefidx_vbuxx=pbuc2_derefidx_vbuyy lda colseq,y sta cols,x // if(++j==3) // [4] main::j#1 = ++ main::j#3 -- vbuyy=_inc_vbuyy iny // [5] if(main::j#1!=3) goto main::@3 -- vbuyy_neq_vbuc1_then_la1 cpy #3 bne __b2 // [7] phi from main::@1 to main::@2 [phi:main::@1->main::@2] // [7] phi main::j#4 = 0 [phi:main::@1->main::@2#0] -- vbuyy=vbuc1 ldy #0 // [6] phi from main::@1 to main::@3 [phi:main::@1->main::@3] // main::@3 // [7] phi from main::@3 to main::@2 [phi:main::@3->main::@2] // [7] phi main::j#4 = main::j#1 [phi:main::@3->main::@2#0] -- register_copy // main::@2 __b2: // for( byte i : 0..39) // [8] main::i#1 = ++ main::i#2 -- vbuxx=_inc_vbuxx inx // [9] if(main::i#1!=$28) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 cpx #$28 bne __b1 // main::@return // } // [10] return rts .segment Data colseq: .byte WHITE, RED, GREEN } // File Data