diff --git a/src/test/ref/struct-unwinding-1.asm b/src/test/ref/struct-unwinding-1.asm new file mode 100644 index 000000000..fa1a3836e --- /dev/null +++ b/src/test/ref/struct-unwinding-1.asm @@ -0,0 +1,58 @@ +// Test combining unwind structs with classic structs +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 +main: { + .label p1 = 2 + // p1 = { 1, 2 } + ldy #SIZEOF_STRUCT_POINT + !: + lda __0-1,y + sta p1-1,y + dey + bne !- + // SCREEN[0] = p1 + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta SCREEN-1,y + dey + bne !- + // p2 = p1 + lda.z p1 + ldx p1+OFFSET_STRUCT_POINT_Y + // SCREEN[2] = p2 + sta SCREEN+2*SIZEOF_STRUCT_POINT + stx SCREEN+2*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y + // p1.x = 3 + // Set in classic struct + lda #3 + sta.z p1 + // SCREEN[4] = p1 + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta SCREEN+4*SIZEOF_STRUCT_POINT-1,y + dey + bne !- + // SCREEN[6] = p2 + lda #4 + sta SCREEN+6*SIZEOF_STRUCT_POINT + stx SCREEN+6*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y + // p1 = p2 + sta.z p1 + stx p1+OFFSET_STRUCT_POINT_Y + // SCREEN[8] = p1 + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta SCREEN+8*SIZEOF_STRUCT_POINT-1,y + dey + bne !- + // } + rts +} + __0: .byte 1, 2 diff --git a/src/test/ref/struct-unwinding-1.cfg b/src/test/ref/struct-unwinding-1.cfg new file mode 100644 index 000000000..5a5f395c2 --- /dev/null +++ b/src/test/ref/struct-unwinding-1.cfg @@ -0,0 +1,20 @@ + +void main() +main: scope:[main] from + [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) + [1] *SCREEN = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + [2] main::p2_x#0 = *((byte*)&main::p1) + [3] main::p2_y#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) + [4] *((byte*)SCREEN+2*SIZEOF_STRUCT_POINT) = main::p2_x#0 + [5] *((byte*)SCREEN+2*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 + [6] *((byte*)&main::p1) = 3 + [7] *(SCREEN+4*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + [8] *((byte*)SCREEN+6*SIZEOF_STRUCT_POINT) = 4 + [9] *((byte*)SCREEN+6*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 + [10] *((byte*)&main::p1) = 4 + [11] *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 + [12] *(SCREEN+8*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + to:main::@return +main::@return: scope:[main] from main + [13] return + to:@return diff --git a/src/test/ref/struct-unwinding-1.log b/src/test/ref/struct-unwinding-1.log new file mode 100644 index 000000000..953db860b --- /dev/null +++ b/src/test/ref/struct-unwinding-1.log @@ -0,0 +1,423 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) + main::p1 = struct-unwound {*(&main::p1)} + main::$0 = 0 * SIZEOF_STRUCT_POINT + SCREEN[main::$0] = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + main::p2_x#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_X) + main::p2_y#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) + main::$1 = 2 * SIZEOF_STRUCT_POINT + main::$5 = (byte*)SCREEN + main::$1 + main::$5[OFFSET_STRUCT_POINT_X] = main::p2_x#0 + main::$6 = (byte*)SCREEN + main::$1 + main::$6[OFFSET_STRUCT_POINT_Y] = main::p2_y#0 + *((byte*)&main::p1+OFFSET_STRUCT_POINT_X) = 3 + main::$2 = 4 * SIZEOF_STRUCT_POINT + SCREEN[main::$2] = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + main::p2_x#1 = 4 + main::$3 = 6 * SIZEOF_STRUCT_POINT + main::$7 = (byte*)SCREEN + main::$3 + main::$7[OFFSET_STRUCT_POINT_X] = main::p2_x#1 + main::$8 = (byte*)SCREEN + main::$3 + main::$8[OFFSET_STRUCT_POINT_Y] = main::p2_y#0 + *((byte*)&main::p1+OFFSET_STRUCT_POINT_X) = main::p2_x#1 + *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 + main::p1 = struct-unwound {*((byte*)&main::p1+OFFSET_STRUCT_POINT_X), *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y)} + main::$4 = 8 * SIZEOF_STRUCT_POINT + SCREEN[main::$4] = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + to:main::@return +main::@return: scope:[main] from main + 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 +const struct Point $0 = { x: 1, y: 2 } +const byte OFFSET_STRUCT_POINT_X = 0 +const byte OFFSET_STRUCT_POINT_Y = 1 +const nomodify struct Point* SCREEN = (struct Point*)$400 +const byte SIZEOF_STRUCT_POINT = 2 +void __start() +void main() +number~ main::$0 +number~ main::$1 +number~ main::$2 +number~ main::$3 +number~ main::$4 +byte*~ main::$5 +byte*~ main::$6 +byte*~ main::$7 +byte*~ main::$8 +struct Point main::p1 loadstore +byte main::p2_x +byte main::p2_x#0 +byte main::p2_x#1 +byte main::p2_y +byte main::p2_y#0 + +Adding number conversion cast (unumber) 0 in main::$0 = 0 * SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) main::$0 in main::$0 = (unumber)0 * SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) 2 in main::$1 = 2 * SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) main::$1 in main::$1 = (unumber)2 * SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) 3 in *((byte*)&main::p1+OFFSET_STRUCT_POINT_X) = 3 +Adding number conversion cast (unumber) 4 in main::$2 = 4 * SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) main::$2 in main::$2 = (unumber)4 * SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) 4 in main::p2_x#1 = 4 +Adding number conversion cast (unumber) 6 in main::$3 = 6 * SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) main::$3 in main::$3 = (unumber)6 * SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) 8 in main::$4 = 8 * SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) main::$4 in main::$4 = (unumber)8 * SIZEOF_STRUCT_POINT +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast *((byte*)&main::p1+OFFSET_STRUCT_POINT_X) = (unumber)3 +Inlining cast main::p2_x#1 = (unumber)4 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (struct Point*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 2 +Simplifying constant integer cast 3 +Simplifying constant integer cast 4 +Simplifying constant integer cast 4 +Simplifying constant integer cast 6 +Simplifying constant integer cast 8 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type 0 +Finalized unsigned number type 2 +Finalized unsigned number type 3 +Finalized unsigned number type 4 +Finalized unsigned number type 4 +Finalized unsigned number type 6 +Finalized unsigned number type 8 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inferred type updated to byte in main::$0 = 0 * SIZEOF_STRUCT_POINT +Inferred type updated to byte in main::$1 = 2 * SIZEOF_STRUCT_POINT +Inferred type updated to byte in main::$2 = 4 * SIZEOF_STRUCT_POINT +Inferred type updated to byte in main::$3 = 6 * SIZEOF_STRUCT_POINT +Inferred type updated to byte in main::$4 = 8 * SIZEOF_STRUCT_POINT +Removing C-classic struct-unwound assignment [1] main::p1 = struct-unwound {*(&main::p1)} +Removing C-classic struct-unwound assignment [22] main::p1 = struct-unwound {*((byte*)&main::p1+OFFSET_STRUCT_POINT_X), *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y)} +Constant right-side identified [2] main::$0 = 0 * SIZEOF_STRUCT_POINT +Constant right-side identified [6] main::$1 = 2 * SIZEOF_STRUCT_POINT +Constant right-side identified [12] main::$2 = 4 * SIZEOF_STRUCT_POINT +Constant right-side identified [15] main::$3 = 6 * SIZEOF_STRUCT_POINT +Constant right-side identified [23] main::$4 = 8 * SIZEOF_STRUCT_POINT +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant main::$0 = 0*SIZEOF_STRUCT_POINT +Constant main::$1 = 2*SIZEOF_STRUCT_POINT +Constant main::$2 = 4*SIZEOF_STRUCT_POINT +Constant main::p2_x#1 = 4 +Constant main::$3 = 6*SIZEOF_STRUCT_POINT +Constant main::$4 = 8*SIZEOF_STRUCT_POINT +Successful SSA optimization Pass2ConstantIdentification +Simplifying constant evaluating to zero 0*SIZEOF_STRUCT_POINT in +Successful SSA optimization PassNSimplifyConstantZero +Simplifying expression containing zero SCREEN in [3] SCREEN[main::$0] = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) +Simplifying expression containing zero (byte*)&main::p1 in [4] main::p2_x#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_X) +Simplifying expression containing zero main::$5 in [8] main::$5[OFFSET_STRUCT_POINT_X] = main::p2_x#0 +Simplifying expression containing zero (byte*)&main::p1 in [11] *((byte*)&main::p1+OFFSET_STRUCT_POINT_X) = 3 +Simplifying expression containing zero main::$7 in [17] main::$7[OFFSET_STRUCT_POINT_X] = main::p2_x#1 +Simplifying expression containing zero (byte*)&main::p1 in [20] *((byte*)&main::p1+OFFSET_STRUCT_POINT_X) = main::p2_x#1 +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant main::$0 +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::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Constant right-side identified [4] main::$5 = (byte*)SCREEN + main::$1 +Constant right-side identified [6] main::$6 = (byte*)SCREEN + main::$1 +Constant right-side identified [10] main::$7 = (byte*)SCREEN + main::$3 +Constant right-side identified [12] main::$8 = (byte*)SCREEN + main::$3 +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant main::$5 = (byte*)SCREEN+main::$1 +Constant main::$6 = (byte*)SCREEN+main::$1 +Constant main::$7 = (byte*)SCREEN+main::$3 +Constant main::$8 = (byte*)SCREEN+main::$3 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings main::p2_x#1 +Constant inlined main::$1 = 2*SIZEOF_STRUCT_POINT +Constant inlined main::$2 = 4*SIZEOF_STRUCT_POINT +Constant inlined main::$5 = (byte*)SCREEN+2*SIZEOF_STRUCT_POINT +Constant inlined main::$6 = (byte*)SCREEN+2*SIZEOF_STRUCT_POINT +Constant inlined main::$3 = 6*SIZEOF_STRUCT_POINT +Constant inlined main::$4 = 8*SIZEOF_STRUCT_POINT +Constant inlined main::$7 = (byte*)SCREEN+6*SIZEOF_STRUCT_POINT +Constant inlined main::p2_x#1 = 4 +Constant inlined main::$8 = (byte*)SCREEN+6*SIZEOF_STRUCT_POINT +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *((byte*)SCREEN+2*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y) +Consolidated array index constant in *(SCREEN+4*SIZEOF_STRUCT_POINT) +Consolidated array index constant in *((byte*)SCREEN+6*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y) +Consolidated array index constant in *(SCREEN+8*SIZEOF_STRUCT_POINT) +Successful SSA optimization Pass2ConstantAdditionElimination +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) + [1] *SCREEN = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + [2] main::p2_x#0 = *((byte*)&main::p1) + [3] main::p2_y#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) + [4] *((byte*)SCREEN+2*SIZEOF_STRUCT_POINT) = main::p2_x#0 + [5] *((byte*)SCREEN+2*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 + [6] *((byte*)&main::p1) = 3 + [7] *(SCREEN+4*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + [8] *((byte*)SCREEN+6*SIZEOF_STRUCT_POINT) = 4 + [9] *((byte*)SCREEN+6*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 + [10] *((byte*)&main::p1) = 4 + [11] *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 + [12] *(SCREEN+8*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + to:main::@return +main::@return: scope:[main] from main + [13] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +struct Point main::p1 loadstore +byte main::p2_x +byte main::p2_x#0 2.0 +byte main::p2_y +byte main::p2_y#0 1.0 + +Initial phi equivalence classes +Added variable main::p2_x#0 to live range equivalence class [ main::p2_x#0 ] +Added variable main::p2_y#0 to live range equivalence class [ main::p2_y#0 ] +Added variable main::p1 to live range equivalence class [ main::p1 ] +Complete equivalence classes +[ main::p2_x#0 ] +[ main::p2_y#0 ] +[ main::p1 ] +Allocated zp[1]:2 [ main::p2_x#0 ] +Allocated zp[1]:3 [ main::p2_y#0 ] +Allocated zp[2]:4 [ main::p1 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) [ main::p1 ] ( [ main::p1 ] { } ) always clobbers reg byte a reg byte y +Statement [1] *SCREEN = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) [ main::p1 ] ( [ main::p1 ] { } ) always clobbers reg byte a reg byte y +Statement [6] *((byte*)&main::p1) = 3 [ main::p1 main::p2_y#0 ] ( [ main::p1 main::p2_y#0 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:3 [ main::p2_y#0 ] +Statement [7] *(SCREEN+4*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) [ main::p1 main::p2_y#0 ] ( [ main::p1 main::p2_y#0 ] { } ) always clobbers reg byte a reg byte y +Removing always clobbered register reg byte y as potential for zp[1]:3 [ main::p2_y#0 ] +Statement [8] *((byte*)SCREEN+6*SIZEOF_STRUCT_POINT) = 4 [ main::p1 main::p2_y#0 ] ( [ main::p1 main::p2_y#0 ] { } ) always clobbers reg byte a +Statement [10] *((byte*)&main::p1) = 4 [ main::p1 main::p2_y#0 ] ( [ main::p1 main::p2_y#0 ] { } ) always clobbers reg byte a +Statement [12] *(SCREEN+8*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) [ ] ( [ ] { } ) always clobbers reg byte a reg byte y +Statement [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) [ main::p1 ] ( [ main::p1 ] { } ) always clobbers reg byte a reg byte y +Statement [1] *SCREEN = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) [ main::p1 ] ( [ main::p1 ] { } ) always clobbers reg byte a reg byte y +Statement [6] *((byte*)&main::p1) = 3 [ main::p1 main::p2_y#0 ] ( [ main::p1 main::p2_y#0 ] { } ) always clobbers reg byte a +Statement [7] *(SCREEN+4*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) [ main::p1 main::p2_y#0 ] ( [ main::p1 main::p2_y#0 ] { } ) always clobbers reg byte a reg byte y +Statement [8] *((byte*)SCREEN+6*SIZEOF_STRUCT_POINT) = 4 [ main::p1 main::p2_y#0 ] ( [ main::p1 main::p2_y#0 ] { } ) always clobbers reg byte a +Statement [10] *((byte*)&main::p1) = 4 [ main::p1 main::p2_y#0 ] ( [ main::p1 main::p2_y#0 ] { } ) always clobbers reg byte a +Statement [12] *(SCREEN+8*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) [ ] ( [ ] { } ) always clobbers reg byte a reg byte y +Potential registers zp[1]:2 [ main::p2_x#0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ main::p2_y#0 ] : zp[1]:3 , reg byte x , +Potential registers zp[2]:4 [ main::p1 ] : zp[2]:4 , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 2: zp[1]:2 [ main::p2_x#0 ] 1: zp[1]:3 [ main::p2_y#0 ] 0: zp[2]:4 [ main::p1 ] +Uplift Scope [Point] +Uplift Scope [] + +Uplifting [main] best 112 combination reg byte a [ main::p2_x#0 ] reg byte x [ main::p2_y#0 ] zp[2]:4 [ main::p1 ] +Uplifting [Point] best 112 combination +Uplifting [] best 112 combination +Allocated (was zp[2]:4) zp[2]:2 [ main::p1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test combining unwind structs with classic structs + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + // main +main: { + .label p1 = 2 + // [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda __0-1,y + sta p1-1,y + dey + bne !- + // [1] *SCREEN = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta SCREEN-1,y + dey + bne !- + // [2] main::p2_x#0 = *((byte*)&main::p1) -- vbuaa=_deref_pbuc1 + lda.z p1 + // [3] main::p2_y#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) -- vbuxx=_deref_pbuc1 + ldx p1+OFFSET_STRUCT_POINT_Y + // [4] *((byte*)SCREEN+2*SIZEOF_STRUCT_POINT) = main::p2_x#0 -- _deref_pbuc1=vbuaa + sta SCREEN+2*SIZEOF_STRUCT_POINT + // [5] *((byte*)SCREEN+2*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 -- _deref_pbuc1=vbuxx + stx SCREEN+2*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y + // [6] *((byte*)&main::p1) = 3 -- _deref_pbuc1=vbuc2 + // Set in classic struct + lda #3 + sta.z p1 + // [7] *(SCREEN+4*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta SCREEN+4*SIZEOF_STRUCT_POINT-1,y + dey + bne !- + // [8] *((byte*)SCREEN+6*SIZEOF_STRUCT_POINT) = 4 -- _deref_pbuc1=vbuc2 + lda #4 + sta SCREEN+6*SIZEOF_STRUCT_POINT + // [9] *((byte*)SCREEN+6*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 -- _deref_pbuc1=vbuxx + stx SCREEN+6*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y + // [10] *((byte*)&main::p1) = 4 -- _deref_pbuc1=vbuc2 + lda #4 + sta.z p1 + // [11] *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 -- _deref_pbuc1=vbuxx + stx p1+OFFSET_STRUCT_POINT_Y + // [12] *(SCREEN+8*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta SCREEN+8*SIZEOF_STRUCT_POINT-1,y + dey + bne !- + jmp __breturn + // main::@return + __breturn: + // [13] return + rts +} + // File Data + __0: .byte 1, 2 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction lda #4 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +const struct Point $0 = { x: 1, y: 2 } +const byte OFFSET_STRUCT_POINT_Y = 1 +const nomodify struct Point* SCREEN = (struct Point*) 1024 +const byte SIZEOF_STRUCT_POINT = 2 +void main() +struct Point main::p1 loadstore zp[2]:2 +byte main::p2_x +byte main::p2_x#0 reg byte a 2.0 +byte main::p2_y +byte main::p2_y#0 reg byte x 1.0 + +reg byte a [ main::p2_x#0 ] +reg byte x [ main::p2_y#0 ] +zp[2]:2 [ main::p1 ] + + +FINAL ASSEMBLER +Score: 107 + + // File Comments +// Test combining unwind structs with classic structs + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + // main +main: { + .label p1 = 2 + // p1 = { 1, 2 } + // [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda __0-1,y + sta p1-1,y + dey + bne !- + // SCREEN[0] = p1 + // [1] *SCREEN = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta SCREEN-1,y + dey + bne !- + // p2 = p1 + // [2] main::p2_x#0 = *((byte*)&main::p1) -- vbuaa=_deref_pbuc1 + lda.z p1 + // [3] main::p2_y#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) -- vbuxx=_deref_pbuc1 + ldx p1+OFFSET_STRUCT_POINT_Y + // SCREEN[2] = p2 + // [4] *((byte*)SCREEN+2*SIZEOF_STRUCT_POINT) = main::p2_x#0 -- _deref_pbuc1=vbuaa + sta SCREEN+2*SIZEOF_STRUCT_POINT + // [5] *((byte*)SCREEN+2*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 -- _deref_pbuc1=vbuxx + stx SCREEN+2*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y + // p1.x = 3 + // [6] *((byte*)&main::p1) = 3 -- _deref_pbuc1=vbuc2 + // Set in classic struct + lda #3 + sta.z p1 + // SCREEN[4] = p1 + // [7] *(SCREEN+4*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta SCREEN+4*SIZEOF_STRUCT_POINT-1,y + dey + bne !- + // SCREEN[6] = p2 + // [8] *((byte*)SCREEN+6*SIZEOF_STRUCT_POINT) = 4 -- _deref_pbuc1=vbuc2 + lda #4 + sta SCREEN+6*SIZEOF_STRUCT_POINT + // [9] *((byte*)SCREEN+6*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 -- _deref_pbuc1=vbuxx + stx SCREEN+6*SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y + // p1 = p2 + // [10] *((byte*)&main::p1) = 4 -- _deref_pbuc1=vbuc2 + sta.z p1 + // [11] *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) = main::p2_y#0 -- _deref_pbuc1=vbuxx + stx p1+OFFSET_STRUCT_POINT_Y + // SCREEN[8] = p1 + // [12] *(SCREEN+8*SIZEOF_STRUCT_POINT) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta SCREEN+8*SIZEOF_STRUCT_POINT-1,y + dey + bne !- + // main::@return + // } + // [13] return + rts +} + // File Data + __0: .byte 1, 2 + diff --git a/src/test/ref/struct-unwinding-1.sym b/src/test/ref/struct-unwinding-1.sym new file mode 100644 index 000000000..12bf58a28 --- /dev/null +++ b/src/test/ref/struct-unwinding-1.sym @@ -0,0 +1,14 @@ +const struct Point $0 = { x: 1, y: 2 } +const byte OFFSET_STRUCT_POINT_Y = 1 +const nomodify struct Point* SCREEN = (struct Point*) 1024 +const byte SIZEOF_STRUCT_POINT = 2 +void main() +struct Point main::p1 loadstore zp[2]:2 +byte main::p2_x +byte main::p2_x#0 reg byte a 2.0 +byte main::p2_y +byte main::p2_y#0 reg byte x 1.0 + +reg byte a [ main::p2_x#0 ] +reg byte x [ main::p2_y#0 ] +zp[2]:2 [ main::p1 ] diff --git a/src/test/ref/struct-unwinding-2.asm b/src/test/ref/struct-unwinding-2.asm new file mode 100644 index 000000000..5564e58fd --- /dev/null +++ b/src/test/ref/struct-unwinding-2.asm @@ -0,0 +1,86 @@ +// Test combining unwind structs with classic structs +// Function calls parameter passing +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 +main: { + .const p2_x = 3 + .const p2_y = 4 + .label p1 = 3 + // p1 = { 1, 2 } + ldy #SIZEOF_STRUCT_POINT + !: + lda __0-1,y + sta p1-1,y + dey + bne !- + // print1(p1, 0) + ldy.z p1 + ldx p1+OFFSET_STRUCT_POINT_Y + // Pass classic struct to function taking unwound struct + lda #0 + jsr print1 + // print2(p1, 2) + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta print2.p-1,y + dey + bne !- + // Pass classic struct to function taking classic struct + lda #2 + jsr print2 + // print1(p2, 4) + // Pass unwound struct to function taking unwound struct + ldx #p2_y + ldy #p2_x + lda #4 + jsr print1 + // print2(p2, 6) + lda #p2_x + sta.z print2.p + lda #p2_y + sta print2.p+OFFSET_STRUCT_POINT_Y + // Pass unwound struct to function taking classic struct + lda #6 + jsr print2 + // } + rts +} +// Function taking unwound struct as parameter +// print1(byte register(Y) p_x, byte register(X) p_y, byte register(A) idx) +print1: { + .label __0 = 2 + // SCREEN[idx] = p + asl + sta.z __0 + tya + ldy.z __0 + sta SCREEN,y + txa + sta SCREEN+OFFSET_STRUCT_POINT_Y,y + // } + rts +} +// Function taking classic struct as parameter +// print2(struct Point zp(5) p, byte register(A) idx) +print2: { + .label p = 5 + // SCREEN[idx] = p + asl + tay + ldx #0 + !: + lda.z p,x + sta SCREEN,y + iny + inx + cpx #SIZEOF_STRUCT_POINT + bne !- + // } + rts +} + __0: .byte 1, 2 diff --git a/src/test/ref/struct-unwinding-2.cfg b/src/test/ref/struct-unwinding-2.cfg new file mode 100644 index 000000000..11ef9a384 --- /dev/null +++ b/src/test/ref/struct-unwinding-2.cfg @@ -0,0 +1,47 @@ + +void main() +main: scope:[main] from + [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) + [1] print1::p_x#0 = *((byte*)&main::p1) + [2] print1::p_y#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) + [3] call print1 + to:main::@1 +main::@1: scope:[main] from main + [4] *(&print2::p) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + [5] call print2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [6] phi() + [7] call print1 + to:main::@3 +main::@3: scope:[main] from main::@2 + [8] *((byte*)&print2::p) = main::p2_x + [9] *((byte*)&print2::p+OFFSET_STRUCT_POINT_Y) = main::p2_y + [10] call print2 + to:main::@return +main::@return: scope:[main] from main::@3 + [11] return + to:@return + +void print1(byte print1::p_x , byte print1::p_y , byte print1::idx) +print1: scope:[print1] from main main::@2 + [12] print1::p_y#2 = phi( main/print1::p_y#0, main::@2/main::p2_y ) + [12] print1::p_x#2 = phi( main/print1::p_x#0, main::@2/main::p2_x ) + [12] print1::idx#2 = phi( main/0, main::@2/4 ) + [13] print1::$0 = print1::idx#2 << 1 + [14] ((byte*)SCREEN)[print1::$0] = print1::p_x#2 + [15] ((byte*)SCREEN+OFFSET_STRUCT_POINT_Y)[print1::$0] = print1::p_y#2 + to:print1::@return +print1::@return: scope:[print1] from print1 + [16] return + to:@return + +void print2(struct Point print2::p , byte print2::idx) +print2: scope:[print2] from main::@1 main::@3 + [17] print2::idx#2 = phi( main::@1/2, main::@3/6 ) + [18] print2::$0 = print2::idx#2 << 1 + [19] SCREEN[print2::$0] = memcpy(*(&print2::p), struct Point, SIZEOF_STRUCT_POINT) + to:print2::@return +print2::@return: scope:[print2] from print2 + [20] return + to:@return diff --git a/src/test/ref/struct-unwinding-2.log b/src/test/ref/struct-unwinding-2.log new file mode 100644 index 000000000..160403c42 --- /dev/null +++ b/src/test/ref/struct-unwinding-2.log @@ -0,0 +1,660 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) + main::p1 = struct-unwound {*(&main::p1)} + print1::p_x#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_X) + print1::p_y#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) + print1::idx#0 = 0 + call print1 + to:main::@1 +main::@1: scope:[main] from main + *(&print2::p) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + print2::p = struct-unwound {*(&print2::p)} + print2::idx#0 = 2 + call print2 + to:main::@2 +main::@2: scope:[main] from main::@1 + main::p2#0 = struct-unwound {main::p2_x, main::p2_y} + print1::p_x#1 = main::p2_x + print1::p_y#1 = main::p2_y + print1::idx#1 = 4 + call print1 + to:main::@3 +main::@3: scope:[main] from main::@2 + *((byte*)&print2::p+OFFSET_STRUCT_POINT_X) = main::p2_x + *((byte*)&print2::p+OFFSET_STRUCT_POINT_Y) = main::p2_y + print2::p = struct-unwound {*((byte*)&print2::p+OFFSET_STRUCT_POINT_X), *((byte*)&print2::p+OFFSET_STRUCT_POINT_Y)} + print2::idx#1 = 6 + call print2 + to:main::@4 +main::@4: scope:[main] from main::@3 + to:main::@return +main::@return: scope:[main] from main::@4 + return + to:@return + +void print1(byte print1::p_x , byte print1::p_y , byte print1::idx) +print1: scope:[print1] from main main::@2 + print1::p_y#2 = phi( main/print1::p_y#0, main::@2/print1::p_y#1 ) + print1::p_x#2 = phi( main/print1::p_x#0, main::@2/print1::p_x#1 ) + print1::idx#2 = phi( main/print1::idx#0, main::@2/print1::idx#1 ) + print1::$0 = print1::idx#2 * SIZEOF_STRUCT_POINT + ((byte*)SCREEN+OFFSET_STRUCT_POINT_X)[print1::$0] = print1::p_x#2 + ((byte*)SCREEN+OFFSET_STRUCT_POINT_Y)[print1::$0] = print1::p_y#2 + to:print1::@return +print1::@return: scope:[print1] from print1 + return + to:@return + +void print2(struct Point print2::p , byte print2::idx) +print2: scope:[print2] from main::@1 main::@3 + print2::idx#2 = phi( main::@1/print2::idx#0, main::@3/print2::idx#1 ) + print2::$0 = print2::idx#2 * SIZEOF_STRUCT_POINT + SCREEN[print2::$0] = memcpy(*(&print2::p), struct Point, SIZEOF_STRUCT_POINT) + to:print2::@return +print2::@return: scope:[print2] from print2 + 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 +const struct Point $0 = { x: 1, y: 2 } +const byte OFFSET_STRUCT_POINT_X = 0 +const byte OFFSET_STRUCT_POINT_Y = 1 +const nomodify struct Point* SCREEN = (struct Point*)$400 +const byte SIZEOF_STRUCT_POINT = 2 +void __start() +void main() +struct Point main::p1 loadstore +struct Point main::p2 +struct Point main::p2#0 +const byte main::p2_x = 3 +const byte main::p2_y = 4 +void print1(byte print1::p_x , byte print1::p_y , byte print1::idx) +byte~ print1::$0 +byte print1::idx +byte print1::idx#0 +byte print1::idx#1 +byte print1::idx#2 +struct Point print1::p +byte print1::p_x +byte print1::p_x#0 +byte print1::p_x#1 +byte print1::p_x#2 +byte print1::p_y +byte print1::p_y#0 +byte print1::p_y#1 +byte print1::p_y#2 +void print2(struct Point print2::p , byte print2::idx) +byte~ print2::$0 +byte print2::idx +byte print2::idx#0 +byte print2::idx#1 +byte print2::idx#2 +struct Point print2::p loadstore + +Adding number conversion cast (unumber) 0 in print1::idx#0 = 0 +Adding number conversion cast (unumber) 2 in print2::idx#0 = 2 +Adding number conversion cast (unumber) 4 in print1::idx#1 = 4 +Adding number conversion cast (unumber) 6 in print2::idx#1 = 6 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast print1::idx#0 = (unumber)0 +Inlining cast print2::idx#0 = (unumber)2 +Inlining cast print1::idx#1 = (unumber)4 +Inlining cast print2::idx#1 = (unumber)6 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (struct Point*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 2 +Simplifying constant integer cast 4 +Simplifying constant integer cast 6 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type 0 +Finalized unsigned number type 2 +Finalized unsigned number type 4 +Finalized unsigned number type 6 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Removing C-classic struct-unwound assignment [1] main::p1 = struct-unwound {*(&main::p1)} +Removing C-classic struct-unwound assignment [7] print2::p = struct-unwound {*(&print2::p)} +Removing C-classic struct-unwound assignment [17] print2::p = struct-unwound {*((byte*)&print2::p+OFFSET_STRUCT_POINT_X), *((byte*)&print2::p+OFFSET_STRUCT_POINT_Y)} +Constant print1::idx#0 = 0 +Constant print2::idx#0 = 2 +Constant print1::p_x#1 = main::p2_x +Constant print1::p_y#1 = main::p2_y +Constant print1::idx#1 = 4 +Constant print2::idx#1 = 6 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero (byte*)&main::p1 in [2] print1::p_x#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_X) +Simplifying expression containing zero (byte*)&print2::p in [15] *((byte*)&print2::p+OFFSET_STRUCT_POINT_X) = main::p2_x +Simplifying expression containing zero (byte*)SCREEN in [23] ((byte*)SCREEN+OFFSET_STRUCT_POINT_X)[print1::$0] = print1::p_x#2 +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable main::p2#0 and assignment [6] main::p2#0 = struct-unwound {main::p2_x, main::p2_y} +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::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Rewriting multiplication to use shift [12] print1::$0 = print1::idx#2 * SIZEOF_STRUCT_POINT +Rewriting multiplication to use shift [17] print2::$0 = print2::idx#2 * SIZEOF_STRUCT_POINT +Successful SSA optimization Pass2MultiplyToShiftRewriting +Inlining constant with var siblings print1::idx#0 +Inlining constant with var siblings print1::p_x#1 +Inlining constant with var siblings print1::p_y#1 +Inlining constant with var siblings print1::idx#1 +Inlining constant with var siblings print2::idx#0 +Inlining constant with var siblings print2::idx#1 +Constant inlined print1::idx#1 = 4 +Constant inlined print1::idx#0 = 0 +Constant inlined print1::p_x#1 = main::p2_x +Constant inlined print1::p_y#1 = main::p2_y +Constant inlined print2::idx#0 = 2 +Constant inlined print2::idx#1 = 6 +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of main::@2 +Adding NOP phi() at start of main::@4 +CALL GRAPH +Calls in [main] to print1:5 print2:7 print1:9 print2:12 + +Created 4 initial phi equivalence classes +Coalesced [3] print1::p_x#3 = print1::p_x#0 +Coalesced [4] print1::p_y#3 = print1::p_y#0 +Coalesced down to 4 phi equivalence classes +Culled Empty Block label main::@4 +Adding NOP phi() at start of main::@2 + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) + [1] print1::p_x#0 = *((byte*)&main::p1) + [2] print1::p_y#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) + [3] call print1 + to:main::@1 +main::@1: scope:[main] from main + [4] *(&print2::p) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) + [5] call print2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [6] phi() + [7] call print1 + to:main::@3 +main::@3: scope:[main] from main::@2 + [8] *((byte*)&print2::p) = main::p2_x + [9] *((byte*)&print2::p+OFFSET_STRUCT_POINT_Y) = main::p2_y + [10] call print2 + to:main::@return +main::@return: scope:[main] from main::@3 + [11] return + to:@return + +void print1(byte print1::p_x , byte print1::p_y , byte print1::idx) +print1: scope:[print1] from main main::@2 + [12] print1::p_y#2 = phi( main/print1::p_y#0, main::@2/main::p2_y ) + [12] print1::p_x#2 = phi( main/print1::p_x#0, main::@2/main::p2_x ) + [12] print1::idx#2 = phi( main/0, main::@2/4 ) + [13] print1::$0 = print1::idx#2 << 1 + [14] ((byte*)SCREEN)[print1::$0] = print1::p_x#2 + [15] ((byte*)SCREEN+OFFSET_STRUCT_POINT_Y)[print1::$0] = print1::p_y#2 + to:print1::@return +print1::@return: scope:[print1] from print1 + [16] return + to:@return + +void print2(struct Point print2::p , byte print2::idx) +print2: scope:[print2] from main::@1 main::@3 + [17] print2::idx#2 = phi( main::@1/2, main::@3/6 ) + [18] print2::$0 = print2::idx#2 << 1 + [19] SCREEN[print2::$0] = memcpy(*(&print2::p), struct Point, SIZEOF_STRUCT_POINT) + to:print2::@return +print2::@return: scope:[print2] from print2 + [20] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +struct Point main::p1 loadstore +struct Point main::p2 +void print1(byte print1::p_x , byte print1::p_y , byte print1::idx) +byte~ print1::$0 16.5 +byte print1::idx +byte print1::idx#2 11.0 +struct Point print1::p +byte print1::p_x +byte print1::p_x#0 2.0 +byte print1::p_x#2 6.5 +byte print1::p_y +byte print1::p_y#0 4.0 +byte print1::p_y#2 4.333333333333333 +void print2(struct Point print2::p , byte print2::idx) +byte~ print2::$0 22.0 +byte print2::idx +byte print2::idx#2 11.0 +struct Point print2::p loadstore + +Initial phi equivalence classes +[ print1::idx#2 ] +[ print1::p_x#2 print1::p_x#0 ] +[ print1::p_y#2 print1::p_y#0 ] +[ print2::idx#2 ] +Added variable print1::$0 to live range equivalence class [ print1::$0 ] +Added variable print2::$0 to live range equivalence class [ print2::$0 ] +Added variable main::p1 to live range equivalence class [ main::p1 ] +Added variable print2::p to live range equivalence class [ print2::p ] +Complete equivalence classes +[ print1::idx#2 ] +[ print1::p_x#2 print1::p_x#0 ] +[ print1::p_y#2 print1::p_y#0 ] +[ print2::idx#2 ] +[ print1::$0 ] +[ print2::$0 ] +[ main::p1 ] +[ print2::p ] +Allocated zp[1]:2 [ print1::idx#2 ] +Allocated zp[1]:3 [ print1::p_x#2 print1::p_x#0 ] +Allocated zp[1]:4 [ print1::p_y#2 print1::p_y#0 ] +Allocated zp[1]:5 [ print2::idx#2 ] +Allocated zp[1]:6 [ print1::$0 ] +Allocated zp[1]:7 [ print2::$0 ] +Allocated zp[2]:8 [ main::p1 ] +Allocated zp[2]:10 [ print2::p ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) [ main::p1 print2::p ] ( [ main::p1 print2::p ] { { print1::p_x#0 = print1::p_x#2 } { print1::p_y#0 = print1::p_y#2 } } ) always clobbers reg byte a reg byte y +Statement [4] *(&print2::p) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) [ print2::p ] ( [ print2::p ] { } ) always clobbers reg byte a reg byte y +Statement [8] *((byte*)&print2::p) = main::p2_x [ print2::p ] ( [ print2::p ] { } ) always clobbers reg byte a +Statement [9] *((byte*)&print2::p+OFFSET_STRUCT_POINT_Y) = main::p2_y [ print2::p ] ( [ print2::p ] { } ) always clobbers reg byte a +Statement [13] print1::$0 = print1::idx#2 << 1 [ print1::p_x#2 print1::p_y#2 print1::$0 ] ( print1:3 [ main::p1 print2::p print1::p_x#2 print1::p_y#2 print1::$0 ] { { print1::p_x#0 = print1::p_x#2 } { print1::p_y#0 = print1::p_y#2 } } print1:7 [ print2::p print1::p_x#2 print1::p_y#2 print1::$0 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:3 [ print1::p_x#2 print1::p_x#0 ] +Removing always clobbered register reg byte a as potential for zp[1]:4 [ print1::p_y#2 print1::p_y#0 ] +Statement [14] ((byte*)SCREEN)[print1::$0] = print1::p_x#2 [ print1::p_y#2 print1::$0 ] ( print1:3 [ main::p1 print2::p print1::p_y#2 print1::$0 ] { { print1::p_x#0 = print1::p_x#2 } { print1::p_y#0 = print1::p_y#2 } } print1:7 [ print2::p print1::p_y#2 print1::$0 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:6 [ print1::$0 ] +Statement [15] ((byte*)SCREEN+OFFSET_STRUCT_POINT_Y)[print1::$0] = print1::p_y#2 [ ] ( print1:3 [ main::p1 print2::p ] { { print1::p_x#0 = print1::p_x#2 } { print1::p_y#0 = print1::p_y#2 } } print1:7 [ print2::p ] { } ) always clobbers reg byte a +Statement [18] print2::$0 = print2::idx#2 << 1 [ print2::p print2::$0 ] ( print2:5 [ print2::p print2::$0 ] { } print2:10 [ print2::p print2::$0 ] { } ) always clobbers reg byte a +Statement [19] SCREEN[print2::$0] = memcpy(*(&print2::p), struct Point, SIZEOF_STRUCT_POINT) [ print2::p ] ( print2:5 [ print2::p ] { } print2:10 [ print2::p ] { } ) always clobbers reg byte a reg byte x reg byte y +Statement [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) [ main::p1 print2::p ] ( [ main::p1 print2::p ] { { print1::p_x#0 = print1::p_x#2 } { print1::p_y#0 = print1::p_y#2 } } ) always clobbers reg byte a reg byte y +Statement [4] *(&print2::p) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) [ print2::p ] ( [ print2::p ] { } ) always clobbers reg byte a reg byte y +Statement [8] *((byte*)&print2::p) = main::p2_x [ print2::p ] ( [ print2::p ] { } ) always clobbers reg byte a +Statement [9] *((byte*)&print2::p+OFFSET_STRUCT_POINT_Y) = main::p2_y [ print2::p ] ( [ print2::p ] { } ) always clobbers reg byte a +Statement [13] print1::$0 = print1::idx#2 << 1 [ print1::p_x#2 print1::p_y#2 print1::$0 ] ( print1:3 [ main::p1 print2::p print1::p_x#2 print1::p_y#2 print1::$0 ] { { print1::p_x#0 = print1::p_x#2 } { print1::p_y#0 = print1::p_y#2 } } print1:7 [ print2::p print1::p_x#2 print1::p_y#2 print1::$0 ] { } ) always clobbers reg byte a +Statement [14] ((byte*)SCREEN)[print1::$0] = print1::p_x#2 [ print1::p_y#2 print1::$0 ] ( print1:3 [ main::p1 print2::p print1::p_y#2 print1::$0 ] { { print1::p_x#0 = print1::p_x#2 } { print1::p_y#0 = print1::p_y#2 } } print1:7 [ print2::p print1::p_y#2 print1::$0 ] { } ) always clobbers reg byte a +Statement [15] ((byte*)SCREEN+OFFSET_STRUCT_POINT_Y)[print1::$0] = print1::p_y#2 [ ] ( print1:3 [ main::p1 print2::p ] { { print1::p_x#0 = print1::p_x#2 } { print1::p_y#0 = print1::p_y#2 } } print1:7 [ print2::p ] { } ) always clobbers reg byte a +Statement [18] print2::$0 = print2::idx#2 << 1 [ print2::p print2::$0 ] ( print2:5 [ print2::p print2::$0 ] { } print2:10 [ print2::p print2::$0 ] { } ) always clobbers reg byte a +Statement [19] SCREEN[print2::$0] = memcpy(*(&print2::p), struct Point, SIZEOF_STRUCT_POINT) [ print2::p ] ( print2:5 [ print2::p ] { } print2:10 [ print2::p ] { } ) always clobbers reg byte a reg byte x reg byte y +Potential registers zp[1]:2 [ print1::idx#2 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ print1::p_x#2 print1::p_x#0 ] : zp[1]:3 , reg byte x , reg byte y , +Potential registers zp[1]:4 [ print1::p_y#2 print1::p_y#0 ] : zp[1]:4 , reg byte x , reg byte y , +Potential registers zp[1]:5 [ print2::idx#2 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:6 [ print1::$0 ] : zp[1]:6 , reg byte x , reg byte y , +Potential registers zp[1]:7 [ print2::$0 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[2]:8 [ main::p1 ] : zp[2]:8 , +Potential registers zp[2]:10 [ print2::p ] : zp[2]:10 , + +REGISTER UPLIFT SCOPES +Uplift Scope [print1] 16.5: zp[1]:6 [ print1::$0 ] 11: zp[1]:2 [ print1::idx#2 ] 8.5: zp[1]:3 [ print1::p_x#2 print1::p_x#0 ] 8.33: zp[1]:4 [ print1::p_y#2 print1::p_y#0 ] +Uplift Scope [print2] 22: zp[1]:7 [ print2::$0 ] 11: zp[1]:5 [ print2::idx#2 ] 0: zp[2]:10 [ print2::p ] +Uplift Scope [Point] +Uplift Scope [main] 0: zp[2]:8 [ main::p1 ] +Uplift Scope [] + +Uplifting [print1] best 184 combination zp[1]:6 [ print1::$0 ] reg byte a [ print1::idx#2 ] reg byte y [ print1::p_x#2 print1::p_x#0 ] reg byte x [ print1::p_y#2 print1::p_y#0 ] +Limited combination testing to 100 combinations of 108 possible. +Uplifting [print2] best 170 combination reg byte y [ print2::$0 ] reg byte a [ print2::idx#2 ] zp[2]:10 [ print2::p ] +Uplifting [Point] best 170 combination +Uplifting [main] best 170 combination zp[2]:8 [ main::p1 ] +Uplifting [] best 170 combination +Attempting to uplift remaining variables inzp[1]:6 [ print1::$0 ] +Uplifting [print1] best 170 combination zp[1]:6 [ print1::$0 ] +Allocated (was zp[1]:6) zp[1]:2 [ print1::$0 ] +Allocated (was zp[2]:8) zp[2]:3 [ main::p1 ] +Allocated (was zp[2]:10) zp[2]:5 [ print2::p ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test combining unwind structs with classic structs +// Function calls parameter passing + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + // main +main: { + .const p2_x = 3 + .const p2_y = 4 + .label p1 = 3 + // [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda __0-1,y + sta p1-1,y + dey + bne !- + // [1] print1::p_x#0 = *((byte*)&main::p1) -- vbuyy=_deref_pbuc1 + ldy.z p1 + // [2] print1::p_y#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) -- vbuxx=_deref_pbuc1 + ldx p1+OFFSET_STRUCT_POINT_Y + // [3] call print1 + // Pass classic struct to function taking unwound struct + // [12] phi from main to print1 [phi:main->print1] + print1_from_main: + // [12] phi print1::p_y#2 = print1::p_y#0 [phi:main->print1#0] -- register_copy + // [12] phi print1::p_x#2 = print1::p_x#0 [phi:main->print1#1] -- register_copy + // [12] phi print1::idx#2 = 0 [phi:main->print1#2] -- vbuaa=vbuc1 + lda #0 + jsr print1 + jmp __b1 + // main::@1 + __b1: + // [4] *(&print2::p) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta print2.p-1,y + dey + bne !- + // [5] call print2 + // Pass classic struct to function taking classic struct + // [17] phi from main::@1 to print2 [phi:main::@1->print2] + print2_from___b1: + // [17] phi print2::idx#2 = 2 [phi:main::@1->print2#0] -- vbuaa=vbuc1 + lda #2 + jsr print2 + // [6] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + __b2_from___b1: + jmp __b2 + // main::@2 + __b2: + // [7] call print1 + // Pass unwound struct to function taking unwound struct + // [12] phi from main::@2 to print1 [phi:main::@2->print1] + print1_from___b2: + // [12] phi print1::p_y#2 = main::p2_y [phi:main::@2->print1#0] -- vbuxx=vbuc1 + ldx #p2_y + // [12] phi print1::p_x#2 = main::p2_x [phi:main::@2->print1#1] -- vbuyy=vbuc1 + ldy #p2_x + // [12] phi print1::idx#2 = 4 [phi:main::@2->print1#2] -- vbuaa=vbuc1 + lda #4 + jsr print1 + jmp __b3 + // main::@3 + __b3: + // [8] *((byte*)&print2::p) = main::p2_x -- _deref_pbuc1=vbuc2 + lda #p2_x + sta.z print2.p + // [9] *((byte*)&print2::p+OFFSET_STRUCT_POINT_Y) = main::p2_y -- _deref_pbuc1=vbuc2 + lda #p2_y + sta print2.p+OFFSET_STRUCT_POINT_Y + // [10] call print2 + // Pass unwound struct to function taking classic struct + // [17] phi from main::@3 to print2 [phi:main::@3->print2] + print2_from___b3: + // [17] phi print2::idx#2 = 6 [phi:main::@3->print2#0] -- vbuaa=vbuc1 + lda #6 + jsr print2 + jmp __breturn + // main::@return + __breturn: + // [11] return + rts +} + // print1 +// Function taking unwound struct as parameter +// print1(byte register(Y) p_x, byte register(X) p_y, byte register(A) idx) +print1: { + .label __0 = 2 + // [13] print1::$0 = print1::idx#2 << 1 -- vbuz1=vbuaa_rol_1 + asl + sta.z __0 + // [14] ((byte*)SCREEN)[print1::$0] = print1::p_x#2 -- pbuc1_derefidx_vbuz1=vbuyy + tya + ldy.z __0 + sta SCREEN,y + // [15] ((byte*)SCREEN+OFFSET_STRUCT_POINT_Y)[print1::$0] = print1::p_y#2 -- pbuc1_derefidx_vbuz1=vbuxx + ldy.z __0 + txa + sta SCREEN+OFFSET_STRUCT_POINT_Y,y + jmp __breturn + // print1::@return + __breturn: + // [16] return + rts +} + // print2 +// Function taking classic struct as parameter +// print2(struct Point zp(5) p, byte register(A) idx) +print2: { + .label p = 5 + // [18] print2::$0 = print2::idx#2 << 1 -- vbuyy=vbuaa_rol_1 + asl + tay + // [19] SCREEN[print2::$0] = memcpy(*(&print2::p), struct Point, SIZEOF_STRUCT_POINT) -- pssc1_derefidx_vbuyy=_deref_pssc2_memcpy_vbuc3 + ldx #0 + !: + lda.z p,x + sta SCREEN,y + iny + inx + cpx #SIZEOF_STRUCT_POINT + bne !- + jmp __breturn + // print2::@return + __breturn: + // [20] return + rts +} + // File Data + __0: .byte 1, 2 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __b3 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction ldy.z __0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Removing instruction __b2_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction print1_from_main: +Removing instruction __b1: +Removing instruction print2_from___b1: +Removing instruction __b2: +Removing instruction print1_from___b2: +Removing instruction __b3: +Removing instruction print2_from___b3: +Removing instruction __breturn: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +const struct Point $0 = { x: 1, y: 2 } +const byte OFFSET_STRUCT_POINT_Y = 1 +const nomodify struct Point* SCREEN = (struct Point*) 1024 +const byte SIZEOF_STRUCT_POINT = 2 +void main() +struct Point main::p1 loadstore zp[2]:3 +struct Point main::p2 +const byte main::p2_x = 3 +const byte main::p2_y = 4 +void print1(byte print1::p_x , byte print1::p_y , byte print1::idx) +byte~ print1::$0 zp[1]:2 16.5 +byte print1::idx +byte print1::idx#2 reg byte a 11.0 +struct Point print1::p +byte print1::p_x +byte print1::p_x#0 reg byte y 2.0 +byte print1::p_x#2 reg byte y 6.5 +byte print1::p_y +byte print1::p_y#0 reg byte x 4.0 +byte print1::p_y#2 reg byte x 4.333333333333333 +void print2(struct Point print2::p , byte print2::idx) +byte~ print2::$0 reg byte y 22.0 +byte print2::idx +byte print2::idx#2 reg byte a 11.0 +struct Point print2::p loadstore zp[2]:5 + +reg byte a [ print1::idx#2 ] +reg byte y [ print1::p_x#2 print1::p_x#0 ] +reg byte x [ print1::p_y#2 print1::p_y#0 ] +reg byte a [ print2::idx#2 ] +zp[1]:2 [ print1::$0 ] +reg byte y [ print2::$0 ] +zp[2]:3 [ main::p1 ] +zp[2]:5 [ print2::p ] + + +FINAL ASSEMBLER +Score: 149 + + // File Comments +// Test combining unwind structs with classic structs +// Function calls parameter passing + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + // main +main: { + .const p2_x = 3 + .const p2_y = 4 + .label p1 = 3 + // p1 = { 1, 2 } + // [0] *(&main::p1) = memcpy(*(&$0), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda __0-1,y + sta p1-1,y + dey + bne !- + // print1(p1, 0) + // [1] print1::p_x#0 = *((byte*)&main::p1) -- vbuyy=_deref_pbuc1 + ldy.z p1 + // [2] print1::p_y#0 = *((byte*)&main::p1+OFFSET_STRUCT_POINT_Y) -- vbuxx=_deref_pbuc1 + ldx p1+OFFSET_STRUCT_POINT_Y + // [3] call print1 + // Pass classic struct to function taking unwound struct + // [12] phi from main to print1 [phi:main->print1] + // [12] phi print1::p_y#2 = print1::p_y#0 [phi:main->print1#0] -- register_copy + // [12] phi print1::p_x#2 = print1::p_x#0 [phi:main->print1#1] -- register_copy + // [12] phi print1::idx#2 = 0 [phi:main->print1#2] -- vbuaa=vbuc1 + lda #0 + jsr print1 + // main::@1 + // print2(p1, 2) + // [4] *(&print2::p) = memcpy(*(&main::p1), struct Point, SIZEOF_STRUCT_POINT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_POINT + !: + lda p1-1,y + sta print2.p-1,y + dey + bne !- + // [5] call print2 + // Pass classic struct to function taking classic struct + // [17] phi from main::@1 to print2 [phi:main::@1->print2] + // [17] phi print2::idx#2 = 2 [phi:main::@1->print2#0] -- vbuaa=vbuc1 + lda #2 + jsr print2 + // [6] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + // main::@2 + // print1(p2, 4) + // [7] call print1 + // Pass unwound struct to function taking unwound struct + // [12] phi from main::@2 to print1 [phi:main::@2->print1] + // [12] phi print1::p_y#2 = main::p2_y [phi:main::@2->print1#0] -- vbuxx=vbuc1 + ldx #p2_y + // [12] phi print1::p_x#2 = main::p2_x [phi:main::@2->print1#1] -- vbuyy=vbuc1 + ldy #p2_x + // [12] phi print1::idx#2 = 4 [phi:main::@2->print1#2] -- vbuaa=vbuc1 + lda #4 + jsr print1 + // main::@3 + // print2(p2, 6) + // [8] *((byte*)&print2::p) = main::p2_x -- _deref_pbuc1=vbuc2 + lda #p2_x + sta.z print2.p + // [9] *((byte*)&print2::p+OFFSET_STRUCT_POINT_Y) = main::p2_y -- _deref_pbuc1=vbuc2 + lda #p2_y + sta print2.p+OFFSET_STRUCT_POINT_Y + // [10] call print2 + // Pass unwound struct to function taking classic struct + // [17] phi from main::@3 to print2 [phi:main::@3->print2] + // [17] phi print2::idx#2 = 6 [phi:main::@3->print2#0] -- vbuaa=vbuc1 + lda #6 + jsr print2 + // main::@return + // } + // [11] return + rts +} + // print1 +// Function taking unwound struct as parameter +// print1(byte register(Y) p_x, byte register(X) p_y, byte register(A) idx) +print1: { + .label __0 = 2 + // SCREEN[idx] = p + // [13] print1::$0 = print1::idx#2 << 1 -- vbuz1=vbuaa_rol_1 + asl + sta.z __0 + // [14] ((byte*)SCREEN)[print1::$0] = print1::p_x#2 -- pbuc1_derefidx_vbuz1=vbuyy + tya + ldy.z __0 + sta SCREEN,y + // [15] ((byte*)SCREEN+OFFSET_STRUCT_POINT_Y)[print1::$0] = print1::p_y#2 -- pbuc1_derefidx_vbuz1=vbuxx + txa + sta SCREEN+OFFSET_STRUCT_POINT_Y,y + // print1::@return + // } + // [16] return + rts +} + // print2 +// Function taking classic struct as parameter +// print2(struct Point zp(5) p, byte register(A) idx) +print2: { + .label p = 5 + // SCREEN[idx] = p + // [18] print2::$0 = print2::idx#2 << 1 -- vbuyy=vbuaa_rol_1 + asl + tay + // [19] SCREEN[print2::$0] = memcpy(*(&print2::p), struct Point, SIZEOF_STRUCT_POINT) -- pssc1_derefidx_vbuyy=_deref_pssc2_memcpy_vbuc3 + ldx #0 + !: + lda.z p,x + sta SCREEN,y + iny + inx + cpx #SIZEOF_STRUCT_POINT + bne !- + // print2::@return + // } + // [20] return + rts +} + // File Data + __0: .byte 1, 2 + diff --git a/src/test/ref/struct-unwinding-2.sym b/src/test/ref/struct-unwinding-2.sym new file mode 100644 index 000000000..5e38206e6 --- /dev/null +++ b/src/test/ref/struct-unwinding-2.sym @@ -0,0 +1,34 @@ +const struct Point $0 = { x: 1, y: 2 } +const byte OFFSET_STRUCT_POINT_Y = 1 +const nomodify struct Point* SCREEN = (struct Point*) 1024 +const byte SIZEOF_STRUCT_POINT = 2 +void main() +struct Point main::p1 loadstore zp[2]:3 +struct Point main::p2 +const byte main::p2_x = 3 +const byte main::p2_y = 4 +void print1(byte print1::p_x , byte print1::p_y , byte print1::idx) +byte~ print1::$0 zp[1]:2 16.5 +byte print1::idx +byte print1::idx#2 reg byte a 11.0 +struct Point print1::p +byte print1::p_x +byte print1::p_x#0 reg byte y 2.0 +byte print1::p_x#2 reg byte y 6.5 +byte print1::p_y +byte print1::p_y#0 reg byte x 4.0 +byte print1::p_y#2 reg byte x 4.333333333333333 +void print2(struct Point print2::p , byte print2::idx) +byte~ print2::$0 reg byte y 22.0 +byte print2::idx +byte print2::idx#2 reg byte a 11.0 +struct Point print2::p loadstore zp[2]:5 + +reg byte a [ print1::idx#2 ] +reg byte y [ print1::p_x#2 print1::p_x#0 ] +reg byte x [ print1::p_y#2 print1::p_y#0 ] +reg byte a [ print2::idx#2 ] +zp[1]:2 [ print1::$0 ] +reg byte y [ print2::$0 ] +zp[2]:3 [ main::p1 ] +zp[2]:5 [ print2::p ]