CONTROL FLOW GRAPH SSA void main() main: scope:[main] from __start main::i#0 = 0 to:main::@1 main::@1: scope:[main] from main main::@1 main::i#2 = phi( main/main::i#0, main::@1/main::i#1 ) main::$0 = $28*$a + main::i#2 main::screen[main::$0] = 'a' main::i#1 = main::i#2 + rangenext(0,$27) main::$1 = main::i#1 != rangelast(0,$27) if(main::$1) goto main::@1 to:main::@return main::@return: scope:[main] from main::@1 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 void __start() void main() number~ main::$0 bool~ main::$1 byte main::i byte main::i#0 byte main::i#1 byte main::i#2 constant byte* const main::screen = (byte*)$400 Adding number conversion cast (unumber) $28*$a in main::$0 = $28*$a + main::i#2 Adding number conversion cast (unumber) main::$0 in main::$0 = (unumber)$28*$a + main::i#2 Successful SSA optimization PassNAddNumberTypeConversions Simplifying constant pointer cast (byte*) 1024 Successful SSA optimization PassNCastSimplification Inferred type updated to word in main::$0 = (word)$28*$a + main::i#2 Simple Condition main::$1 [6] if(main::i#1!=rangelast(0,$27)) goto main::@1 Successful SSA optimization Pass2ConditionalJumpSimplification Constant main::i#0 = 0 Successful SSA optimization Pass2ConstantIdentification Resolved ranged next value [4] main::i#1 = ++ main::i#2 to ++ Resolved ranged comparison value [6] if(main::i#1!=rangelast(0,$27)) goto main::@1 to $28 De-inlining pointer[w] to *(pointer+w) [3] main::screen[main::$0] = 'a' Successful SSA optimization Pass2DeInlineWordDerefIdx 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 [5] 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 (byte) $28 Successful SSA optimization PassNFinalizeNumberTypeConversions Inlining constant with var siblings main::i#0 Constant inlined main::i#0 = 0 Successful SSA optimization Pass2ConstantInlining Consolidated constant in assignment main::$2 Successful SSA optimization Pass2ConstantAdditionElimination Alias main::i#2 = main::$0 Successful SSA optimization Pass2AliasElimination Converting *(pointer+n) to pointer[n] [2] *main::$2 = 'a' -- (main::screen+(word)$28*$a)[main::i#2] Successful SSA optimization Pass2InlineDerefIdx Eliminating unused variable main::$2 and assignment [1] main::$2 = main::screen+(word)$28*$a + main::i#2 Successful SSA optimization PassNEliminateUnusedVars Finalized unsigned number type (byte) $28 Finalized unsigned number type (byte) $a Successful SSA optimization PassNFinalizeNumberTypeConversions Added new block during phi lifting main::@2(between main::@1 and main::@1) Adding NOP phi() at start of main CALL GRAPH Created 1 initial phi equivalence classes Coalesced [6] main::i#3 = main::i#1 Coalesced down to 1 phi equivalence classes Culled Empty Block label main::@2 Adding NOP phi() at start of main FINAL CONTROL FLOW GRAPH void main() main: scope:[main] from [0] phi() to:main::@1 main::@1: scope:[main] from main main::@1 [1] main::i#2 = phi( main/0, main::@1/main::i#1 ) [2] (main::screen+(word)$28*$a)[main::i#2] = 'a' [3] main::i#1 = ++ main::i#2 [4] if(main::i#1!=$28) goto main::@1 to:main::@return main::@return: scope:[main] from main::@1 [5] return to:@return VARIABLE REGISTER WEIGHTS void main() byte main::i byte main::i#1 16.5 byte main::i#2 16.5 Initial phi equivalence classes [ main::i#2 main::i#1 ] Complete equivalence classes [ main::i#2 main::i#1 ] Allocated zp[1]:2 [ main::i#2 main::i#1 ] REGISTER UPLIFT POTENTIAL REGISTERS Statement [2] (main::screen+(word)$28*$a)[main::i#2] = 'a' [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::i#2 main::i#1 ] Statement [2] (main::screen+(word)$28*$a)[main::i#2] = 'a' [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a Potential registers zp[1]:2 [ main::i#2 main::i#1 ] : zp[1]:2 , reg byte x , reg byte y , REGISTER UPLIFT SCOPES Uplift Scope [main] 33: zp[1]:2 [ main::i#2 main::i#1 ] Uplift Scope [] Uplifting [main] best 251 combination reg byte x [ main::i#2 main::i#1 ] Uplifting [] best 251 combination ASSEMBLER BEFORE OPTIMIZATION // File Comments // Tests that array-indexing by a word variable that is a sum of a constant word and a byte is turned back into derefidx // Upstart // Commodore 64 PRG executable file .file [name="derefidx-word-2.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 .segment Code // main main: { .label screen = $400 // [1] phi from main to main::@1 [phi:main->main::@1] __b1_from_main: // [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 ldx #0 jmp __b1 // [1] phi from main::@1 to main::@1 [phi:main::@1->main::@1] __b1_from___b1: // [1] phi main::i#2 = main::i#1 [phi:main::@1->main::@1#0] -- register_copy jmp __b1 // main::@1 __b1: // [2] (main::screen+(word)$28*$a)[main::i#2] = 'a' -- pbuc1_derefidx_vbuxx=vbuc2 lda #'a' sta screen+$28*$a,x // [3] main::i#1 = ++ main::i#2 -- vbuxx=_inc_vbuxx inx // [4] if(main::i#1!=$28) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 cpx #$28 bne __b1_from___b1 jmp __breturn // main::@return __breturn: // [5] return rts } // File Data ASSEMBLER OPTIMIZATIONS Removing instruction jmp __b1 Removing instruction jmp __breturn Succesful ASM optimization Pass5NextJumpElimination Replacing label __b1_from___b1 with __b1 Removing instruction __b1_from___b1: Succesful ASM optimization Pass5RedundantLabelElimination Removing instruction __b1_from_main: Removing instruction __breturn: Succesful ASM optimization Pass5UnusedLabelElimination Removing instruction jmp __b1 Succesful ASM optimization Pass5NextJumpElimination FINAL SYMBOL TABLE void main() byte main::i byte main::i#1 reg byte x 16.5 byte main::i#2 reg byte x 16.5 constant byte* const main::screen = (byte*) 1024 reg byte x [ main::i#2 main::i#1 ] FINAL ASSEMBLER Score: 161 // File Comments // Tests that array-indexing by a word variable that is a sum of a constant word and a byte is turned back into derefidx // Upstart // Commodore 64 PRG executable file .file [name="derefidx-word-2.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 .segment Code // main main: { .label screen = $400 // [1] phi from main to main::@1 [phi:main->main::@1] // [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 ldx #0 // [1] phi from main::@1 to main::@1 [phi:main::@1->main::@1] // [1] phi main::i#2 = main::i#1 [phi:main::@1->main::@1#0] -- register_copy // main::@1 __b1: // screen[40*10+i] = 'a' // [2] (main::screen+(word)$28*$a)[main::i#2] = 'a' -- pbuc1_derefidx_vbuxx=vbuc2 lda #'a' sta screen+$28*$a,x // for( byte i : 0..39) // [3] main::i#1 = ++ main::i#2 -- vbuxx=_inc_vbuxx inx // [4] if(main::i#1!=$28) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 cpx #$28 bne __b1 // main::@return // } // [5] return rts } // File Data