Culled Empty Block (label) malloc::@1 CONTROL FLOW GRAPH SSA @begin: scope:[] from (byte*) MEM#0 ← ((byte*)) (number) $400 to:@1 (byte*()) malloc() malloc: scope:[malloc] from @1 @3 (byte*) MEM#5 ← phi( @1/(byte*) MEM#9 @3/(byte*) MEM#3 ) (byte*) MEM#1 ← ++ (byte*) MEM#5 (byte*) malloc::return#0 ← (byte*) MEM#1 to:malloc::@return malloc::@return: scope:[malloc] from malloc (byte*) MEM#6 ← phi( malloc/(byte*) MEM#1 ) (byte*) malloc::return#4 ← phi( malloc/(byte*) malloc::return#0 ) (byte*) malloc::return#1 ← (byte*) malloc::return#4 (byte*) MEM#2 ← (byte*) MEM#6 return to:@return @1: scope:[] from @begin (byte*) MEM#9 ← phi( @begin/(byte*) MEM#0 ) call malloc (byte*) malloc::return#2 ← (byte*) malloc::return#1 to:@3 @3: scope:[] from @1 (byte*) MEM#7 ← phi( @1/(byte*) MEM#2 ) (byte*) malloc::return#5 ← phi( @1/(byte*) malloc::return#2 ) (byte*~) $0 ← (byte*) malloc::return#5 (byte*) MEM#3 ← (byte*) MEM#7 (byte*) SCREEN_1#0 ← (byte*~) $0 call malloc (byte*) malloc::return#3 ← (byte*) malloc::return#1 to:@4 @4: scope:[] from @3 (byte*) SCREEN_1#3 ← phi( @3/(byte*) SCREEN_1#0 ) (byte*) MEM#8 ← phi( @3/(byte*) MEM#2 ) (byte*) malloc::return#6 ← phi( @3/(byte*) malloc::return#3 ) (byte*~) $1 ← (byte*) malloc::return#6 (byte*) MEM#4 ← (byte*) MEM#8 (byte*) SCREEN_2#0 ← (byte*~) $1 to:@2 (void()) main() main: scope:[main] from @2 (byte*) SCREEN_2#1 ← phi( @2/(byte*) SCREEN_2#2 ) (byte*) SCREEN_1#1 ← phi( @2/(byte*) SCREEN_1#2 ) *((byte*) SCREEN_1#1) ← (number) 0 *((byte*) SCREEN_2#1) ← (number) 0 to:main::@return main::@return: scope:[main] from main return to:@return @2: scope:[] from @4 (byte*) SCREEN_2#2 ← phi( @4/(byte*) SCREEN_2#0 ) (byte*) SCREEN_1#2 ← phi( @4/(byte*) SCREEN_1#3 ) call main to:@5 @5: scope:[] from @2 to:@end @end: scope:[] from @5 SYMBOL TABLE SSA (byte*~) $0 (byte*~) $1 (label) @1 (label) @2 (label) @3 (label) @4 (label) @5 (label) @begin (label) @end (byte*) MEM (byte*) MEM#0 (byte*) MEM#1 (byte*) MEM#2 (byte*) MEM#3 (byte*) MEM#4 (byte*) MEM#5 (byte*) MEM#6 (byte*) MEM#7 (byte*) MEM#8 (byte*) MEM#9 (byte*) SCREEN_1 (byte*) SCREEN_1#0 (byte*) SCREEN_1#1 (byte*) SCREEN_1#2 (byte*) SCREEN_1#3 (byte*) SCREEN_2 (byte*) SCREEN_2#0 (byte*) SCREEN_2#1 (byte*) SCREEN_2#2 (void()) main() (label) main::@return (byte*()) malloc() (label) malloc::@return (byte*) malloc::return (byte*) malloc::return#0 (byte*) malloc::return#1 (byte*) malloc::return#2 (byte*) malloc::return#3 (byte*) malloc::return#4 (byte*) malloc::return#5 (byte*) malloc::return#6 Adding number conversion cast (unumber) 0 in *((byte*) SCREEN_1#1) ← (number) 0 Adding number conversion cast (unumber) 0 in *((byte*) SCREEN_2#1) ← (number) 0 Successful SSA optimization PassNAddNumberTypeConversions Inlining cast (byte*) MEM#0 ← (byte*)(number) $400 Inlining cast *((byte*) SCREEN_1#1) ← (unumber)(number) 0 Inlining cast *((byte*) SCREEN_2#1) ← (unumber)(number) 0 Successful SSA optimization Pass2InlineCast Simplifying constant pointer cast (byte*) 1024 Simplifying constant integer cast 0 Simplifying constant integer cast 0 Successful SSA optimization PassNCastSimplification Finalized unsigned number type (byte) 0 Finalized unsigned number type (byte) 0 Successful SSA optimization PassNFinalizeNumberTypeConversions Alias (byte*) malloc::return#0 = (byte*) malloc::return#4 (byte*) malloc::return#1 Alias (byte*) MEM#1 = (byte*) MEM#6 (byte*) MEM#2 Alias (byte*) MEM#0 = (byte*) MEM#9 Alias (byte*) malloc::return#2 = (byte*) malloc::return#5 Alias (byte*) MEM#3 = (byte*) MEM#7 Alias (byte*) SCREEN_1#0 = (byte*~) $0 (byte*) SCREEN_1#3 (byte*) SCREEN_1#2 Alias (byte*) malloc::return#3 = (byte*) malloc::return#6 Alias (byte*) MEM#4 = (byte*) MEM#8 Alias (byte*) SCREEN_2#0 = (byte*~) $1 (byte*) SCREEN_2#2 Successful SSA optimization Pass2AliasElimination Identical Phi Values (byte*) MEM#3 (byte*) MEM#1 Identical Phi Values (byte*) MEM#4 (byte*) MEM#1 Identical Phi Values (byte*) SCREEN_1#1 (byte*) SCREEN_1#0 Identical Phi Values (byte*) SCREEN_2#1 (byte*) SCREEN_2#0 Successful SSA optimization Pass2IdenticalPhiElimination Constant (const byte*) MEM#0 = (byte*) 1024 Successful SSA optimization Pass2ConstantIdentification Inlining constant with var siblings (const byte*) MEM#0 Constant inlined MEM#0 = (byte*) 1024 Successful SSA optimization Pass2ConstantInlining Adding NOP phi() at start of @begin Adding NOP phi() at start of @1 Adding NOP phi() at start of @2 Adding NOP phi() at start of @5 Adding NOP phi() at start of @end CALL GRAPH Calls in [] to malloc:2 malloc:6 main:10 Created 1 initial phi equivalence classes Coalesced [5] MEM#10 ← MEM#1 Coalesced down to 1 phi equivalence classes Culled Empty Block (label) @5 Adding NOP phi() at start of @begin Adding NOP phi() at start of @1 Adding NOP phi() at start of @2 Adding NOP phi() at start of @end FINAL CONTROL FLOW GRAPH @begin: scope:[] from [0] phi() to:@1 @1: scope:[] from @begin [1] phi() [2] call malloc [3] (byte*) malloc::return#2 ← (byte*) malloc::return#0 to:@3 @3: scope:[] from @1 [4] (byte*) SCREEN_1#0 ← (byte*) malloc::return#2 [5] call malloc [6] (byte*) malloc::return#3 ← (byte*) malloc::return#0 to:@4 @4: scope:[] from @3 [7] (byte*) SCREEN_2#0 ← (byte*) malloc::return#3 to:@2 @2: scope:[] from @4 [8] phi() [9] call main to:@end @end: scope:[] from @2 [10] phi() (void()) main() main: scope:[main] from @2 [11] *((byte*) SCREEN_1#0) ← (byte) 0 [12] *((byte*) SCREEN_2#0) ← (byte) 0 to:main::@return main::@return: scope:[main] from main [13] return to:@return (byte*()) malloc() malloc: scope:[malloc] from @1 @3 [14] (byte*) MEM#5 ← phi( @1/(byte*) 1024 @3/(byte*) MEM#1 ) [15] (byte*) MEM#1 ← ++ (byte*) MEM#5 [16] (byte*) malloc::return#0 ← (byte*) MEM#1 to:malloc::@return malloc::@return: scope:[malloc] from malloc [17] return to:@return VARIABLE REGISTER WEIGHTS (byte*) MEM (byte*) MEM#1 1.0 (byte*) MEM#5 4.0 (byte*) SCREEN_1 (byte*) SCREEN_1#0 0.8 (byte*) SCREEN_2 (byte*) SCREEN_2#0 1.3333333333333333 (void()) main() (byte*()) malloc() (byte*) malloc::return (byte*) malloc::return#0 1.5 (byte*) malloc::return#2 4.0 (byte*) malloc::return#3 4.0 Initial phi equivalence classes [ MEM#5 MEM#1 ] Added variable malloc::return#2 to zero page equivalence class [ malloc::return#2 ] Added variable SCREEN_1#0 to zero page equivalence class [ SCREEN_1#0 ] Added variable malloc::return#3 to zero page equivalence class [ malloc::return#3 ] Added variable SCREEN_2#0 to zero page equivalence class [ SCREEN_2#0 ] Added variable malloc::return#0 to zero page equivalence class [ malloc::return#0 ] Complete equivalence classes [ MEM#5 MEM#1 ] [ malloc::return#2 ] [ SCREEN_1#0 ] [ malloc::return#3 ] [ SCREEN_2#0 ] [ malloc::return#0 ] Allocated zp[2]:2 [ MEM#5 MEM#1 ] Allocated zp[2]:4 [ malloc::return#2 ] Allocated zp[2]:6 [ SCREEN_1#0 ] Allocated zp[2]:8 [ malloc::return#3 ] Allocated zp[2]:10 [ SCREEN_2#0 ] Allocated zp[2]:12 [ malloc::return#0 ] INITIAL ASM Target platform is c64basic / MOS6502X // File Comments // Error where the compiler is reusing the same ZP for two byte* variables. // SCREEN_1 and SCREEN_2 are both allocated to ZP: 4 // Problem is that outside main() scope statements have zero call-paths and then isStatementAllocationOverlapping() never checks liveranges // CallPath code must be rewritten to use @begin as the outermost call instead of main() // Upstart .pc = $801 "Basic" :BasicUpstart(__bbegin) .pc = $80d "Program" // Global Constants & labels .label MEM = 2 .label SCREEN_1 = 6 .label SCREEN_2 = $a // @begin __bbegin: // [1] phi from @begin to @1 [phi:@begin->@1] __b1_from___bbegin: jmp __b1 // @1 __b1: // [2] call malloc // [14] phi from @1 to malloc [phi:@1->malloc] malloc_from___b1: // [14] phi (byte*) MEM#5 = (byte*) 1024 [phi:@1->malloc#0] -- pbuz1=pbuc1 lda #<$400 sta.z MEM lda #>$400 sta.z MEM+1 jsr malloc // [3] (byte*) malloc::return#2 ← (byte*) malloc::return#0 -- pbuz1=pbuz2 lda.z malloc.return sta.z malloc.return_1 lda.z malloc.return+1 sta.z malloc.return_1+1 jmp __b3 // @3 __b3: // [4] (byte*) SCREEN_1#0 ← (byte*) malloc::return#2 -- pbuz1=pbuz2 lda.z malloc.return_1 sta.z SCREEN_1 lda.z malloc.return_1+1 sta.z SCREEN_1+1 // [5] call malloc // [14] phi from @3 to malloc [phi:@3->malloc] malloc_from___b3: // [14] phi (byte*) MEM#5 = (byte*) MEM#1 [phi:@3->malloc#0] -- register_copy jsr malloc // [6] (byte*) malloc::return#3 ← (byte*) malloc::return#0 -- pbuz1=pbuz2 lda.z malloc.return sta.z malloc.return_2 lda.z malloc.return+1 sta.z malloc.return_2+1 jmp __b4 // @4 __b4: // [7] (byte*) SCREEN_2#0 ← (byte*) malloc::return#3 -- pbuz1=pbuz2 lda.z malloc.return_2 sta.z SCREEN_2 lda.z malloc.return_2+1 sta.z SCREEN_2+1 // [8] phi from @4 to @2 [phi:@4->@2] __b2_from___b4: jmp __b2 // @2 __b2: // [9] call main jsr main // [10] phi from @2 to @end [phi:@2->@end] __bend_from___b2: jmp __bend // @end __bend: // main main: { // [11] *((byte*) SCREEN_1#0) ← (byte) 0 -- _deref_pbuz1=vbuc1 lda #0 ldy #0 sta (SCREEN_1),y // [12] *((byte*) SCREEN_2#0) ← (byte) 0 -- _deref_pbuz1=vbuc1 lda #0 ldy #0 sta (SCREEN_2),y jmp __breturn // main::@return __breturn: // [13] return rts } // malloc malloc: { .label return = $c .label return_1 = 4 .label return_2 = 8 // [15] (byte*) MEM#1 ← ++ (byte*) MEM#5 -- pbuz1=_inc_pbuz1 inc.z MEM bne !+ inc.z MEM+1 !: // [16] (byte*) malloc::return#0 ← (byte*) MEM#1 -- pbuz1=pbuz2 lda.z MEM sta.z return lda.z MEM+1 sta.z return+1 jmp __breturn // malloc::@return __breturn: // [17] return rts } // File Data REGISTER UPLIFT POTENTIAL REGISTERS Statement [3] (byte*) malloc::return#2 ← (byte*) malloc::return#0 [ malloc::return#2 MEM#1 ] ( [ malloc::return#2 MEM#1 ] ) always clobbers reg byte a Statement [4] (byte*) SCREEN_1#0 ← (byte*) malloc::return#2 [ SCREEN_1#0 MEM#1 ] ( [ SCREEN_1#0 MEM#1 ] ) always clobbers reg byte a Statement [6] (byte*) malloc::return#3 ← (byte*) malloc::return#0 [ SCREEN_1#0 malloc::return#3 ] ( [ SCREEN_1#0 malloc::return#3 ] ) always clobbers reg byte a Statement [7] (byte*) SCREEN_2#0 ← (byte*) malloc::return#3 [ SCREEN_1#0 SCREEN_2#0 ] ( [ SCREEN_1#0 SCREEN_2#0 ] ) always clobbers reg byte a Statement [11] *((byte*) SCREEN_1#0) ← (byte) 0 [ SCREEN_2#0 ] ( main:9 [ SCREEN_2#0 ] ) always clobbers reg byte a reg byte y Statement [12] *((byte*) SCREEN_2#0) ← (byte) 0 [ ] ( main:9 [ ] ) always clobbers reg byte a reg byte y Statement [16] (byte*) malloc::return#0 ← (byte*) MEM#1 [ malloc::return#0 MEM#1 ] ( malloc:2 [ malloc::return#0 MEM#1 ] malloc:5 [ malloc::return#0 MEM#1 ] ) always clobbers reg byte a Potential registers zp[2]:2 [ MEM#5 MEM#1 ] : zp[2]:2 , Potential registers zp[2]:4 [ malloc::return#2 ] : zp[2]:4 , Potential registers zp[2]:6 [ SCREEN_1#0 ] : zp[2]:6 , Potential registers zp[2]:8 [ malloc::return#3 ] : zp[2]:8 , Potential registers zp[2]:10 [ SCREEN_2#0 ] : zp[2]:10 , Potential registers zp[2]:12 [ malloc::return#0 ] : zp[2]:12 , REGISTER UPLIFT SCOPES Uplift Scope [malloc] 4: zp[2]:4 [ malloc::return#2 ] 4: zp[2]:8 [ malloc::return#3 ] 1.5: zp[2]:12 [ malloc::return#0 ] Uplift Scope [] 5: zp[2]:2 [ MEM#5 MEM#1 ] 1.33: zp[2]:10 [ SCREEN_2#0 ] 0.8: zp[2]:6 [ SCREEN_1#0 ] Uplift Scope [main] Uplifting [malloc] best 153 combination zp[2]:4 [ malloc::return#2 ] zp[2]:8 [ malloc::return#3 ] zp[2]:12 [ malloc::return#0 ] Uplifting [] best 153 combination zp[2]:2 [ MEM#5 MEM#1 ] zp[2]:10 [ SCREEN_2#0 ] zp[2]:6 [ SCREEN_1#0 ] Uplifting [main] best 153 combination Coalescing zero page register [ zp[2]:4 [ malloc::return#2 ] ] with [ zp[2]:6 [ SCREEN_1#0 ] ] - score: 1 Coalescing zero page register [ zp[2]:8 [ malloc::return#3 ] ] with [ zp[2]:10 [ SCREEN_2#0 ] ] - score: 1 Coalescing zero page register [ zp[2]:8 [ malloc::return#3 SCREEN_2#0 ] ] with [ zp[2]:12 [ malloc::return#0 ] ] - score: 1 Allocated (was zp[2]:8) zp[2]:6 [ malloc::return#3 SCREEN_2#0 malloc::return#0 ] ASSEMBLER BEFORE OPTIMIZATION // File Comments // Error where the compiler is reusing the same ZP for two byte* variables. // SCREEN_1 and SCREEN_2 are both allocated to ZP: 4 // Problem is that outside main() scope statements have zero call-paths and then isStatementAllocationOverlapping() never checks liveranges // CallPath code must be rewritten to use @begin as the outermost call instead of main() // Upstart .pc = $801 "Basic" :BasicUpstart(__bbegin) .pc = $80d "Program" // Global Constants & labels .label MEM = 2 .label SCREEN_1 = 4 .label SCREEN_2 = 6 // @begin __bbegin: // [1] phi from @begin to @1 [phi:@begin->@1] __b1_from___bbegin: jmp __b1 // @1 __b1: // [2] call malloc // [14] phi from @1 to malloc [phi:@1->malloc] malloc_from___b1: // [14] phi (byte*) MEM#5 = (byte*) 1024 [phi:@1->malloc#0] -- pbuz1=pbuc1 lda #<$400 sta.z MEM lda #>$400 sta.z MEM+1 jsr malloc // [3] (byte*) malloc::return#2 ← (byte*) malloc::return#0 -- pbuz1=pbuz2 lda.z malloc.return sta.z malloc.return_1 lda.z malloc.return+1 sta.z malloc.return_1+1 jmp __b3 // @3 __b3: // [4] (byte*) SCREEN_1#0 ← (byte*) malloc::return#2 // [5] call malloc // [14] phi from @3 to malloc [phi:@3->malloc] malloc_from___b3: // [14] phi (byte*) MEM#5 = (byte*) MEM#1 [phi:@3->malloc#0] -- register_copy jsr malloc // [6] (byte*) malloc::return#3 ← (byte*) malloc::return#0 jmp __b4 // @4 __b4: // [7] (byte*) SCREEN_2#0 ← (byte*) malloc::return#3 // [8] phi from @4 to @2 [phi:@4->@2] __b2_from___b4: jmp __b2 // @2 __b2: // [9] call main jsr main // [10] phi from @2 to @end [phi:@2->@end] __bend_from___b2: jmp __bend // @end __bend: // main main: { // [11] *((byte*) SCREEN_1#0) ← (byte) 0 -- _deref_pbuz1=vbuc1 lda #0 ldy #0 sta (SCREEN_1),y // [12] *((byte*) SCREEN_2#0) ← (byte) 0 -- _deref_pbuz1=vbuc1 lda #0 ldy #0 sta (SCREEN_2),y jmp __breturn // main::@return __breturn: // [13] return rts } // malloc malloc: { .label return = 6 .label return_1 = 4 // [15] (byte*) MEM#1 ← ++ (byte*) MEM#5 -- pbuz1=_inc_pbuz1 inc.z MEM bne !+ inc.z MEM+1 !: // [16] (byte*) malloc::return#0 ← (byte*) MEM#1 -- pbuz1=pbuz2 lda.z MEM sta.z return lda.z MEM+1 sta.z return+1 jmp __breturn // malloc::@return __breturn: // [17] return rts } // File Data ASSEMBLER OPTIMIZATIONS Removing instruction jmp __b1 Removing instruction jmp __b3 Removing instruction jmp __b4 Removing instruction jmp __b2 Removing instruction jmp __bend Removing instruction jmp __breturn Removing instruction jmp __breturn Succesful ASM optimization Pass5NextJumpElimination Replacing instruction ldy #0 with TAY Removing instruction lda #0 Removing instruction ldy #0 Succesful ASM optimization Pass5UnnecesaryLoadElimination Replacing label __bbegin with __b1 Removing instruction __bbegin: Removing instruction __b1_from___bbegin: Removing instruction malloc_from___b1: Removing instruction malloc_from___b3: Removing instruction __b4: Removing instruction __b2_from___b4: Removing instruction __bend_from___b2: Succesful ASM optimization Pass5RedundantLabelElimination Removing instruction __b3: Removing instruction __b2: Removing instruction __bend: Removing instruction __breturn: Removing instruction __breturn: Succesful ASM optimization Pass5UnusedLabelElimination Adding RTS to root block Succesful ASM optimization Pass5AddMainRts FINAL SYMBOL TABLE (label) @1 (label) @2 (label) @3 (label) @4 (label) @begin (label) @end (byte*) MEM (byte*) MEM#1 MEM zp[2]:2 1.0 (byte*) MEM#5 MEM zp[2]:2 4.0 (byte*) SCREEN_1 (byte*) SCREEN_1#0 SCREEN_1 zp[2]:4 0.8 (byte*) SCREEN_2 (byte*) SCREEN_2#0 SCREEN_2 zp[2]:6 1.3333333333333333 (void()) main() (label) main::@return (byte*()) malloc() (label) malloc::@return (byte*) malloc::return (byte*) malloc::return#0 return zp[2]:6 1.5 (byte*) malloc::return#2 return_1 zp[2]:4 4.0 (byte*) malloc::return#3 return zp[2]:6 4.0 zp[2]:2 [ MEM#5 MEM#1 ] zp[2]:4 [ malloc::return#2 SCREEN_1#0 ] zp[2]:6 [ malloc::return#3 SCREEN_2#0 malloc::return#0 ] FINAL ASSEMBLER Score: 98 // File Comments // Error where the compiler is reusing the same ZP for two byte* variables. // SCREEN_1 and SCREEN_2 are both allocated to ZP: 4 // Problem is that outside main() scope statements have zero call-paths and then isStatementAllocationOverlapping() never checks liveranges // CallPath code must be rewritten to use @begin as the outermost call instead of main() // Upstart .pc = $801 "Basic" :BasicUpstart(__b1) .pc = $80d "Program" // Global Constants & labels .label MEM = 2 .label SCREEN_1 = 4 .label SCREEN_2 = 6 // @begin // [1] phi from @begin to @1 [phi:@begin->@1] // @1 __b1: // malloc() // [2] call malloc // [14] phi from @1 to malloc [phi:@1->malloc] // [14] phi (byte*) MEM#5 = (byte*) 1024 [phi:@1->malloc#0] -- pbuz1=pbuc1 lda #<$400 sta.z MEM lda #>$400 sta.z MEM+1 jsr malloc // malloc() // [3] (byte*) malloc::return#2 ← (byte*) malloc::return#0 -- pbuz1=pbuz2 lda.z malloc.return sta.z malloc.return_1 lda.z malloc.return+1 sta.z malloc.return_1+1 // @3 // SCREEN_1 = malloc() // [4] (byte*) SCREEN_1#0 ← (byte*) malloc::return#2 // malloc() // [5] call malloc // [14] phi from @3 to malloc [phi:@3->malloc] // [14] phi (byte*) MEM#5 = (byte*) MEM#1 [phi:@3->malloc#0] -- register_copy jsr malloc // malloc() // [6] (byte*) malloc::return#3 ← (byte*) malloc::return#0 // @4 // SCREEN_2 = malloc() // [7] (byte*) SCREEN_2#0 ← (byte*) malloc::return#3 // [8] phi from @4 to @2 [phi:@4->@2] // @2 // [9] call main jsr main rts // [10] phi from @2 to @end [phi:@2->@end] // @end // main main: { // *SCREEN_1 = 0 // [11] *((byte*) SCREEN_1#0) ← (byte) 0 -- _deref_pbuz1=vbuc1 lda #0 tay sta (SCREEN_1),y // *SCREEN_2 = 0 // [12] *((byte*) SCREEN_2#0) ← (byte) 0 -- _deref_pbuz1=vbuc1 sta (SCREEN_2),y // main::@return // } // [13] return rts } // malloc malloc: { .label return = 6 .label return_1 = 4 // return ++MEM; // [15] (byte*) MEM#1 ← ++ (byte*) MEM#5 -- pbuz1=_inc_pbuz1 inc.z MEM bne !+ inc.z MEM+1 !: // [16] (byte*) malloc::return#0 ← (byte*) MEM#1 -- pbuz1=pbuz2 lda.z MEM sta.z return lda.z MEM+1 sta.z return+1 // malloc::@return // } // [17] return rts } // File Data