Constantified RValue points[main::$2] = { x: 1, y: 2 } Constantified RValue points[main::$3] = { x: 3, y: 4 } Inlined call call __init CONTROL FLOW GRAPH SSA void main() main: scope:[main] from __start::@1 idx#16 = phi( __start::@1/idx#15 ) main::$2 = 0 * SIZEOF_STRUCT_POINT points[main::$2] = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) main::$3 = 1 * SIZEOF_STRUCT_POINT points[main::$3] = memcpy(*(&$1), struct Point, SIZEOF_STRUCT_POINT) main::i#0 = 0 to:main::@1 main::@1: scope:[main] from main main::@2 idx#14 = phi( main/idx#16, main::@2/idx#0 ) main::i#2 = phi( main/main::i#0, main::@2/main::i#1 ) main::$4 = main::i#2 * SIZEOF_STRUCT_POINT print::p_x#0 = ((char *)points+OFFSET_STRUCT_POINT_X)[main::$4] print::p_y#0 = ((char *)points+OFFSET_STRUCT_POINT_Y)[main::$4] call print to:main::@2 main::@2: scope:[main] from main::@1 main::i#3 = phi( main::@1/main::i#2 ) idx#8 = phi( main::@1/idx#4 ) idx#0 = idx#8 main::i#1 = main::i#3 + rangenext(0,1) main::$1 = main::i#1 != rangelast(0,1) if(main::$1) goto main::@1 to:main::@return main::@return: scope:[main] from main::@2 idx#9 = phi( main::@2/idx#0 ) idx#1 = idx#9 return to:@return void print(char p_x , char p_y) print: scope:[print] from main::@1 print::p_y#1 = phi( main::@1/print::p_y#0 ) idx#10 = phi( main::@1/idx#14 ) print::p_x#1 = phi( main::@1/print::p_x#0 ) SCREEN[idx#10] = print::p_x#1 idx#2 = ++ idx#10 SCREEN[idx#2] = print::p_y#1 idx#3 = ++ idx#2 to:print::@return print::@return: scope:[print] from print idx#11 = phi( print/idx#3 ) idx#4 = idx#11 return to:@return void __start() __start: scope:[__start] from to:__start::__init1 __start::__init1: scope:[__start] from __start idx#5 = 0 to:__start::@1 __start::@1: scope:[__start] from __start::__init1 idx#15 = phi( __start::__init1/idx#5 ) call main to:__start::@2 __start::@2: scope:[__start] from __start::@1 idx#12 = phi( __start::@1/idx#1 ) idx#6 = idx#12 to:__start::@return __start::@return: scope:[__start] from __start::@2 idx#13 = phi( __start::@2/idx#6 ) idx#7 = idx#13 return to:@return SYMBOL TABLE SSA __constant struct Point $0 = { x: 1, y: 2 } __constant struct Point $1 = { x: 3, y: 4 } __constant char OFFSET_STRUCT_POINT_X = 0 __constant char OFFSET_STRUCT_POINT_Y = 1 __constant char * const SCREEN = (char *)$400 __constant char SIZEOF_STRUCT_POINT = 2 void __start() char idx char idx#0 char idx#1 char idx#10 char idx#11 char idx#12 char idx#13 char idx#14 char idx#15 char idx#16 char idx#2 char idx#3 char idx#4 char idx#5 char idx#6 char idx#7 char idx#8 char idx#9 void main() bool main::$1 number main::$2 number main::$3 char main::$4 char main::i char main::i#0 char main::i#1 char main::i#2 char main::i#3 __constant struct Point points[2] = { fill( 2, 0) } void print(char p_x , char p_y) struct Point print::p char print::p_x char print::p_x#0 char print::p_x#1 char print::p_y char print::p_y#0 char print::p_y#1 Adding number conversion cast (unumber) 0 in main::$2 = 0 * SIZEOF_STRUCT_POINT Adding number conversion cast (unumber) main::$2 in main::$2 = (unumber)0 * SIZEOF_STRUCT_POINT Adding number conversion cast (unumber) 1 in main::$3 = 1 * SIZEOF_STRUCT_POINT Adding number conversion cast (unumber) main::$3 in main::$3 = (unumber)1 * SIZEOF_STRUCT_POINT Successful SSA optimization PassNAddNumberTypeConversions Simplifying constant pointer cast (char *) 1024 Simplifying constant integer cast 0 Simplifying constant integer cast 1 Successful SSA optimization PassNCastSimplification Finalized unsigned number type (char) 0 Finalized unsigned number type (char) 1 Successful SSA optimization PassNFinalizeNumberTypeConversions Inferred type updated to char in main::$2 = 0 * SIZEOF_STRUCT_POINT Inferred type updated to char in main::$3 = 1 * SIZEOF_STRUCT_POINT Alias main::i#2 = main::i#3 Alias idx#0 = idx#8 idx#9 idx#1 Alias idx#11 = idx#3 idx#4 Alias idx#15 = idx#5 Alias idx#12 = idx#6 idx#13 idx#7 Successful SSA optimization Pass2AliasElimination Identical Phi Values idx#16 idx#15 Identical Phi Values idx#0 idx#11 Identical Phi Values print::p_x#1 print::p_x#0 Identical Phi Values idx#10 idx#14 Identical Phi Values print::p_y#1 print::p_y#0 Identical Phi Values idx#12 idx#0 Successful SSA optimization Pass2IdenticalPhiElimination Simple Condition main::$1 [14] if(main::i#1!=rangelast(0,1)) goto main::@1 Successful SSA optimization Pass2ConditionalJumpSimplification Constant right-side identified [1] main::$2 = 0 * SIZEOF_STRUCT_POINT Constant right-side identified [3] main::$3 = 1 * SIZEOF_STRUCT_POINT Successful SSA optimization Pass2ConstantRValueConsolidation Constant main::$2 = 0*SIZEOF_STRUCT_POINT Constant main::$3 = 1*SIZEOF_STRUCT_POINT Constant main::i#0 = 0 Constant idx#15 = 0 Successful SSA optimization Pass2ConstantIdentification Resolved ranged next value [12] main::i#1 = ++ main::i#2 to ++ Resolved ranged comparison value [14] if(main::i#1!=rangelast(0,1)) goto main::@1 to 2 Simplifying constant evaluating to zero 0*SIZEOF_STRUCT_POINT in Successful SSA optimization PassNSimplifyConstantZero Simplifying expression containing zero points in [2] points[main::$2] = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) Simplifying expression containing zero (char *)points in [8] print::p_x#0 = ((char *)points+OFFSET_STRUCT_POINT_X)[main::$4] Successful SSA optimization PassNSimplifyExpressionWithZero Eliminating unused constant main::$2 Eliminating unused constant OFFSET_STRUCT_POINT_X Successful SSA optimization PassNEliminateUnusedVars Removing unused procedure __start Removing unused procedure block __start Removing unused procedure block __start::__init1 Removing unused procedure block __start::@1 Removing unused procedure block __start::@2 Removing unused procedure block __start::@return Successful SSA optimization PassNEliminateEmptyStart Adding number conversion cast (unumber) 2 in [8] if(main::i#1!=2) goto main::@1 Successful SSA optimization PassNAddNumberTypeConversions Simplifying constant integer cast 2 Successful SSA optimization PassNCastSimplification Finalized unsigned number type (char) 2 Successful SSA optimization PassNFinalizeNumberTypeConversions Rewriting multiplication to use shift [3] main::$4 = main::i#2 * SIZEOF_STRUCT_POINT Successful SSA optimization Pass2MultiplyToShiftRewriting Inlining constant with var siblings main::i#0 Inlining constant with var siblings idx#15 Constant inlined main::i#0 = 0 Constant inlined main::$3 = 1*SIZEOF_STRUCT_POINT Constant inlined idx#15 = 0 Successful SSA optimization Pass2ConstantInlining Consolidated array index constant in *(points+1*SIZEOF_STRUCT_POINT) Successful SSA optimization Pass2ConstantAdditionElimination Finalized unsigned number type (char) 2 Finalized unsigned number type (char) 2 Successful SSA optimization PassNFinalizeNumberTypeConversions Added new block during phi lifting main::@3(between main::@2 and main::@1) CALL GRAPH Calls in [main] to print:6 Created 2 initial phi equivalence classes Coalesced [10] main::i#4 = main::i#1 Coalesced [11] idx#17 = idx#11 Coalesced down to 2 phi equivalence classes Culled Empty Block label main::@3 FINAL CONTROL FLOW GRAPH void main() main: scope:[main] from [0] *points = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) [1] *(points+1*SIZEOF_STRUCT_POINT) = memcpy(*(&$1), struct Point, SIZEOF_STRUCT_POINT) to:main::@1 main::@1: scope:[main] from main main::@2 [2] idx#14 = phi( main/0, main::@2/idx#11 ) [2] main::i#2 = phi( main/0, main::@2/main::i#1 ) [3] main::$4 = main::i#2 << 1 [4] print::p_x#0 = ((char *)points)[main::$4] [5] print::p_y#0 = ((char *)points+OFFSET_STRUCT_POINT_Y)[main::$4] [6] call print to:main::@2 main::@2: scope:[main] from main::@1 [7] main::i#1 = ++ main::i#2 [8] if(main::i#1!=2) goto main::@1 to:main::@return main::@return: scope:[main] from main::@2 [9] return to:@return void print(char p_x , char p_y) print: scope:[print] from main::@1 [10] SCREEN[idx#14] = print::p_x#0 [11] idx#2 = ++ idx#14 [12] SCREEN[idx#2] = print::p_y#0 [13] idx#11 = ++ idx#2 to:print::@return print::@return: scope:[print] from print [14] return to:@return VARIABLE REGISTER WEIGHTS char idx char idx#11 // 22.4 char idx#14 // 42.599999999999994 char idx#2 // 151.5 void main() char main::$4 // 16.5 char main::i char main::i#1 // 16.5 char main::i#2 // 6.6000000000000005 void print(char p_x , char p_y) struct Point print::p char print::p_x char print::p_x#0 // 56.0 char print::p_y char print::p_y#0 // 37.33333333333333 Initial phi equivalence classes [ main::i#2 main::i#1 ] [ idx#14 idx#11 ] Added variable main::$4 to live range equivalence class [ main::$4 ] Added variable print::p_x#0 to live range equivalence class [ print::p_x#0 ] Added variable print::p_y#0 to live range equivalence class [ print::p_y#0 ] Added variable idx#2 to live range equivalence class [ idx#2 ] Complete equivalence classes [ main::i#2 main::i#1 ] [ idx#14 idx#11 ] [ main::$4 ] [ print::p_x#0 ] [ print::p_y#0 ] [ idx#2 ] Allocated zp[1]:2 [ main::i#2 main::i#1 ] Allocated zp[1]:3 [ idx#14 idx#11 ] Allocated zp[1]:4 [ main::$4 ] Allocated zp[1]:5 [ print::p_x#0 ] Allocated zp[1]:6 [ print::p_y#0 ] Allocated zp[1]:7 [ idx#2 ] REGISTER UPLIFT POTENTIAL REGISTERS Statement [0] *points = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) [ ] ( [ ] { } ) always clobbers reg byte a reg byte y Statement [1] *(points+1*SIZEOF_STRUCT_POINT) = memcpy(*(&$1), struct Point, SIZEOF_STRUCT_POINT) [ ] ( [ ] { } ) always clobbers reg byte a reg byte y Statement [3] main::$4 = main::i#2 << 1 [ main::i#2 idx#14 main::$4 ] ( [ main::i#2 idx#14 main::$4 ] { } ) always clobbers reg byte a Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::i#2 main::i#1 ] Removing always clobbered register reg byte a as potential for zp[1]:3 [ idx#14 idx#11 ] Statement [0] *points = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) [ ] ( [ ] { } ) always clobbers reg byte a reg byte y Statement [1] *(points+1*SIZEOF_STRUCT_POINT) = memcpy(*(&$1), struct Point, SIZEOF_STRUCT_POINT) [ ] ( [ ] { } ) always clobbers reg byte a reg byte y Statement [3] main::$4 = main::i#2 << 1 [ main::i#2 idx#14 main::$4 ] ( [ main::i#2 idx#14 main::$4 ] { } ) 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 , Potential registers zp[1]:3 [ idx#14 idx#11 ] : zp[1]:3 , reg byte x , reg byte y , Potential registers zp[1]:4 [ main::$4 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , Potential registers zp[1]:5 [ print::p_x#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , Potential registers zp[1]:6 [ print::p_y#0 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , Potential registers zp[1]:7 [ idx#2 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , REGISTER UPLIFT SCOPES Uplift Scope [] 151.5: zp[1]:7 [ idx#2 ] 65: zp[1]:3 [ idx#14 idx#11 ] Uplift Scope [print] 56: zp[1]:5 [ print::p_x#0 ] 37.33: zp[1]:6 [ print::p_y#0 ] Uplift Scope [main] 23.1: zp[1]:2 [ main::i#2 main::i#1 ] 16.5: zp[1]:4 [ main::$4 ] Uplift Scope [Point] Uplifting [] best 732 combination reg byte x [ idx#2 ] reg byte x [ idx#14 idx#11 ] Uplifting [print] best 732 combination zp[1]:5 [ print::p_x#0 ] zp[1]:6 [ print::p_y#0 ] Uplifting [main] best 662 combination zp[1]:2 [ main::i#2 main::i#1 ] reg byte y [ main::$4 ] Uplifting [Point] best 662 combination Attempting to uplift remaining variables inzp[1]:5 [ print::p_x#0 ] Uplifting [print] best 662 combination zp[1]:5 [ print::p_x#0 ] Attempting to uplift remaining variables inzp[1]:6 [ print::p_y#0 ] Uplifting [print] best 651 combination reg byte y [ print::p_y#0 ] Attempting to uplift remaining variables inzp[1]:2 [ main::i#2 main::i#1 ] Uplifting [main] best 651 combination zp[1]:2 [ main::i#2 main::i#1 ] Allocated (was zp[1]:5) zp[1]:3 [ print::p_x#0 ] ASSEMBLER BEFORE OPTIMIZATION // File Comments // Demonstrates problem with passing struct array element as parameter to call // Upstart // Commodore 64 PRG executable file .file [name="struct-ptr-18.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 SIZEOF_STRUCT_POINT = 2 .const OFFSET_STRUCT_POINT_Y = 1 .label SCREEN = $400 .segment Code // main main: { .label i = 2 // [0] *points = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 ldy #SIZEOF_STRUCT_POINT !: lda __0-1,y sta points-1,y dey bne !- // [1] *(points+1*SIZEOF_STRUCT_POINT) = memcpy(*(&$1), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 ldy #SIZEOF_STRUCT_POINT !: lda __1-1,y sta points+1*SIZEOF_STRUCT_POINT-1,y dey bne !- // [2] phi from main to main::@1 [phi:main->main::@1] __b1_from_main: // [2] phi idx#14 = 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 ldx #0 // [2] phi main::i#2 = 0 [phi:main->main::@1#1] -- vbuz1=vbuc1 lda #0 sta.z i jmp __b1 // [2] phi from main::@2 to main::@1 [phi:main::@2->main::@1] __b1_from___b2: // [2] phi idx#14 = idx#11 [phi:main::@2->main::@1#0] -- register_copy // [2] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#1] -- register_copy jmp __b1 // main::@1 __b1: // [3] main::$4 = main::i#2 << 1 -- vbuyy=vbuz1_rol_1 lda.z i asl tay // [4] print::p_x#0 = ((char *)points)[main::$4] -- vbuz1=pbuc1_derefidx_vbuyy lda points,y sta.z print.p_x // [5] print::p_y#0 = ((char *)points+OFFSET_STRUCT_POINT_Y)[main::$4] -- vbuyy=pbuc1_derefidx_vbuyy lda points+OFFSET_STRUCT_POINT_Y,y tay // [6] call print jsr print jmp __b2 // main::@2 __b2: // [7] main::i#1 = ++ main::i#2 -- vbuz1=_inc_vbuz1 inc.z i // [8] if(main::i#1!=2) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 lda #2 cmp.z i bne __b1_from___b2 jmp __breturn // main::@return __breturn: // [9] return rts } // print // void print(__zp(3) char p_x, __register(Y) char p_y) print: { .label p_x = 3 // [10] SCREEN[idx#14] = print::p_x#0 -- pbuc1_derefidx_vbuxx=vbuz1 lda.z p_x sta SCREEN,x // [11] idx#2 = ++ idx#14 -- vbuxx=_inc_vbuxx inx // [12] SCREEN[idx#2] = print::p_y#0 -- pbuc1_derefidx_vbuxx=vbuyy tya sta SCREEN,x // [13] idx#11 = ++ idx#2 -- vbuxx=_inc_vbuxx inx jmp __breturn // print::@return __breturn: // [14] return rts } // File Data .segment Data points: .fill 2*2, 0 __0: .byte 1, 2 __1: .byte 3, 4 ASSEMBLER OPTIMIZATIONS Removing instruction jmp __b1 Removing instruction jmp __b2 Removing instruction jmp __breturn Removing instruction jmp __breturn Succesful ASM optimization Pass5NextJumpElimination Replacing instruction lda #0 with TXA Replacing label __b1_from___b2 with __b1 Removing instruction __b1_from___b2: Succesful ASM optimization Pass5RedundantLabelElimination Removing instruction __b1_from_main: Removing instruction __b2: Removing instruction __breturn: Removing instruction __breturn: Succesful ASM optimization Pass5UnusedLabelElimination Removing instruction jmp __b1 Succesful ASM optimization Pass5NextJumpElimination FINAL SYMBOL TABLE __constant struct Point $0 = { x: 1, y: 2 } __constant struct Point $1 = { x: 3, y: 4 } __constant char OFFSET_STRUCT_POINT_Y = 1 __constant char * const SCREEN = (char *) 1024 __constant char SIZEOF_STRUCT_POINT = 2 char idx char idx#11 // reg byte x 22.4 char idx#14 // reg byte x 42.599999999999994 char idx#2 // reg byte x 151.5 void main() char main::$4 // reg byte y 16.5 char main::i char main::i#1 // i zp[1]:2 16.5 char main::i#2 // i zp[1]:2 6.6000000000000005 __constant struct Point points[2] = { fill( 2, 0) } void print(char p_x , char p_y) struct Point print::p char print::p_x char print::p_x#0 // p_x zp[1]:3 56.0 char print::p_y char print::p_y#0 // reg byte y 37.33333333333333 zp[1]:2 [ main::i#2 main::i#1 ] reg byte x [ idx#14 idx#11 ] reg byte y [ main::$4 ] zp[1]:3 [ print::p_x#0 ] reg byte y [ print::p_y#0 ] reg byte x [ idx#2 ] FINAL ASSEMBLER Score: 528 // File Comments // Demonstrates problem with passing struct array element as parameter to call // Upstart // Commodore 64 PRG executable file .file [name="struct-ptr-18.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 SIZEOF_STRUCT_POINT = 2 .const OFFSET_STRUCT_POINT_Y = 1 .label SCREEN = $400 .segment Code // main main: { .label i = 2 // points[0] = { 1, 2 } // [0] *points = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 ldy #SIZEOF_STRUCT_POINT !: lda __0-1,y sta points-1,y dey bne !- // points[1] = { 3, 4 } // [1] *(points+1*SIZEOF_STRUCT_POINT) = memcpy(*(&$1), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 ldy #SIZEOF_STRUCT_POINT !: lda __1-1,y sta points+1*SIZEOF_STRUCT_POINT-1,y dey bne !- // [2] phi from main to main::@1 [phi:main->main::@1] // [2] phi idx#14 = 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 ldx #0 // [2] phi main::i#2 = 0 [phi:main->main::@1#1] -- vbuz1=vbuc1 txa sta.z i // [2] phi from main::@2 to main::@1 [phi:main::@2->main::@1] // [2] phi idx#14 = idx#11 [phi:main::@2->main::@1#0] -- register_copy // [2] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#1] -- register_copy // main::@1 __b1: // print(points[i]) // [3] main::$4 = main::i#2 << 1 -- vbuyy=vbuz1_rol_1 lda.z i asl tay // [4] print::p_x#0 = ((char *)points)[main::$4] -- vbuz1=pbuc1_derefidx_vbuyy lda points,y sta.z print.p_x // [5] print::p_y#0 = ((char *)points+OFFSET_STRUCT_POINT_Y)[main::$4] -- vbuyy=pbuc1_derefidx_vbuyy lda points+OFFSET_STRUCT_POINT_Y,y tay // [6] call print jsr print // main::@2 // for ( char i: 0..1) // [7] main::i#1 = ++ main::i#2 -- vbuz1=_inc_vbuz1 inc.z i // [8] if(main::i#1!=2) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 lda #2 cmp.z i bne __b1 // main::@return // } // [9] return rts } // print // void print(__zp(3) char p_x, __register(Y) char p_y) print: { .label p_x = 3 // SCREEN[idx++] = p.x // [10] SCREEN[idx#14] = print::p_x#0 -- pbuc1_derefidx_vbuxx=vbuz1 lda.z p_x sta SCREEN,x // SCREEN[idx++] = p.x; // [11] idx#2 = ++ idx#14 -- vbuxx=_inc_vbuxx inx // SCREEN[idx++] = p.y // [12] SCREEN[idx#2] = print::p_y#0 -- pbuc1_derefidx_vbuxx=vbuyy tya sta SCREEN,x // SCREEN[idx++] = p.y; // [13] idx#11 = ++ idx#2 -- vbuxx=_inc_vbuxx inx // print::@return // } // [14] return rts } // File Data .segment Data points: .fill 2*2, 0 __0: .byte 1, 2 __1: .byte 3, 4