CONTROL FLOW GRAPH SSA void main() main: scope:[main] from __start main::a#0 = 0 to:main::@1 main::@1: scope:[main] from main main::@5 main::a#7 = phi( main/main::a#0, main::@5/main::a#1 ) main::b#0 = 0 to:main::@2 main::@2: scope:[main] from main::@1 main::@4 main::b#4 = phi( main::@1/main::b#0, main::@4/main::b#1 ) main::a#4 = phi( main::@1/main::a#7, main::@4/main::a#6 ) main::c#0 = 0 to:main::@3 main::@3: scope:[main] from main::@2 main::@6 main::b#2 = phi( main::@2/main::b#4, main::@6/main::b#5 ) main::a#2 = phi( main::@2/main::a#4, main::@6/main::a#5 ) main::c#2 = phi( main::@2/main::c#0, main::@6/main::c#1 ) main::$0 = main::c#2 + main::a#2 main::ca#0 = main::$0 print::b#0 = main::b#2 print::ca#0 = main::ca#0 call print to:main::@6 main::@6: scope:[main] from main::@3 main::b#5 = phi( main::@3/main::b#2 ) main::a#5 = phi( main::@3/main::a#2 ) main::c#3 = phi( main::@3/main::c#2 ) main::c#1 = main::c#3 + rangenext(0,$64) main::$2 = main::c#1 != rangelast(0,$64) if(main::$2) goto main::@3 to:main::@4 main::@4: scope:[main] from main::@6 main::a#6 = phi( main::@6/main::a#5 ) main::b#3 = phi( main::@6/main::b#5 ) main::b#1 = main::b#3 + rangenext(0,$64) main::$3 = main::b#1 != rangelast(0,$64) if(main::$3) goto main::@2 to:main::@5 main::@5: scope:[main] from main::@4 main::a#3 = phi( main::@4/main::a#6 ) main::a#1 = main::a#3 + rangenext(0,$64) main::$4 = main::a#1 != rangelast(0,$64) if(main::$4) goto main::@1 to:main::@return main::@return: scope:[main] from main::@5 return to:@return void print(byte print::b , byte print::ca) print: scope:[print] from main::@3 print::b#1 = phi( main::@3/print::b#0 ) print::ca#1 = phi( main::@3/print::ca#0 ) SCREEN[print::b#1] = print::ca#1 to:print::@return print::@return: scope:[print] from print 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 byte* const SCREEN = (byte*)$400 void __start() void main() byte~ main::$0 bool~ main::$2 bool~ main::$3 bool~ main::$4 byte main::a byte main::a#0 byte main::a#1 byte main::a#2 byte main::a#3 byte main::a#4 byte main::a#5 byte main::a#6 byte main::a#7 byte main::b byte main::b#0 byte main::b#1 byte main::b#2 byte main::b#3 byte main::b#4 byte main::b#5 byte main::c byte main::c#0 byte main::c#1 byte main::c#2 byte main::c#3 byte main::ca byte main::ca#0 void print(byte print::b , byte print::ca) byte print::b byte print::b#0 byte print::b#1 byte print::ca byte print::ca#0 byte print::ca#1 Simplifying constant pointer cast (byte*) 1024 Successful SSA optimization PassNCastSimplification Alias main::ca#0 = main::$0 Alias main::c#2 = main::c#3 Alias main::a#2 = main::a#5 main::a#6 main::a#3 Alias main::b#2 = main::b#5 main::b#3 Successful SSA optimization Pass2AliasElimination Identical Phi Values main::a#2 main::a#4 Identical Phi Values main::b#2 main::b#4 Identical Phi Values print::ca#1 print::ca#0 Identical Phi Values print::b#1 print::b#0 Successful SSA optimization Pass2IdenticalPhiElimination Identical Phi Values main::a#4 main::a#7 Successful SSA optimization Pass2IdenticalPhiElimination Simple Condition main::$2 [12] if(main::c#1!=rangelast(0,$64)) goto main::@3 Simple Condition main::$3 [15] if(main::b#1!=rangelast(0,$64)) goto main::@2 Simple Condition main::$4 [18] if(main::a#1!=rangelast(0,$64)) goto main::@1 Successful SSA optimization Pass2ConditionalJumpSimplification Constant main::a#0 = 0 Constant main::b#0 = 0 Constant main::c#0 = 0 Successful SSA optimization Pass2ConstantIdentification Resolved ranged next value [10] main::c#1 = ++ main::c#2 to ++ Resolved ranged comparison value [12] if(main::c#1!=rangelast(0,$64)) goto main::@3 to $65 Resolved ranged next value [13] main::b#1 = ++ main::b#4 to ++ Resolved ranged comparison value [15] if(main::b#1!=rangelast(0,$64)) goto main::@2 to $65 Resolved ranged next value [16] main::a#1 = ++ main::a#7 to ++ Resolved ranged comparison value [18] if(main::a#1!=rangelast(0,$64)) goto main::@1 to $65 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) $65 in [8] if(main::c#1!=$65) goto main::@3 Adding number conversion cast (unumber) $65 in [10] if(main::b#1!=$65) goto main::@2 Adding number conversion cast (unumber) $65 in [12] if(main::a#1!=$65) goto main::@1 Successful SSA optimization PassNAddNumberTypeConversions Simplifying constant integer cast $65 Simplifying constant integer cast $65 Simplifying constant integer cast $65 Successful SSA optimization PassNCastSimplification Finalized unsigned number type (byte) $65 Finalized unsigned number type (byte) $65 Finalized unsigned number type (byte) $65 Successful SSA optimization PassNFinalizeNumberTypeConversions Inlining constant with var siblings main::a#0 Inlining constant with var siblings main::b#0 Inlining constant with var siblings main::c#0 Constant inlined main::a#0 = 0 Constant inlined main::c#0 = 0 Constant inlined main::b#0 = 0 Successful SSA optimization Pass2ConstantInlining Added new block during phi lifting main::@7(between main::@5 and main::@1) Added new block during phi lifting main::@8(between main::@4 and main::@2) Added new block during phi lifting main::@9(between main::@6 and main::@3) Adding NOP phi() at start of main CALL GRAPH Calls in [main] to print:7 Created 3 initial phi equivalence classes Coalesced [15] main::a#8 = main::a#1 Coalesced [16] main::b#6 = main::b#1 Coalesced [17] main::c#4 = main::c#1 Coalesced down to 3 phi equivalence classes Culled Empty Block label main::@7 Culled Empty Block label main::@8 Culled Empty Block label main::@9 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::@5 [1] main::a#7 = phi( main/0, main::@5/main::a#1 ) to:main::@2 main::@2: scope:[main] from main::@1 main::@4 [2] main::b#4 = phi( main::@1/0, main::@4/main::b#1 ) to:main::@3 main::@3: scope:[main] from main::@2 main::@6 [3] main::c#2 = phi( main::@2/0, main::@6/main::c#1 ) [4] main::ca#0 = main::c#2 + main::a#7 [5] print::b#0 = main::b#4 [6] print::ca#0 = main::ca#0 [7] call print to:main::@6 main::@6: scope:[main] from main::@3 [8] main::c#1 = ++ main::c#2 [9] if(main::c#1!=$65) goto main::@3 to:main::@4 main::@4: scope:[main] from main::@6 [10] main::b#1 = ++ main::b#4 [11] if(main::b#1!=$65) goto main::@2 to:main::@5 main::@5: scope:[main] from main::@4 [12] main::a#1 = ++ main::a#7 [13] if(main::a#1!=$65) goto main::@1 to:main::@return main::@return: scope:[main] from main::@5 [14] return to:@return void print(byte print::b , byte print::ca) print: scope:[print] from main::@3 [15] SCREEN[print::b#0] = print::ca#0 to:print::@return print::@return: scope:[print] from print [16] return to:@return VARIABLE REGISTER WEIGHTS void main() byte main::a byte main::a#1 16.5 byte main::a#7 93.0 byte main::b byte main::b#1 151.5 byte main::b#4 150.375 byte main::c byte main::c#1 1501.5 byte main::c#2 600.5999999999999 byte main::ca byte main::ca#0 1001.0 void print(byte print::b , byte print::ca) byte print::b byte print::b#0 5501.0 byte print::ca byte print::ca#0 11002.0 Initial phi equivalence classes [ main::a#7 main::a#1 ] [ main::b#4 main::b#1 ] [ main::c#2 main::c#1 ] Added variable main::ca#0 to live range equivalence class [ main::ca#0 ] Added variable print::b#0 to live range equivalence class [ print::b#0 ] Added variable print::ca#0 to live range equivalence class [ print::ca#0 ] Complete equivalence classes [ main::a#7 main::a#1 ] [ main::b#4 main::b#1 ] [ main::c#2 main::c#1 ] [ main::ca#0 ] [ print::b#0 ] [ print::ca#0 ] Allocated zp[1]:2 [ main::a#7 main::a#1 ] Allocated zp[1]:3 [ main::b#4 main::b#1 ] Allocated zp[1]:4 [ main::c#2 main::c#1 ] Allocated zp[1]:5 [ main::ca#0 ] Allocated zp[1]:6 [ print::b#0 ] Allocated zp[1]:7 [ print::ca#0 ] REGISTER UPLIFT POTENTIAL REGISTERS Statement [4] main::ca#0 = main::c#2 + main::a#7 [ main::a#7 main::b#4 main::c#2 main::ca#0 ] ( [ main::a#7 main::b#4 main::c#2 main::ca#0 ] { { print::b#0 = main::b#4 } { print::ca#0 = main::ca#0 } } ) always clobbers reg byte a Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::a#7 main::a#1 ] Removing always clobbered register reg byte a as potential for zp[1]:3 [ main::b#4 main::b#1 ] Removing always clobbered register reg byte a as potential for zp[1]:4 [ main::c#2 main::c#1 ] Statement [4] main::ca#0 = main::c#2 + main::a#7 [ main::a#7 main::b#4 main::c#2 main::ca#0 ] ( [ main::a#7 main::b#4 main::c#2 main::ca#0 ] { { print::b#0 = main::b#4 } { print::ca#0 = main::ca#0 } } ) always clobbers reg byte a Potential registers zp[1]:2 [ main::a#7 main::a#1 ] : zp[1]:2 , reg byte x , reg byte y , Potential registers zp[1]:3 [ main::b#4 main::b#1 ] : zp[1]:3 , reg byte x , reg byte y , Potential registers zp[1]:4 [ main::c#2 main::c#1 ] : zp[1]:4 , reg byte x , reg byte y , Potential registers zp[1]:5 [ main::ca#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , Potential registers zp[1]:6 [ print::b#0 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , Potential registers zp[1]:7 [ print::ca#0 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , REGISTER UPLIFT SCOPES Uplift Scope [print] 11,002: zp[1]:7 [ print::ca#0 ] 5,501: zp[1]:6 [ print::b#0 ] Uplift Scope [main] 2,102.1: zp[1]:4 [ main::c#2 main::c#1 ] 1,001: zp[1]:5 [ main::ca#0 ] 301.88: zp[1]:3 [ main::b#4 main::b#1 ] 109.5: zp[1]:2 [ main::a#7 main::a#1 ] Uplift Scope [] Uplifting [print] best 55435 combination reg byte a [ print::ca#0 ] reg byte x [ print::b#0 ] Uplifting [main] best 35535 combination reg byte y [ main::c#2 main::c#1 ] reg byte a [ main::ca#0 ] reg byte x [ main::b#4 main::b#1 ] zp[1]:2 [ main::a#7 main::a#1 ] Limited combination testing to 100 combinations of 108 possible. Uplifting [] best 35535 combination Attempting to uplift remaining variables inzp[1]:2 [ main::a#7 main::a#1 ] Uplifting [main] best 35535 combination zp[1]:2 [ main::a#7 main::a#1 ] ASSEMBLER BEFORE OPTIMIZATION // File Comments // Test effective live range and register allocation // main::b and main::c should be allocated to hardware registers // Upstart // Commodore 64 PRG executable file .file [name="liverange-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 .label SCREEN = $400 .segment Code // main main: { .label a = 2 // [1] phi from main to main::@1 [phi:main->main::@1] __b1_from_main: // [1] phi main::a#7 = 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 lda #0 sta.z a jmp __b1 // [1] phi from main::@5 to main::@1 [phi:main::@5->main::@1] __b1_from___b5: // [1] phi main::a#7 = main::a#1 [phi:main::@5->main::@1#0] -- register_copy jmp __b1 // main::@1 __b1: // [2] phi from main::@1 to main::@2 [phi:main::@1->main::@2] __b2_from___b1: // [2] phi main::b#4 = 0 [phi:main::@1->main::@2#0] -- vbuxx=vbuc1 ldx #0 jmp __b2 // [2] phi from main::@4 to main::@2 [phi:main::@4->main::@2] __b2_from___b4: // [2] phi main::b#4 = main::b#1 [phi:main::@4->main::@2#0] -- register_copy jmp __b2 // main::@2 __b2: // [3] phi from main::@2 to main::@3 [phi:main::@2->main::@3] __b3_from___b2: // [3] phi main::c#2 = 0 [phi:main::@2->main::@3#0] -- vbuyy=vbuc1 ldy #0 jmp __b3 // [3] phi from main::@6 to main::@3 [phi:main::@6->main::@3] __b3_from___b6: // [3] phi main::c#2 = main::c#1 [phi:main::@6->main::@3#0] -- register_copy jmp __b3 // main::@3 __b3: // [4] main::ca#0 = main::c#2 + main::a#7 -- vbuaa=vbuyy_plus_vbuz1 tya clc adc.z a // [5] print::b#0 = main::b#4 // [6] print::ca#0 = main::ca#0 // [7] call print jsr print jmp __b6 // main::@6 __b6: // [8] main::c#1 = ++ main::c#2 -- vbuyy=_inc_vbuyy iny // [9] if(main::c#1!=$65) goto main::@3 -- vbuyy_neq_vbuc1_then_la1 cpy #$65 bne __b3_from___b6 jmp __b4 // main::@4 __b4: // [10] main::b#1 = ++ main::b#4 -- vbuxx=_inc_vbuxx inx // [11] if(main::b#1!=$65) goto main::@2 -- vbuxx_neq_vbuc1_then_la1 cpx #$65 bne __b2_from___b4 jmp __b5 // main::@5 __b5: // [12] main::a#1 = ++ main::a#7 -- vbuz1=_inc_vbuz1 inc.z a // [13] if(main::a#1!=$65) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 lda #$65 cmp.z a bne __b1_from___b5 jmp __breturn // main::@return __breturn: // [14] return rts } // print // print(byte register(X) b, byte register(A) ca) print: { // [15] SCREEN[print::b#0] = print::ca#0 -- pbuc1_derefidx_vbuxx=vbuaa sta SCREEN,x jmp __breturn // print::@return __breturn: // [16] return rts } // File Data ASSEMBLER OPTIMIZATIONS Removing instruction jmp __b1 Removing instruction jmp __b2 Removing instruction jmp __b3 Removing instruction jmp __b6 Removing instruction jmp __b4 Removing instruction jmp __b5 Removing instruction jmp __breturn Removing instruction jmp __breturn Succesful ASM optimization Pass5NextJumpElimination Replacing label __b3_from___b6 with __b3 Replacing label __b2_from___b4 with __b2 Replacing label __b1_from___b5 with __b1 Removing instruction __b1_from___b5: Removing instruction __b2_from___b1: Removing instruction __b2_from___b4: Removing instruction __b3_from___b2: Removing instruction __b3_from___b6: Succesful ASM optimization Pass5RedundantLabelElimination Removing instruction __b1_from_main: Removing instruction __b6: Removing instruction __b4: Removing instruction __b5: Removing instruction __breturn: Removing instruction __breturn: Succesful ASM optimization Pass5UnusedLabelElimination Removing instruction jmp __b1 Removing instruction jmp __b2 Removing instruction jmp __b3 Succesful ASM optimization Pass5NextJumpElimination FINAL SYMBOL TABLE constant byte* const SCREEN = (byte*) 1024 void main() byte main::a byte main::a#1 a zp[1]:2 16.5 byte main::a#7 a zp[1]:2 93.0 byte main::b byte main::b#1 reg byte x 151.5 byte main::b#4 reg byte x 150.375 byte main::c byte main::c#1 reg byte y 1501.5 byte main::c#2 reg byte y 600.5999999999999 byte main::ca byte main::ca#0 reg byte a 1001.0 void print(byte print::b , byte print::ca) byte print::b byte print::b#0 reg byte x 5501.0 byte print::ca byte print::ca#0 reg byte a 11002.0 zp[1]:2 [ main::a#7 main::a#1 ] reg byte x [ main::b#4 main::b#1 ] reg byte y [ main::c#2 main::c#1 ] reg byte a [ main::ca#0 ] reg byte x [ print::b#0 ] reg byte a [ print::ca#0 ] FINAL ASSEMBLER Score: 22542 // File Comments // Test effective live range and register allocation // main::b and main::c should be allocated to hardware registers // Upstart // Commodore 64 PRG executable file .file [name="liverange-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 .label SCREEN = $400 .segment Code // main main: { .label a = 2 // [1] phi from main to main::@1 [phi:main->main::@1] // [1] phi main::a#7 = 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 lda #0 sta.z a // [1] phi from main::@5 to main::@1 [phi:main::@5->main::@1] // [1] phi main::a#7 = main::a#1 [phi:main::@5->main::@1#0] -- register_copy // main::@1 __b1: // [2] phi from main::@1 to main::@2 [phi:main::@1->main::@2] // [2] phi main::b#4 = 0 [phi:main::@1->main::@2#0] -- vbuxx=vbuc1 ldx #0 // [2] phi from main::@4 to main::@2 [phi:main::@4->main::@2] // [2] phi main::b#4 = main::b#1 [phi:main::@4->main::@2#0] -- register_copy // main::@2 __b2: // [3] phi from main::@2 to main::@3 [phi:main::@2->main::@3] // [3] phi main::c#2 = 0 [phi:main::@2->main::@3#0] -- vbuyy=vbuc1 ldy #0 // [3] phi from main::@6 to main::@3 [phi:main::@6->main::@3] // [3] phi main::c#2 = main::c#1 [phi:main::@6->main::@3#0] -- register_copy // main::@3 __b3: // char ca = c+a // [4] main::ca#0 = main::c#2 + main::a#7 -- vbuaa=vbuyy_plus_vbuz1 tya clc adc.z a // print(b, ca) // [5] print::b#0 = main::b#4 // [6] print::ca#0 = main::ca#0 // [7] call print jsr print // main::@6 // for( char c: 0..100 ) // [8] main::c#1 = ++ main::c#2 -- vbuyy=_inc_vbuyy iny // [9] if(main::c#1!=$65) goto main::@3 -- vbuyy_neq_vbuc1_then_la1 cpy #$65 bne __b3 // main::@4 // for( char b: 0..100 ) // [10] main::b#1 = ++ main::b#4 -- vbuxx=_inc_vbuxx inx // [11] if(main::b#1!=$65) goto main::@2 -- vbuxx_neq_vbuc1_then_la1 cpx #$65 bne __b2 // main::@5 // for(char a: 0..100 ) // [12] main::a#1 = ++ main::a#7 -- vbuz1=_inc_vbuz1 inc.z a // [13] if(main::a#1!=$65) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 lda #$65 cmp.z a bne __b1 // main::@return // } // [14] return rts } // print // print(byte register(X) b, byte register(A) ca) print: { // SCREEN[b] = ca // [15] SCREEN[print::b#0] = print::ca#0 -- pbuc1_derefidx_vbuxx=vbuaa sta SCREEN,x // print::@return // } // [16] return rts } // File Data