From f336907efde98647a1cabe60f85a940b352a41b7 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Fri, 26 Jul 2019 14:31:37 +0200 Subject: [PATCH] Added test refs. --- .../problem-struct-return-pointer-deref.asm | 58 + .../problem-struct-return-pointer-deref.cfg | 64 + .../problem-struct-return-pointer-deref.log | 1178 +++++++++++++++++ .../problem-struct-return-pointer-deref.sym | 67 + .../ref/problem-struct-return-to-pointer.asm | 35 + .../ref/problem-struct-return-to-pointer.cfg | 42 + .../ref/problem-struct-return-to-pointer.log | 800 +++++++++++ .../ref/problem-struct-return-to-pointer.sym | 42 + 8 files changed, 2286 insertions(+) create mode 100644 src/test/ref/problem-struct-return-pointer-deref.asm create mode 100644 src/test/ref/problem-struct-return-pointer-deref.cfg create mode 100644 src/test/ref/problem-struct-return-pointer-deref.log create mode 100644 src/test/ref/problem-struct-return-pointer-deref.sym create mode 100644 src/test/ref/problem-struct-return-to-pointer.asm create mode 100644 src/test/ref/problem-struct-return-to-pointer.cfg create mode 100644 src/test/ref/problem-struct-return-to-pointer.log create mode 100644 src/test/ref/problem-struct-return-to-pointer.sym diff --git a/src/test/ref/problem-struct-return-pointer-deref.asm b/src/test/ref/problem-struct-return-pointer-deref.asm new file mode 100644 index 000000000..dc3b95387 --- /dev/null +++ b/src/test/ref/problem-struct-return-pointer-deref.asm @@ -0,0 +1,58 @@ +// Demonstrates problem with returning a dereferenced pointer to a struct +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + .label p0 = $a000 + .label p1 = $b000 + .label p2 = $e000 +main: { + .label _1_x = 3 + .label _1_y = 4 + lda #0 + jsr get + lda get.return_y + stx SCREEN + sta SCREEN+OFFSET_STRUCT_POINT_Y + ldy #1 + b1: + tya + jsr get + lda get.return_y + stx _1_x + sta _1_y + tya + asl + tax + lda _1_x + sta SCREEN,x + lda _1_y + sta SCREEN+OFFSET_STRUCT_POINT_Y,x + iny + cpy #3 + bne b1 + rts +} +// get(byte register(A) i) +get: { + .label return_y = 2 + cmp #0 + beq b1 + cmp #1 + beq b2 + ldx p2 + lda p2+OFFSET_STRUCT_POINT_Y + sta return_y + rts + b2: + ldx p1 + lda p1+OFFSET_STRUCT_POINT_Y + sta return_y + rts + b1: + ldx p0 + lda p0+OFFSET_STRUCT_POINT_Y + sta return_y + rts +} diff --git a/src/test/ref/problem-struct-return-pointer-deref.cfg b/src/test/ref/problem-struct-return-pointer-deref.cfg new file mode 100644 index 000000000..cd2e23f56 --- /dev/null +++ b/src/test/ref/problem-struct-return-pointer-deref.cfg @@ -0,0 +1,64 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + [5] call get + [6] (byte) get::return_x#0 ← (byte) get::return_x#5 + [7] (byte) get::return_y#0 ← (byte) get::return_y#5 + to:main::@2 +main::@2: scope:[main] from main + [8] (byte) main::$0_x ← (byte) get::return_x#0 + [9] (byte) main::$0_y ← (byte) get::return_y#0 + [10] *((byte*)(const struct Point*) SCREEN#0) ← (byte) main::$0_x + [11] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) main::$0_y + to:main::@1 +main::@1: scope:[main] from main::@2 main::@3 + [12] (byte) main::i#2 ← phi( main::@2/(byte) 1 main::@3/(byte) main::i#1 ) + [13] (byte) get::i#1 ← (byte) main::i#2 + [14] call get + [15] (byte) get::return_x#1 ← (byte) get::return_x#5 + [16] (byte) get::return_y#1 ← (byte) get::return_y#5 + to:main::@3 +main::@3: scope:[main] from main::@1 + [17] (byte) main::$1_x ← (byte) get::return_x#1 + [18] (byte) main::$1_y ← (byte) get::return_y#1 + [19] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 + [20] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x + [21] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (byte) main::$1_y + [22] (byte) main::i#1 ← ++ (byte) main::i#2 + [23] if((byte) main::i#1!=(byte) 3) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@3 + [24] return + to:@return +get: scope:[get] from main main::@1 + [25] (byte) get::i#2 ← phi( main/(byte) 0 main::@1/(byte) get::i#1 ) + [26] if((byte) get::i#2==(byte) 0) goto get::@1 + to:get::@3 +get::@3: scope:[get] from get + [27] if((byte) get::i#2==(byte) 1) goto get::@2 + to:get::@4 +get::@4: scope:[get] from get::@3 + [28] (byte) get::return_x#4 ← *((byte*)(const struct Point*) p2#0) + [29] (byte) get::return_y#4 ← *((byte*)(const struct Point*) p2#0+(const byte) OFFSET_STRUCT_POINT_Y) + to:get::@return +get::@return: scope:[get] from get::@1 get::@2 get::@4 + [30] (byte) get::return_y#5 ← phi( get::@1/(byte) get::return_y#2 get::@2/(byte) get::return_y#3 get::@4/(byte) get::return_y#4 ) + [30] (byte) get::return_x#5 ← phi( get::@1/(byte) get::return_x#2 get::@2/(byte) get::return_x#3 get::@4/(byte) get::return_x#4 ) + [31] return + to:@return +get::@2: scope:[get] from get::@3 + [32] (byte) get::return_x#3 ← *((byte*)(const struct Point*) p1#0) + [33] (byte) get::return_y#3 ← *((byte*)(const struct Point*) p1#0+(const byte) OFFSET_STRUCT_POINT_Y) + to:get::@return +get::@1: scope:[get] from get + [34] (byte) get::return_x#2 ← *((byte*)(const struct Point*) p0#0) + [35] (byte) get::return_y#2 ← *((byte*)(const struct Point*) p0#0+(const byte) OFFSET_STRUCT_POINT_Y) + to:get::@return diff --git a/src/test/ref/problem-struct-return-pointer-deref.log b/src/test/ref/problem-struct-return-pointer-deref.log new file mode 100644 index 000000000..c365a2d8e --- /dev/null +++ b/src/test/ref/problem-struct-return-pointer-deref.log @@ -0,0 +1,1178 @@ +Fixing pointer array-indexing *((struct Point*) SCREEN + (byte) main::i) +Created struct value member variable (byte) main::$0_x +Created struct value member variable (byte) main::$0_y +Converted struct value to member variables (struct Point~) main::$0 +Created struct value member variable (byte) main::$1_x +Created struct value member variable (byte) main::$1_y +Converted struct value to member variables (struct Point~) main::$1 +Created struct value member variable (byte) get::return_x +Created struct value member variable (byte) get::return_y +Converted struct value to member variables (struct Point) get::return +Converted procedure call LValue to member unwinding { (byte) main::$0_x, (byte) main::$0_y } ← call get (number) 0 +Adding struct value member variable copy *((byte*) main::$4) ← (byte) main::$0_x +Adding struct value member variable copy *((byte*) main::$5) ← (byte) main::$0_y +Converted procedure call LValue to member unwinding { (byte) main::$1_x, (byte) main::$1_y } ← call get (byte) main::i +Adding struct value member variable copy *((byte*) main::$6 + (byte~) main::$3) ← (byte) main::$1_x +Adding struct value member variable copy *((byte*) main::$7 + (byte~) main::$3) ← (byte) main::$1_y +Adding struct value member variable copy (byte) get::return_x ← *((byte*) get::$2) +Adding struct value member variable copy (byte) get::return_y ← *((byte*) get::$3) +Adding struct value member variable copy (byte) get::return_x ← *((byte*) get::$4) +Adding struct value member variable copy (byte) get::return_y ← *((byte*) get::$5) +Adding struct value member variable copy (byte) get::return_x ← *((byte*) get::$6) +Adding struct value member variable copy (byte) get::return_y ← *((byte*) get::$7) +Adding struct value member variable copy (byte) get::return_x ← (byte) get::return_x +Adding struct value member variable copy (byte) get::return_y ← (byte) get::return_y +Converted procedure struct return value to member unwinding return { (byte) get::return_x, (byte) get::return_y } +Identified constant variable (byte) idx +Identified constant variable (struct Point*) p0 +Identified constant variable (struct Point*) p1 +Identified constant variable (struct Point*) p2 +Culled Empty Block (label) main::@2 +Culled Empty Block (label) get::@7 +Culled Empty Block (label) get::@3 +Culled Empty Block (label) get::@8 +Culled Empty Block (label) get::@9 +Culled Empty Block (label) get::@4 +Culled Empty Block (label) get::@10 +Culled Empty Block (label) get::@11 +Unwinding list assignment { (byte) main::$0_x, (byte) main::$0_y } ← { (byte) get::return_x, (byte) get::return_y } +Unwinding list assignment { (byte) main::$1_x, (byte) main::$1_y } ← { (byte) get::return_x, (byte) get::return_y } +Unwinding list assignment { (byte) get::return_x#0, (byte) get::return_y#0 } ← { (byte) get::return_x#5, (byte) get::return_y#5 } +Unwinding list assignment { (byte) get::return_x#1, (byte) get::return_y#1 } ← { (byte) get::return_x#5, (byte) get::return_y#5 } +Adding versioned struct unwinding for (struct Point) get::return#0 +Adding versioned struct unwinding for (struct Point) get::return#1 +Adding versioned struct unwinding for (struct Point) get::return#2 +Adding versioned struct unwinding for (struct Point) get::return#3 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (struct Point*) SCREEN#0 ← ((struct Point*)) (number) $400 + to:@1 +main: scope:[main] from @2 + (byte) get::i#0 ← (number) 0 + call get + (byte) get::return_x#0 ← (byte) get::return_x#5 + (byte) get::return_y#0 ← (byte) get::return_y#5 + to:main::@3 +main::@3: scope:[main] from main + (byte) get::return_y#6 ← phi( main/(byte) get::return_y#0 ) + (byte) get::return_x#6 ← phi( main/(byte) get::return_x#0 ) + (byte) main::$0_x ← (byte) get::return_x#6 + (byte) main::$0_y ← (byte) get::return_y#6 + (byte*) main::$4 ← (byte*)(struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::$4) ← (byte) main::$0_x + (byte*) main::$5 ← (byte*)(struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::$5) ← (byte) main::$0_y + (byte) main::i#0 ← (byte) 1 + to:main::@1 +main::@1: scope:[main] from main::@3 main::@4 + (byte) main::i#2 ← phi( main::@3/(byte) main::i#0 main::@4/(byte) main::i#1 ) + (byte) get::i#1 ← (byte) main::i#2 + call get + (byte) get::return_x#1 ← (byte) get::return_x#5 + (byte) get::return_y#1 ← (byte) get::return_y#5 + to:main::@4 +main::@4: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + (byte) get::return_y#7 ← phi( main::@1/(byte) get::return_y#1 ) + (byte) get::return_x#7 ← phi( main::@1/(byte) get::return_x#1 ) + (byte) main::$1_x ← (byte) get::return_x#7 + (byte) main::$1_y ← (byte) get::return_y#7 + (byte~) main::$3 ← (byte) main::i#3 * (const byte) SIZEOF_STRUCT_POINT + (byte*) main::$6 ← (byte*)(struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::$6 + (byte~) main::$3) ← (byte) main::$1_x + (byte*) main::$7 ← (byte*)(struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::$7 + (byte~) main::$3) ← (byte) main::$1_y + (byte) main::i#1 ← (byte) main::i#3 + rangenext(1,2) + (bool~) main::$2 ← (byte) main::i#1 != rangelast(1,2) + if((bool~) main::$2) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@4 + return + to:@return +@1: scope:[] from @begin + (struct Point*) p0#0 ← ((struct Point*)) (number) $a000 + (struct Point*) p1#0 ← ((struct Point*)) (number) $b000 + (struct Point*) p2#0 ← ((struct Point*)) (number) $e000 + to:@2 +get: scope:[get] from main main::@1 + (byte) get::i#2 ← phi( main/(byte) get::i#0 main::@1/(byte) get::i#1 ) + (bool~) get::$0 ← (byte) get::i#2 == (number) 0 + if((bool~) get::$0) goto get::@1 + to:get::@5 +get::@1: scope:[get] from get + (byte*) get::$2 ← (byte*)(struct Point*) p0#0 + (const byte) OFFSET_STRUCT_POINT_X + (byte) get::return_x#2 ← *((byte*) get::$2) + (byte*) get::$3 ← (byte*)(struct Point*) p0#0 + (const byte) OFFSET_STRUCT_POINT_Y + (byte) get::return_y#2 ← *((byte*) get::$3) + (struct Point) get::return#0 ← struct-unwound {(byte) get::return_x#2, (byte) get::return_y#2} + to:get::@return +get::@5: scope:[get] from get + (byte) get::i#3 ← phi( get/(byte) get::i#2 ) + (bool~) get::$1 ← (byte) get::i#3 == (number) 1 + if((bool~) get::$1) goto get::@2 + to:get::@6 +get::@2: scope:[get] from get::@5 + (byte*) get::$4 ← (byte*)(struct Point*) p1#0 + (const byte) OFFSET_STRUCT_POINT_X + (byte) get::return_x#3 ← *((byte*) get::$4) + (byte*) get::$5 ← (byte*)(struct Point*) p1#0 + (const byte) OFFSET_STRUCT_POINT_Y + (byte) get::return_y#3 ← *((byte*) get::$5) + (struct Point) get::return#1 ← struct-unwound {(byte) get::return_x#3, (byte) get::return_y#3} + to:get::@return +get::@6: scope:[get] from get::@5 + (byte*) get::$6 ← (byte*)(struct Point*) p2#0 + (const byte) OFFSET_STRUCT_POINT_X + (byte) get::return_x#4 ← *((byte*) get::$6) + (byte*) get::$7 ← (byte*)(struct Point*) p2#0 + (const byte) OFFSET_STRUCT_POINT_Y + (byte) get::return_y#4 ← *((byte*) get::$7) + (struct Point) get::return#2 ← struct-unwound {(byte) get::return_x#4, (byte) get::return_y#4} + to:get::@return +get::@return: scope:[get] from get::@1 get::@2 get::@6 + (byte) get::return_y#8 ← phi( get::@1/(byte) get::return_y#2 get::@2/(byte) get::return_y#3 get::@6/(byte) get::return_y#4 ) + (byte) get::return_x#8 ← phi( get::@1/(byte) get::return_x#2 get::@2/(byte) get::return_x#3 get::@6/(byte) get::return_x#4 ) + (byte) get::return_x#5 ← (byte) get::return_x#8 + (byte) get::return_y#5 ← (byte) get::return_y#8 + (struct Point) get::return#3 ← struct-unwound {(byte) get::return_x#5, (byte) get::return_y#5} + return + to:@return +@2: scope:[] from @1 + call main + to:@3 +@3: scope:[] from @2 + to:@end +@end: scope:[] from @3 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @3 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_X = (byte) 0 +(const byte) OFFSET_STRUCT_POINT_Y = (byte) 1 +(byte) Point::x +(byte) Point::y +(struct Point*) SCREEN +(struct Point*) SCREEN#0 +(const byte) SIZEOF_STRUCT_POINT = (byte) 2 +(struct Point()) get((byte) get::i) +(bool~) get::$0 +(bool~) get::$1 +(byte*) get::$2 +(byte*) get::$3 +(byte*) get::$4 +(byte*) get::$5 +(byte*) get::$6 +(byte*) get::$7 +(label) get::@1 +(label) get::@2 +(label) get::@5 +(label) get::@6 +(label) get::@return +(byte) get::i +(byte) get::i#0 +(byte) get::i#1 +(byte) get::i#2 +(byte) get::i#3 +(struct Point) get::return +(struct Point) get::return#0 +(struct Point) get::return#1 +(struct Point) get::return#2 +(struct Point) get::return#3 +(byte) get::return_x +(byte) get::return_x#0 +(byte) get::return_x#1 +(byte) get::return_x#2 +(byte) get::return_x#3 +(byte) get::return_x#4 +(byte) get::return_x#5 +(byte) get::return_x#6 +(byte) get::return_x#7 +(byte) get::return_x#8 +(byte) get::return_y +(byte) get::return_y#0 +(byte) get::return_y#1 +(byte) get::return_y#2 +(byte) get::return_y#3 +(byte) get::return_y#4 +(byte) get::return_y#5 +(byte) get::return_y#6 +(byte) get::return_y#7 +(byte) get::return_y#8 +(void()) main() +(struct Point~) main::$0 +(byte) main::$0_x +(byte) main::$0_y +(struct Point~) main::$1 +(byte) main::$1_x +(byte) main::$1_y +(bool~) main::$2 +(byte~) main::$3 +(byte*) main::$4 +(byte*) main::$5 +(byte*) main::$6 +(byte*) main::$7 +(label) main::@1 +(label) main::@3 +(label) main::@4 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(struct Point*) p0 +(struct Point*) p0#0 +(struct Point*) p1 +(struct Point*) p1#0 +(struct Point*) p2 +(struct Point*) p2#0 + +Adding number conversion cast (unumber) 0 in (byte) get::i#0 ← (number) 0 +Adding number conversion cast (unumber) 0 in (bool~) get::$0 ← (byte) get::i#2 == (number) 0 +Adding number conversion cast (unumber) 1 in (bool~) get::$1 ← (byte) get::i#3 == (number) 1 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (struct Point*) SCREEN#0 ← (struct Point*)(number) $400 +Inlining cast (byte) get::i#0 ← (unumber)(number) 0 +Inlining cast (struct Point*) p0#0 ← (struct Point*)(number) $a000 +Inlining cast (struct Point*) p1#0 ← (struct Point*)(number) $b000 +Inlining cast (struct Point*) p2#0 ← (struct Point*)(number) $e000 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (struct Point*) 1024 +Simplifying constant integer cast 0 +Simplifying constant pointer cast (struct Point*) 40960 +Simplifying constant pointer cast (struct Point*) 45056 +Simplifying constant pointer cast (struct Point*) 57344 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) get::return_x#0 = (byte) get::return_x#6 +Alias (byte) get::return_y#0 = (byte) get::return_y#6 +Alias (byte) get::return_x#1 = (byte) get::return_x#7 +Alias (byte) get::return_y#1 = (byte) get::return_y#7 +Alias (byte) main::i#2 = (byte) main::i#3 +Alias (byte) get::i#2 = (byte) get::i#3 +Alias (byte) get::return_x#5 = (byte) get::return_x#8 +Alias (byte) get::return_y#5 = (byte) get::return_y#8 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$2 [28] if((byte) main::i#1!=rangelast(1,2)) goto main::@1 +Simple Condition (bool~) get::$0 [35] if((byte) get::i#2==(byte) 0) goto get::@1 +Simple Condition (bool~) get::$1 [43] if((byte) get::i#2==(byte) 1) goto get::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const struct Point*) SCREEN#0 = (struct Point*) 1024 +Constant (const byte) get::i#0 = 0 +Constant (const byte) main::i#0 = 1 +Constant (const struct Point*) p0#0 = (struct Point*) 40960 +Constant (const struct Point*) p1#0 = (struct Point*) 45056 +Constant (const struct Point*) p2#0 = (struct Point*) 57344 +Successful SSA optimization Pass2ConstantIdentification +Constant value identified (byte*)SCREEN#0 in [8] (byte*) main::$4 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)SCREEN#0 in [10] (byte*) main::$5 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant value identified (byte*)SCREEN#0 in [22] (byte*) main::$6 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)SCREEN#0 in [24] (byte*) main::$7 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant value identified (byte*)p0#0 in [36] (byte*) get::$2 ← (byte*)(const struct Point*) p0#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)p0#0 in [38] (byte*) get::$3 ← (byte*)(const struct Point*) p0#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant value identified (byte*)p1#0 in [44] (byte*) get::$4 ← (byte*)(const struct Point*) p1#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)p1#0 in [46] (byte*) get::$5 ← (byte*)(const struct Point*) p1#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant value identified (byte*)p2#0 in [49] (byte*) get::$6 ← (byte*)(const struct Point*) p2#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)p2#0 in [51] (byte*) get::$7 ← (byte*)(const struct Point*) p2#0 + (const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantValues +Resolved ranged next value [26] main::i#1 ← ++ main::i#2 to ++ +Resolved ranged comparison value [28] if(main::i#1!=rangelast(1,2)) goto main::@1 to (number) 3 +Converting *(pointer+n) to pointer[n] [9] *((byte*) main::$4) ← (byte) main::$0_x -- *((byte*)SCREEN#0 + OFFSET_STRUCT_POINT_X) +Converting *(pointer+n) to pointer[n] [11] *((byte*) main::$5) ← (byte) main::$0_y -- *((byte*)SCREEN#0 + OFFSET_STRUCT_POINT_Y) +Converting *(pointer+n) to pointer[n] [37] (byte) get::return_x#2 ← *((byte*) get::$2) -- *((byte*)p0#0 + OFFSET_STRUCT_POINT_X) +Converting *(pointer+n) to pointer[n] [39] (byte) get::return_y#2 ← *((byte*) get::$3) -- *((byte*)p0#0 + OFFSET_STRUCT_POINT_Y) +Converting *(pointer+n) to pointer[n] [45] (byte) get::return_x#3 ← *((byte*) get::$4) -- *((byte*)p1#0 + OFFSET_STRUCT_POINT_X) +Converting *(pointer+n) to pointer[n] [47] (byte) get::return_y#3 ← *((byte*) get::$5) -- *((byte*)p1#0 + OFFSET_STRUCT_POINT_Y) +Converting *(pointer+n) to pointer[n] [50] (byte) get::return_x#4 ← *((byte*) get::$6) -- *((byte*)p2#0 + OFFSET_STRUCT_POINT_X) +Converting *(pointer+n) to pointer[n] [52] (byte) get::return_y#4 ← *((byte*) get::$7) -- *((byte*)p2#0 + OFFSET_STRUCT_POINT_Y) +Successful SSA optimization Pass2InlineDerefIdx +Simplifying expression containing zero (byte*)SCREEN#0 in [8] (byte*) main::$4 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)SCREEN#0 in [9] *((byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X) ← (byte) main::$0_x +Simplifying expression containing zero (byte*)SCREEN#0 in [22] (byte*) main::$6 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)p0#0 in [36] (byte*) get::$2 ← (byte*)(const struct Point*) p0#0 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)p0#0 in [37] (byte) get::return_x#2 ← *((byte*)(const struct Point*) p0#0 + (const byte) OFFSET_STRUCT_POINT_X) +Simplifying expression containing zero (byte*)p1#0 in [44] (byte*) get::$4 ← (byte*)(const struct Point*) p1#0 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)p1#0 in [45] (byte) get::return_x#3 ← *((byte*)(const struct Point*) p1#0 + (const byte) OFFSET_STRUCT_POINT_X) +Simplifying expression containing zero (byte*)p2#0 in [49] (byte*) get::$6 ← (byte*)(const struct Point*) p2#0 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)p2#0 in [50] (byte) get::return_x#4 ← *((byte*)(const struct Point*) p2#0 + (const byte) OFFSET_STRUCT_POINT_X) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable (byte*) main::$4 and assignment [5] (byte*) main::$4 ← (byte*)(const struct Point*) SCREEN#0 +Eliminating unused variable (byte*) main::$5 and assignment [7] (byte*) main::$5 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y +Eliminating unused variable (byte*) get::$2 and assignment [26] (byte*) get::$2 ← (byte*)(const struct Point*) p0#0 +Eliminating unused variable (byte*) get::$3 and assignment [28] (byte*) get::$3 ← (byte*)(const struct Point*) p0#0 + (const byte) OFFSET_STRUCT_POINT_Y +Eliminating unused variable (struct Point) get::return#0 and assignment [30] (struct Point) get::return#0 ← struct-unwound {(byte) get::return_x#2, (byte) get::return_y#2} +Eliminating unused variable (byte*) get::$4 and assignment [32] (byte*) get::$4 ← (byte*)(const struct Point*) p1#0 +Eliminating unused variable (byte*) get::$5 and assignment [34] (byte*) get::$5 ← (byte*)(const struct Point*) p1#0 + (const byte) OFFSET_STRUCT_POINT_Y +Eliminating unused variable (struct Point) get::return#1 and assignment [36] (struct Point) get::return#1 ← struct-unwound {(byte) get::return_x#3, (byte) get::return_y#3} +Eliminating unused variable (byte*) get::$6 and assignment [37] (byte*) get::$6 ← (byte*)(const struct Point*) p2#0 +Eliminating unused variable (byte*) get::$7 and assignment [39] (byte*) get::$7 ← (byte*)(const struct Point*) p2#0 + (const byte) OFFSET_STRUCT_POINT_Y +Eliminating unused variable (struct Point) get::return#2 and assignment [41] (struct Point) get::return#2 ← struct-unwound {(byte) get::return_x#4, (byte) get::return_y#4} +Eliminating unused variable (struct Point) get::return#3 and assignment [43] (struct Point) get::return#3 ← struct-unwound {(byte) get::return_x#5, (byte) get::return_y#5} +Eliminating unused constant (const byte) OFFSET_STRUCT_POINT_X +Successful SSA optimization PassNEliminateUnusedVars +Adding number conversion cast (unumber) 3 in if((byte) main::i#1!=(number) 3) goto main::@1 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 3 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 3 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Constant right-side identified [17] (byte*) main::$7 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte*) main::$6 = (byte*)SCREEN#0 +Constant (const byte*) main::$7 = (byte*)SCREEN#0+OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantIdentification +Rewriting multiplication to use shift [14] (byte~) main::$3 ← (byte) main::i#2 * (const byte) SIZEOF_STRUCT_POINT +Successful SSA optimization Pass2MultiplyToShiftRewriting +Inlining constant with var siblings (const byte) main::i#0 +Inlining constant with var siblings (const byte) get::i#0 +Constant inlined main::i#0 = (byte) 1 +Constant inlined main::$6 = (byte*)(const struct Point*) SCREEN#0 +Constant inlined get::i#0 = (byte) 0 +Constant inlined main::$7 = (byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *((byte*)SCREEN#0+OFFSET_STRUCT_POINT_Y) +Consolidated array index constant in *((byte*)p0#0+OFFSET_STRUCT_POINT_Y) +Consolidated array index constant in *((byte*)p1#0+OFFSET_STRUCT_POINT_Y) +Consolidated array index constant in *((byte*)p2#0+OFFSET_STRUCT_POINT_Y) +Successful SSA optimization Pass2ConstantAdditionElimination +Eliminating unused constant (const byte) SIZEOF_STRUCT_POINT +Successful SSA optimization PassNEliminateUnusedVars +Added new block during phi lifting main::@5(between main::@4 and main::@1) +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 @3 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +CALL GRAPH +Calls in [] to main:3 +Calls in [main] to get:7 get:17 + +Created 4 initial phi equivalence classes +Coalesced [16] get::i#4 ← get::i#1 +Coalesced [28] main::i#4 ← main::i#1 +Coalesced [34] get::return_x#11 ← get::return_x#4 +Coalesced [35] get::return_y#11 ← get::return_y#4 +Coalesced [40] get::return_x#10 ← get::return_x#3 +Coalesced [41] get::return_y#10 ← get::return_y#3 +Coalesced [44] get::return_x#9 ← get::return_x#2 +Coalesced [45] get::return_y#9 ← get::return_y#2 +Coalesced down to 4 phi equivalence classes +Culled Empty Block (label) @1 +Culled Empty Block (label) @3 +Culled Empty Block (label) main::@5 +Renumbering block @2 to @1 +Renumbering block main::@3 to main::@2 +Renumbering block main::@4 to main::@3 +Renumbering block get::@5 to get::@3 +Renumbering block get::@6 to get::@4 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + [5] call get + [6] (byte) get::return_x#0 ← (byte) get::return_x#5 + [7] (byte) get::return_y#0 ← (byte) get::return_y#5 + to:main::@2 +main::@2: scope:[main] from main + [8] (byte) main::$0_x ← (byte) get::return_x#0 + [9] (byte) main::$0_y ← (byte) get::return_y#0 + [10] *((byte*)(const struct Point*) SCREEN#0) ← (byte) main::$0_x + [11] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) main::$0_y + to:main::@1 +main::@1: scope:[main] from main::@2 main::@3 + [12] (byte) main::i#2 ← phi( main::@2/(byte) 1 main::@3/(byte) main::i#1 ) + [13] (byte) get::i#1 ← (byte) main::i#2 + [14] call get + [15] (byte) get::return_x#1 ← (byte) get::return_x#5 + [16] (byte) get::return_y#1 ← (byte) get::return_y#5 + to:main::@3 +main::@3: scope:[main] from main::@1 + [17] (byte) main::$1_x ← (byte) get::return_x#1 + [18] (byte) main::$1_y ← (byte) get::return_y#1 + [19] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 + [20] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x + [21] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (byte) main::$1_y + [22] (byte) main::i#1 ← ++ (byte) main::i#2 + [23] if((byte) main::i#1!=(byte) 3) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@3 + [24] return + to:@return +get: scope:[get] from main main::@1 + [25] (byte) get::i#2 ← phi( main/(byte) 0 main::@1/(byte) get::i#1 ) + [26] if((byte) get::i#2==(byte) 0) goto get::@1 + to:get::@3 +get::@3: scope:[get] from get + [27] if((byte) get::i#2==(byte) 1) goto get::@2 + to:get::@4 +get::@4: scope:[get] from get::@3 + [28] (byte) get::return_x#4 ← *((byte*)(const struct Point*) p2#0) + [29] (byte) get::return_y#4 ← *((byte*)(const struct Point*) p2#0+(const byte) OFFSET_STRUCT_POINT_Y) + to:get::@return +get::@return: scope:[get] from get::@1 get::@2 get::@4 + [30] (byte) get::return_y#5 ← phi( get::@1/(byte) get::return_y#2 get::@2/(byte) get::return_y#3 get::@4/(byte) get::return_y#4 ) + [30] (byte) get::return_x#5 ← phi( get::@1/(byte) get::return_x#2 get::@2/(byte) get::return_x#3 get::@4/(byte) get::return_x#4 ) + [31] return + to:@return +get::@2: scope:[get] from get::@3 + [32] (byte) get::return_x#3 ← *((byte*)(const struct Point*) p1#0) + [33] (byte) get::return_y#3 ← *((byte*)(const struct Point*) p1#0+(const byte) OFFSET_STRUCT_POINT_Y) + to:get::@return +get::@1: scope:[get] from get + [34] (byte) get::return_x#2 ← *((byte*)(const struct Point*) p0#0) + [35] (byte) get::return_y#2 ← *((byte*)(const struct Point*) p0#0+(const byte) OFFSET_STRUCT_POINT_Y) + to:get::@return + + +VARIABLE REGISTER WEIGHTS +(byte) Point::x +(byte) Point::y +(struct Point*) SCREEN +(struct Point()) get((byte) get::i) +(byte) get::i +(byte) get::i#1 22.0 +(byte) get::i#2 7.5 +(struct Point) get::return +(byte) get::return_x +(byte) get::return_x#0 2.0 +(byte) get::return_x#1 11.0 +(byte) get::return_x#2 2.0 +(byte) get::return_x#3 2.0 +(byte) get::return_x#4 2.0 +(byte) get::return_x#5 4.75 +(byte) get::return_y +(byte) get::return_y#0 2.0 +(byte) get::return_y#1 11.0 +(byte) get::return_y#2 4.0 +(byte) get::return_y#3 4.0 +(byte) get::return_y#4 4.0 +(byte) get::return_y#5 3.166666666666667 +(void()) main() +(struct Point~) main::$0 +(byte) main::$0_x 2.0 +(byte) main::$0_y 2.0 +(struct Point~) main::$1 +(byte) main::$1_x 7.333333333333333 +(byte) main::$1_y 7.333333333333333 +(byte~) main::$3 16.5 +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#2 4.4 +(struct Point*) p0 +(struct Point*) p1 +(struct Point*) p2 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ get::i#2 get::i#1 ] +[ get::return_x#5 get::return_x#2 get::return_x#3 get::return_x#4 ] +[ get::return_y#5 get::return_y#2 get::return_y#3 get::return_y#4 ] +Added variable get::return_x#0 to zero page equivalence class [ get::return_x#0 ] +Added variable get::return_y#0 to zero page equivalence class [ get::return_y#0 ] +Added variable main::$0_x to zero page equivalence class [ main::$0_x ] +Added variable main::$0_y to zero page equivalence class [ main::$0_y ] +Added variable get::return_x#1 to zero page equivalence class [ get::return_x#1 ] +Added variable get::return_y#1 to zero page equivalence class [ get::return_y#1 ] +Added variable main::$1_x to zero page equivalence class [ main::$1_x ] +Added variable main::$1_y to zero page equivalence class [ main::$1_y ] +Added variable main::$3 to zero page equivalence class [ main::$3 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ get::i#2 get::i#1 ] +[ get::return_x#5 get::return_x#2 get::return_x#3 get::return_x#4 ] +[ get::return_y#5 get::return_y#2 get::return_y#3 get::return_y#4 ] +[ get::return_x#0 ] +[ get::return_y#0 ] +[ main::$0_x ] +[ main::$0_y ] +[ get::return_x#1 ] +[ get::return_y#1 ] +[ main::$1_x ] +[ main::$1_y ] +[ main::$3 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Allocated zp ZP_BYTE:3 [ get::i#2 get::i#1 ] +Allocated zp ZP_BYTE:4 [ get::return_x#5 get::return_x#2 get::return_x#3 get::return_x#4 ] +Allocated zp ZP_BYTE:5 [ get::return_y#5 get::return_y#2 get::return_y#3 get::return_y#4 ] +Allocated zp ZP_BYTE:6 [ get::return_x#0 ] +Allocated zp ZP_BYTE:7 [ get::return_y#0 ] +Allocated zp ZP_BYTE:8 [ main::$0_x ] +Allocated zp ZP_BYTE:9 [ main::$0_y ] +Allocated zp ZP_BYTE:10 [ get::return_x#1 ] +Allocated zp ZP_BYTE:11 [ get::return_y#1 ] +Allocated zp ZP_BYTE:12 [ main::$1_x ] +Allocated zp ZP_BYTE:13 [ main::$1_y ] +Allocated zp ZP_BYTE:14 [ main::$3 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// Demonstrates problem with returning a dereferenced pointer to a struct + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + .label p0 = $a000 + .label p1 = $b000 + .label p2 = $e000 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label _3 = $e + .label _0_x = 8 + .label _0_y = 9 + .label _1_x = $c + .label _1_y = $d + .label i = 2 + // [5] call get + // [25] phi from main to get [phi:main->get] + get_from_main: + // [25] phi (byte) get::i#2 = (byte) 0 [phi:main->get#0] -- vbuz1=vbuc1 + lda #0 + sta get.i + jsr get + // [6] (byte) get::return_x#0 ← (byte) get::return_x#5 -- vbuz1=vbuz2 + lda get.return_x_5 + sta get.return_x + // [7] (byte) get::return_y#0 ← (byte) get::return_y#5 -- vbuz1=vbuz2 + lda get.return_y_5 + sta get.return_y + jmp b2 + // main::@2 + b2: + // [8] (byte) main::$0_x ← (byte) get::return_x#0 -- vbuz1=vbuz2 + lda get.return_x + sta _0_x + // [9] (byte) main::$0_y ← (byte) get::return_y#0 -- vbuz1=vbuz2 + lda get.return_y + sta _0_y + // [10] *((byte*)(const struct Point*) SCREEN#0) ← (byte) main::$0_x -- _deref_pbuc1=vbuz1 + lda _0_x + sta SCREEN + // [11] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) main::$0_y -- _deref_pbuc1=vbuz1 + lda _0_y + sta SCREEN+OFFSET_STRUCT_POINT_Y + // [12] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [12] phi (byte) main::i#2 = (byte) 1 [phi:main::@2->main::@1#0] -- vbuz1=vbuc1 + lda #1 + sta i + jmp b1 + // [12] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + // [12] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 + // main::@1 + b1: + // [13] (byte) get::i#1 ← (byte) main::i#2 -- vbuz1=vbuz2 + lda i + sta get.i + // [14] call get + // [25] phi from main::@1 to get [phi:main::@1->get] + get_from_b1: + // [25] phi (byte) get::i#2 = (byte) get::i#1 [phi:main::@1->get#0] -- register_copy + jsr get + // [15] (byte) get::return_x#1 ← (byte) get::return_x#5 -- vbuz1=vbuz2 + lda get.return_x_5 + sta get.return_x_1 + // [16] (byte) get::return_y#1 ← (byte) get::return_y#5 -- vbuz1=vbuz2 + lda get.return_y_5 + sta get.return_y_1 + jmp b3 + // main::@3 + b3: + // [17] (byte) main::$1_x ← (byte) get::return_x#1 -- vbuz1=vbuz2 + lda get.return_x_1 + sta _1_x + // [18] (byte) main::$1_y ← (byte) get::return_y#1 -- vbuz1=vbuz2 + lda get.return_y_1 + sta _1_y + // [19] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 -- vbuz1=vbuz2_rol_1 + lda i + asl + sta _3 + // [20] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x -- pbuc1_derefidx_vbuz1=vbuz2 + lda _1_x + ldy _3 + sta SCREEN,y + // [21] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (byte) main::$1_y -- pbuc1_derefidx_vbuz1=vbuz2 + lda _1_y + ldy _3 + sta SCREEN+OFFSET_STRUCT_POINT_Y,y + // [22] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + // [23] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #3 + cmp i + bne b1_from_b3 + jmp breturn + // main::@return + breturn: + // [24] return + rts +} + // get +// get(byte zeropage(3) i) +get: { + .label return_x = 6 + .label return_y = 7 + .label i = 3 + .label return_x_1 = $a + .label return_y_1 = $b + .label return_x_2 = 4 + .label return_y_2 = 5 + .label return_x_3 = 4 + .label return_y_3 = 5 + .label return_x_4 = 4 + .label return_y_4 = 5 + .label return_x_5 = 4 + .label return_y_5 = 5 + // [26] if((byte) get::i#2==(byte) 0) goto get::@1 -- vbuz1_eq_0_then_la1 + lda i + cmp #0 + beq b1 + jmp b3 + // get::@3 + b3: + // [27] if((byte) get::i#2==(byte) 1) goto get::@2 -- vbuz1_eq_vbuc1_then_la1 + lda #1 + cmp i + beq b2 + jmp b4 + // get::@4 + b4: + // [28] (byte) get::return_x#4 ← *((byte*)(const struct Point*) p2#0) -- vbuz1=_deref_pbuc1 + lda p2 + sta return_x_4 + // [29] (byte) get::return_y#4 ← *((byte*)(const struct Point*) p2#0+(const byte) OFFSET_STRUCT_POINT_Y) -- vbuz1=_deref_pbuc1 + lda p2+OFFSET_STRUCT_POINT_Y + sta return_y_4 + // [30] phi from get::@1 get::@2 get::@4 to get::@return [phi:get::@1/get::@2/get::@4->get::@return] + breturn_from_b1: + breturn_from_b2: + breturn_from_b4: + // [30] phi (byte) get::return_y#5 = (byte) get::return_y#2 [phi:get::@1/get::@2/get::@4->get::@return#0] -- register_copy + // [30] phi (byte) get::return_x#5 = (byte) get::return_x#2 [phi:get::@1/get::@2/get::@4->get::@return#1] -- register_copy + jmp breturn + // get::@return + breturn: + // [31] return + rts + // get::@2 + b2: + // [32] (byte) get::return_x#3 ← *((byte*)(const struct Point*) p1#0) -- vbuz1=_deref_pbuc1 + lda p1 + sta return_x_3 + // [33] (byte) get::return_y#3 ← *((byte*)(const struct Point*) p1#0+(const byte) OFFSET_STRUCT_POINT_Y) -- vbuz1=_deref_pbuc1 + lda p1+OFFSET_STRUCT_POINT_Y + sta return_y_3 + jmp breturn_from_b2 + // get::@1 + b1: + // [34] (byte) get::return_x#2 ← *((byte*)(const struct Point*) p0#0) -- vbuz1=_deref_pbuc1 + lda p0 + sta return_x_2 + // [35] (byte) get::return_y#2 ← *((byte*)(const struct Point*) p0#0+(const byte) OFFSET_STRUCT_POINT_Y) -- vbuz1=_deref_pbuc1 + lda p0+OFFSET_STRUCT_POINT_Y + sta return_y_2 + jmp breturn_from_b1 +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [19] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 [ main::i#2 main::$1_x main::$1_y main::$3 ] ( main:2 [ main::i#2 main::$1_x main::$1_y main::$3 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:12 [ main::$1_x ] +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:13 [ main::$1_y ] +Statement [20] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x [ main::i#2 main::$1_y main::$3 ] ( main:2 [ main::i#2 main::$1_y main::$3 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:14 [ main::$3 ] +Statement [21] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (byte) main::$1_y [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Statement [19] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 [ main::i#2 main::$1_x main::$1_y main::$3 ] ( main:2 [ main::i#2 main::$1_x main::$1_y main::$3 ] ) always clobbers reg byte a +Statement [20] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x [ main::i#2 main::$1_y main::$3 ] ( main:2 [ main::i#2 main::$1_y main::$3 ] ) always clobbers reg byte a +Statement [21] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (byte) main::$1_y [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:3 [ get::i#2 get::i#1 ] : zp ZP_BYTE:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:4 [ get::return_x#5 get::return_x#2 get::return_x#3 get::return_x#4 ] : zp ZP_BYTE:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:5 [ get::return_y#5 get::return_y#2 get::return_y#3 get::return_y#4 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:6 [ get::return_x#0 ] : zp ZP_BYTE:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:7 [ get::return_y#0 ] : zp ZP_BYTE:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:8 [ main::$0_x ] : zp ZP_BYTE:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:9 [ main::$0_y ] : zp ZP_BYTE:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:10 [ get::return_x#1 ] : zp ZP_BYTE:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:11 [ get::return_y#1 ] : zp ZP_BYTE:11 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:12 [ main::$1_x ] : zp ZP_BYTE:12 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:13 [ main::$1_y ] : zp ZP_BYTE:13 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:14 [ main::$3 ] : zp ZP_BYTE:14 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [get] 29.5: zp ZP_BYTE:3 [ get::i#2 get::i#1 ] 15.17: zp ZP_BYTE:5 [ get::return_y#5 get::return_y#2 get::return_y#3 get::return_y#4 ] 11: zp ZP_BYTE:10 [ get::return_x#1 ] 11: zp ZP_BYTE:11 [ get::return_y#1 ] 10.75: zp ZP_BYTE:4 [ get::return_x#5 get::return_x#2 get::return_x#3 get::return_x#4 ] 2: zp ZP_BYTE:6 [ get::return_x#0 ] 2: zp ZP_BYTE:7 [ get::return_y#0 ] +Uplift Scope [main] 20.9: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 16.5: zp ZP_BYTE:14 [ main::$3 ] 7.33: zp ZP_BYTE:12 [ main::$1_x ] 7.33: zp ZP_BYTE:13 [ main::$1_y ] 2: zp ZP_BYTE:8 [ main::$0_x ] 2: zp ZP_BYTE:9 [ main::$0_y ] +Uplift Scope [Point] +Uplift Scope [] + +Uplifting [get] best 889 combination reg byte a [ get::i#2 get::i#1 ] zp ZP_BYTE:5 [ get::return_y#5 get::return_y#2 get::return_y#3 get::return_y#4 ] reg byte x [ get::return_x#1 ] reg byte a [ get::return_y#1 ] zp ZP_BYTE:4 [ get::return_x#5 get::return_x#2 get::return_x#3 get::return_x#4 ] zp ZP_BYTE:6 [ get::return_x#0 ] zp ZP_BYTE:7 [ get::return_y#0 ] +Limited combination testing to 100 combinations of 16384 possible. +Uplifting [main] best 709 combination reg byte y [ main::i#2 main::i#1 ] reg byte x [ main::$3 ] zp ZP_BYTE:12 [ main::$1_x ] zp ZP_BYTE:13 [ main::$1_y ] zp ZP_BYTE:8 [ main::$0_x ] zp ZP_BYTE:9 [ main::$0_y ] +Limited combination testing to 100 combinations of 1296 possible. +Uplifting [Point] best 709 combination +Uplifting [] best 709 combination +Attempting to uplift remaining variables inzp ZP_BYTE:5 [ get::return_y#5 get::return_y#2 get::return_y#3 get::return_y#4 ] +Uplifting [get] best 709 combination zp ZP_BYTE:5 [ get::return_y#5 get::return_y#2 get::return_y#3 get::return_y#4 ] +Attempting to uplift remaining variables inzp ZP_BYTE:4 [ get::return_x#5 get::return_x#2 get::return_x#3 get::return_x#4 ] +Uplifting [get] best 667 combination reg byte x [ get::return_x#5 get::return_x#2 get::return_x#3 get::return_x#4 ] +Attempting to uplift remaining variables inzp ZP_BYTE:12 [ main::$1_x ] +Uplifting [main] best 667 combination zp ZP_BYTE:12 [ main::$1_x ] +Attempting to uplift remaining variables inzp ZP_BYTE:13 [ main::$1_y ] +Uplifting [main] best 667 combination zp ZP_BYTE:13 [ main::$1_y ] +Attempting to uplift remaining variables inzp ZP_BYTE:6 [ get::return_x#0 ] +Uplifting [get] best 661 combination reg byte x [ get::return_x#0 ] +Attempting to uplift remaining variables inzp ZP_BYTE:7 [ get::return_y#0 ] +Uplifting [get] best 655 combination reg byte a [ get::return_y#0 ] +Attempting to uplift remaining variables inzp ZP_BYTE:8 [ main::$0_x ] +Uplifting [main] best 649 combination reg byte x [ main::$0_x ] +Attempting to uplift remaining variables inzp ZP_BYTE:9 [ main::$0_y ] +Uplifting [main] best 643 combination reg byte a [ main::$0_y ] +Allocated (was zp ZP_BYTE:5) zp ZP_BYTE:2 [ get::return_y#5 get::return_y#2 get::return_y#3 get::return_y#4 ] +Allocated (was zp ZP_BYTE:12) zp ZP_BYTE:3 [ main::$1_x ] +Allocated (was zp ZP_BYTE:13) zp ZP_BYTE:4 [ main::$1_y ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Demonstrates problem with returning a dereferenced pointer to a struct + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + .label p0 = $a000 + .label p1 = $b000 + .label p2 = $e000 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label _1_x = 3 + .label _1_y = 4 + // [5] call get + // [25] phi from main to get [phi:main->get] + get_from_main: + // [25] phi (byte) get::i#2 = (byte) 0 [phi:main->get#0] -- vbuaa=vbuc1 + lda #0 + jsr get + // [6] (byte) get::return_x#0 ← (byte) get::return_x#5 + // [7] (byte) get::return_y#0 ← (byte) get::return_y#5 -- vbuaa=vbuz1 + lda get.return_y + jmp b2 + // main::@2 + b2: + // [8] (byte) main::$0_x ← (byte) get::return_x#0 + // [9] (byte) main::$0_y ← (byte) get::return_y#0 + // [10] *((byte*)(const struct Point*) SCREEN#0) ← (byte) main::$0_x -- _deref_pbuc1=vbuxx + stx SCREEN + // [11] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) main::$0_y -- _deref_pbuc1=vbuaa + sta SCREEN+OFFSET_STRUCT_POINT_Y + // [12] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [12] phi (byte) main::i#2 = (byte) 1 [phi:main::@2->main::@1#0] -- vbuyy=vbuc1 + ldy #1 + jmp b1 + // [12] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + // [12] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 + // main::@1 + b1: + // [13] (byte) get::i#1 ← (byte) main::i#2 -- vbuaa=vbuyy + tya + // [14] call get + // [25] phi from main::@1 to get [phi:main::@1->get] + get_from_b1: + // [25] phi (byte) get::i#2 = (byte) get::i#1 [phi:main::@1->get#0] -- register_copy + jsr get + // [15] (byte) get::return_x#1 ← (byte) get::return_x#5 + // [16] (byte) get::return_y#1 ← (byte) get::return_y#5 -- vbuaa=vbuz1 + lda get.return_y + jmp b3 + // main::@3 + b3: + // [17] (byte) main::$1_x ← (byte) get::return_x#1 -- vbuz1=vbuxx + stx _1_x + // [18] (byte) main::$1_y ← (byte) get::return_y#1 -- vbuz1=vbuaa + sta _1_y + // [19] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 -- vbuxx=vbuyy_rol_1 + tya + asl + tax + // [20] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x -- pbuc1_derefidx_vbuxx=vbuz1 + lda _1_x + sta SCREEN,x + // [21] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (byte) main::$1_y -- pbuc1_derefidx_vbuxx=vbuz1 + lda _1_y + sta SCREEN+OFFSET_STRUCT_POINT_Y,x + // [22] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuyy=_inc_vbuyy + iny + // [23] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuyy_neq_vbuc1_then_la1 + cpy #3 + bne b1_from_b3 + jmp breturn + // main::@return + breturn: + // [24] return + rts +} + // get +// get(byte register(A) i) +get: { + .label return_y = 2 + // [26] if((byte) get::i#2==(byte) 0) goto get::@1 -- vbuaa_eq_0_then_la1 + cmp #0 + beq b1 + jmp b3 + // get::@3 + b3: + // [27] if((byte) get::i#2==(byte) 1) goto get::@2 -- vbuaa_eq_vbuc1_then_la1 + cmp #1 + beq b2 + jmp b4 + // get::@4 + b4: + // [28] (byte) get::return_x#4 ← *((byte*)(const struct Point*) p2#0) -- vbuxx=_deref_pbuc1 + ldx p2 + // [29] (byte) get::return_y#4 ← *((byte*)(const struct Point*) p2#0+(const byte) OFFSET_STRUCT_POINT_Y) -- vbuz1=_deref_pbuc1 + lda p2+OFFSET_STRUCT_POINT_Y + sta return_y + // [30] phi from get::@1 get::@2 get::@4 to get::@return [phi:get::@1/get::@2/get::@4->get::@return] + breturn_from_b1: + breturn_from_b2: + breturn_from_b4: + // [30] phi (byte) get::return_y#5 = (byte) get::return_y#2 [phi:get::@1/get::@2/get::@4->get::@return#0] -- register_copy + // [30] phi (byte) get::return_x#5 = (byte) get::return_x#2 [phi:get::@1/get::@2/get::@4->get::@return#1] -- register_copy + jmp breturn + // get::@return + breturn: + // [31] return + rts + // get::@2 + b2: + // [32] (byte) get::return_x#3 ← *((byte*)(const struct Point*) p1#0) -- vbuxx=_deref_pbuc1 + ldx p1 + // [33] (byte) get::return_y#3 ← *((byte*)(const struct Point*) p1#0+(const byte) OFFSET_STRUCT_POINT_Y) -- vbuz1=_deref_pbuc1 + lda p1+OFFSET_STRUCT_POINT_Y + sta return_y + jmp breturn_from_b2 + // get::@1 + b1: + // [34] (byte) get::return_x#2 ← *((byte*)(const struct Point*) p0#0) -- vbuxx=_deref_pbuc1 + ldx p0 + // [35] (byte) get::return_y#2 ← *((byte*)(const struct Point*) p0#0+(const byte) OFFSET_STRUCT_POINT_Y) -- vbuz1=_deref_pbuc1 + lda p0+OFFSET_STRUCT_POINT_Y + sta return_y + jmp breturn_from_b1 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b2 +Removing instruction jmp b1 +Removing instruction jmp b3 +Removing instruction jmp breturn +Removing instruction jmp b3 +Removing instruction jmp b4 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b1_from_b3 with b1 +Replacing label breturn_from_b2 with breturn +Replacing label breturn_from_b1 with breturn +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b3: +Removing instruction breturn_from_b1: +Removing instruction breturn_from_b2: +Removing instruction breturn_from_b4: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction get_from_main: +Removing instruction b2: +Removing instruction b1_from_b2: +Removing instruction get_from_b1: +Removing instruction b3: +Removing instruction breturn: +Removing instruction b3: +Removing instruction b4: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Replacing jump to rts with rts in jmp breturn +Replacing jump to rts with rts in jmp breturn +Succesful ASM optimization Pass5DoubleJumpElimination +Removing instruction jmp b1 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction bbegin: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_Y OFFSET_STRUCT_POINT_Y = (byte) 1 +(byte) Point::x +(byte) Point::y +(struct Point*) SCREEN +(const struct Point*) SCREEN#0 SCREEN = (struct Point*) 1024 +(struct Point()) get((byte) get::i) +(label) get::@1 +(label) get::@2 +(label) get::@3 +(label) get::@4 +(label) get::@return +(byte) get::i +(byte) get::i#1 reg byte a 22.0 +(byte) get::i#2 reg byte a 7.5 +(struct Point) get::return +(byte) get::return_x +(byte) get::return_x#0 reg byte x 2.0 +(byte) get::return_x#1 reg byte x 11.0 +(byte) get::return_x#2 reg byte x 2.0 +(byte) get::return_x#3 reg byte x 2.0 +(byte) get::return_x#4 reg byte x 2.0 +(byte) get::return_x#5 reg byte x 4.75 +(byte) get::return_y +(byte) get::return_y#0 reg byte a 2.0 +(byte) get::return_y#1 reg byte a 11.0 +(byte) get::return_y#2 return_y zp ZP_BYTE:2 4.0 +(byte) get::return_y#3 return_y zp ZP_BYTE:2 4.0 +(byte) get::return_y#4 return_y zp ZP_BYTE:2 4.0 +(byte) get::return_y#5 return_y zp ZP_BYTE:2 3.166666666666667 +(void()) main() +(struct Point~) main::$0 +(byte) main::$0_x reg byte x 2.0 +(byte) main::$0_y reg byte a 2.0 +(struct Point~) main::$1 +(byte) main::$1_x $1_x zp ZP_BYTE:3 7.333333333333333 +(byte) main::$1_y $1_y zp ZP_BYTE:4 7.333333333333333 +(byte~) main::$3 reg byte x 16.5 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte y 16.5 +(byte) main::i#2 reg byte y 4.4 +(struct Point*) p0 +(const struct Point*) p0#0 p0 = (struct Point*) 40960 +(struct Point*) p1 +(const struct Point*) p1#0 p1 = (struct Point*) 45056 +(struct Point*) p2 +(const struct Point*) p2#0 p2 = (struct Point*) 57344 + +reg byte y [ main::i#2 main::i#1 ] +reg byte a [ get::i#2 get::i#1 ] +reg byte x [ get::return_x#5 get::return_x#2 get::return_x#3 get::return_x#4 ] +zp ZP_BYTE:2 [ get::return_y#5 get::return_y#2 get::return_y#3 get::return_y#4 ] +reg byte x [ get::return_x#0 ] +reg byte a [ get::return_y#0 ] +reg byte x [ main::$0_x ] +reg byte a [ main::$0_y ] +reg byte x [ get::return_x#1 ] +reg byte a [ get::return_y#1 ] +zp ZP_BYTE:3 [ main::$1_x ] +zp ZP_BYTE:4 [ main::$1_y ] +reg byte x [ main::$3 ] + + +FINAL ASSEMBLER +Score: 505 + + // File Comments +// Demonstrates problem with returning a dereferenced pointer to a struct + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + .label p0 = $a000 + .label p1 = $b000 + .label p2 = $e000 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + .label _1_x = 3 + .label _1_y = 4 + // get(0) + // [5] call get + // [25] phi from main to get [phi:main->get] + // [25] phi (byte) get::i#2 = (byte) 0 [phi:main->get#0] -- vbuaa=vbuc1 + lda #0 + jsr get + // get(0) + // [6] (byte) get::return_x#0 ← (byte) get::return_x#5 + // [7] (byte) get::return_y#0 ← (byte) get::return_y#5 -- vbuaa=vbuz1 + lda get.return_y + // main::@2 + // [8] (byte) main::$0_x ← (byte) get::return_x#0 + // [9] (byte) main::$0_y ← (byte) get::return_y#0 + // *SCREEN = get(0) + // [10] *((byte*)(const struct Point*) SCREEN#0) ← (byte) main::$0_x -- _deref_pbuc1=vbuxx + stx SCREEN + // [11] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) main::$0_y -- _deref_pbuc1=vbuaa + sta SCREEN+OFFSET_STRUCT_POINT_Y + // [12] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [12] phi (byte) main::i#2 = (byte) 1 [phi:main::@2->main::@1#0] -- vbuyy=vbuc1 + ldy #1 + // [12] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + // [12] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + // main::@1 + b1: + // get(i) + // [13] (byte) get::i#1 ← (byte) main::i#2 -- vbuaa=vbuyy + tya + // [14] call get + // [25] phi from main::@1 to get [phi:main::@1->get] + // [25] phi (byte) get::i#2 = (byte) get::i#1 [phi:main::@1->get#0] -- register_copy + jsr get + // get(i) + // [15] (byte) get::return_x#1 ← (byte) get::return_x#5 + // [16] (byte) get::return_y#1 ← (byte) get::return_y#5 -- vbuaa=vbuz1 + lda get.return_y + // main::@3 + // [17] (byte) main::$1_x ← (byte) get::return_x#1 -- vbuz1=vbuxx + stx _1_x + // [18] (byte) main::$1_y ← (byte) get::return_y#1 -- vbuz1=vbuaa + sta _1_y + // SCREEN[i] = get(i) + // [19] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 -- vbuxx=vbuyy_rol_1 + tya + asl + tax + // [20] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x -- pbuc1_derefidx_vbuxx=vbuz1 + lda _1_x + sta SCREEN,x + // [21] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (byte) main::$1_y -- pbuc1_derefidx_vbuxx=vbuz1 + lda _1_y + sta SCREEN+OFFSET_STRUCT_POINT_Y,x + // for ( char i: 1..2) + // [22] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuyy=_inc_vbuyy + iny + // [23] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuyy_neq_vbuc1_then_la1 + cpy #3 + bne b1 + // main::@return + // } + // [24] return + rts +} + // get +// get(byte register(A) i) +get: { + .label return_y = 2 + // if(i==0) + // [26] if((byte) get::i#2==(byte) 0) goto get::@1 -- vbuaa_eq_0_then_la1 + cmp #0 + beq b1 + // get::@3 + // if(i==1) + // [27] if((byte) get::i#2==(byte) 1) goto get::@2 -- vbuaa_eq_vbuc1_then_la1 + cmp #1 + beq b2 + // get::@4 + // return *p2; + // [28] (byte) get::return_x#4 ← *((byte*)(const struct Point*) p2#0) -- vbuxx=_deref_pbuc1 + ldx p2 + // [29] (byte) get::return_y#4 ← *((byte*)(const struct Point*) p2#0+(const byte) OFFSET_STRUCT_POINT_Y) -- vbuz1=_deref_pbuc1 + lda p2+OFFSET_STRUCT_POINT_Y + sta return_y + // [30] phi from get::@1 get::@2 get::@4 to get::@return [phi:get::@1/get::@2/get::@4->get::@return] + // [30] phi (byte) get::return_y#5 = (byte) get::return_y#2 [phi:get::@1/get::@2/get::@4->get::@return#0] -- register_copy + // [30] phi (byte) get::return_x#5 = (byte) get::return_x#2 [phi:get::@1/get::@2/get::@4->get::@return#1] -- register_copy + // get::@return + // } + // [31] return + rts + // get::@2 + b2: + // return *p1; + // [32] (byte) get::return_x#3 ← *((byte*)(const struct Point*) p1#0) -- vbuxx=_deref_pbuc1 + ldx p1 + // [33] (byte) get::return_y#3 ← *((byte*)(const struct Point*) p1#0+(const byte) OFFSET_STRUCT_POINT_Y) -- vbuz1=_deref_pbuc1 + lda p1+OFFSET_STRUCT_POINT_Y + sta return_y + rts + // get::@1 + b1: + // return *p0; + // [34] (byte) get::return_x#2 ← *((byte*)(const struct Point*) p0#0) -- vbuxx=_deref_pbuc1 + ldx p0 + // [35] (byte) get::return_y#2 ← *((byte*)(const struct Point*) p0#0+(const byte) OFFSET_STRUCT_POINT_Y) -- vbuz1=_deref_pbuc1 + lda p0+OFFSET_STRUCT_POINT_Y + sta return_y + rts +} + // File Data + diff --git a/src/test/ref/problem-struct-return-pointer-deref.sym b/src/test/ref/problem-struct-return-pointer-deref.sym new file mode 100644 index 000000000..57396980d --- /dev/null +++ b/src/test/ref/problem-struct-return-pointer-deref.sym @@ -0,0 +1,67 @@ +(label) @1 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_Y OFFSET_STRUCT_POINT_Y = (byte) 1 +(byte) Point::x +(byte) Point::y +(struct Point*) SCREEN +(const struct Point*) SCREEN#0 SCREEN = (struct Point*) 1024 +(struct Point()) get((byte) get::i) +(label) get::@1 +(label) get::@2 +(label) get::@3 +(label) get::@4 +(label) get::@return +(byte) get::i +(byte) get::i#1 reg byte a 22.0 +(byte) get::i#2 reg byte a 7.5 +(struct Point) get::return +(byte) get::return_x +(byte) get::return_x#0 reg byte x 2.0 +(byte) get::return_x#1 reg byte x 11.0 +(byte) get::return_x#2 reg byte x 2.0 +(byte) get::return_x#3 reg byte x 2.0 +(byte) get::return_x#4 reg byte x 2.0 +(byte) get::return_x#5 reg byte x 4.75 +(byte) get::return_y +(byte) get::return_y#0 reg byte a 2.0 +(byte) get::return_y#1 reg byte a 11.0 +(byte) get::return_y#2 return_y zp ZP_BYTE:2 4.0 +(byte) get::return_y#3 return_y zp ZP_BYTE:2 4.0 +(byte) get::return_y#4 return_y zp ZP_BYTE:2 4.0 +(byte) get::return_y#5 return_y zp ZP_BYTE:2 3.166666666666667 +(void()) main() +(struct Point~) main::$0 +(byte) main::$0_x reg byte x 2.0 +(byte) main::$0_y reg byte a 2.0 +(struct Point~) main::$1 +(byte) main::$1_x $1_x zp ZP_BYTE:3 7.333333333333333 +(byte) main::$1_y $1_y zp ZP_BYTE:4 7.333333333333333 +(byte~) main::$3 reg byte x 16.5 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte y 16.5 +(byte) main::i#2 reg byte y 4.4 +(struct Point*) p0 +(const struct Point*) p0#0 p0 = (struct Point*) 40960 +(struct Point*) p1 +(const struct Point*) p1#0 p1 = (struct Point*) 45056 +(struct Point*) p2 +(const struct Point*) p2#0 p2 = (struct Point*) 57344 + +reg byte y [ main::i#2 main::i#1 ] +reg byte a [ get::i#2 get::i#1 ] +reg byte x [ get::return_x#5 get::return_x#2 get::return_x#3 get::return_x#4 ] +zp ZP_BYTE:2 [ get::return_y#5 get::return_y#2 get::return_y#3 get::return_y#4 ] +reg byte x [ get::return_x#0 ] +reg byte a [ get::return_y#0 ] +reg byte x [ main::$0_x ] +reg byte a [ main::$0_y ] +reg byte x [ get::return_x#1 ] +reg byte a [ get::return_y#1 ] +zp ZP_BYTE:3 [ main::$1_x ] +zp ZP_BYTE:4 [ main::$1_y ] +reg byte x [ main::$3 ] diff --git a/src/test/ref/problem-struct-return-to-pointer.asm b/src/test/ref/problem-struct-return-to-pointer.asm new file mode 100644 index 000000000..e9f1bf83d --- /dev/null +++ b/src/test/ref/problem-struct-return-to-pointer.asm @@ -0,0 +1,35 @@ +// Demonstrates problem with returning a struct into a dereferenced pointer to a struct +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 +main: { + .label _1_x = 2 + lda #0 + jsr get + sta SCREEN + lda #get.p_y + sta SCREEN+OFFSET_STRUCT_POINT_Y + ldy #1 + b1: + tya + jsr get + sta _1_x + tya + asl + tax + lda _1_x + sta SCREEN,x + lda #get.p_y + sta SCREEN+OFFSET_STRUCT_POINT_Y,x + iny + cpy #3 + bne b1 + rts +} +// get(byte register(A) i) +get: { + .label p_y = 7 + rts +} diff --git a/src/test/ref/problem-struct-return-to-pointer.cfg b/src/test/ref/problem-struct-return-to-pointer.cfg new file mode 100644 index 000000000..d434af197 --- /dev/null +++ b/src/test/ref/problem-struct-return-to-pointer.cfg @@ -0,0 +1,42 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + [5] call get + [6] (byte) get::return_x#0 ← (byte) get::return_x#2 + to:main::@2 +main::@2: scope:[main] from main + [7] (byte) main::$0_x ← (byte) get::return_x#0 + [8] *((byte*)(const struct Point*) SCREEN#0) ← (byte) main::$0_x + [9] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (const byte) get::p_y#0 + to:main::@1 +main::@1: scope:[main] from main::@2 main::@3 + [10] (byte) main::i#2 ← phi( main::@2/(byte) 1 main::@3/(byte) main::i#1 ) + [11] (byte) get::i#1 ← (byte) main::i#2 + [12] call get + [13] (byte) get::return_x#1 ← (byte) get::return_x#2 + to:main::@3 +main::@3: scope:[main] from main::@1 + [14] (byte) main::$1_x ← (byte) get::return_x#1 + [15] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 + [16] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x + [17] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (const byte) get::p_y#0 + [18] (byte) main::i#1 ← ++ (byte) main::i#2 + [19] if((byte) main::i#1!=(byte) 3) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@3 + [20] return + to:@return +get: scope:[get] from main main::@1 + [21] (byte) get::return_x#2 ← phi( main/(byte) 0 main::@1/(byte) get::i#1 ) + to:get::@return +get::@return: scope:[get] from get + [22] return + to:@return diff --git a/src/test/ref/problem-struct-return-to-pointer.log b/src/test/ref/problem-struct-return-to-pointer.log new file mode 100644 index 000000000..c9b1021df --- /dev/null +++ b/src/test/ref/problem-struct-return-to-pointer.log @@ -0,0 +1,800 @@ +Fixing pointer array-indexing *((struct Point*) SCREEN + (byte) main::i) +Created struct value member variable (byte) main::$0_x +Created struct value member variable (byte) main::$0_y +Converted struct value to member variables (struct Point~) main::$0 +Created struct value member variable (byte) main::$1_x +Created struct value member variable (byte) main::$1_y +Converted struct value to member variables (struct Point~) main::$1 +Created struct value member variable (byte) get::return_x +Created struct value member variable (byte) get::return_y +Converted struct value to member variables (struct Point) get::return +Created struct value member variable (byte) get::p_x +Created struct value member variable (byte) get::p_y +Converted struct value to member variables (struct Point) get::p +Converted procedure call LValue to member unwinding { (byte) main::$0_x, (byte) main::$0_y } ← call get (number) 0 +Adding struct value member variable copy *((byte*) main::$4) ← (byte) main::$0_x +Adding struct value member variable copy *((byte*) main::$5) ← (byte) main::$0_y +Converted procedure call LValue to member unwinding { (byte) main::$1_x, (byte) main::$1_y } ← call get (byte) main::i +Adding struct value member variable copy *((byte*) main::$6 + (byte~) main::$3) ← (byte) main::$1_x +Adding struct value member variable copy *((byte*) main::$7 + (byte~) main::$3) ← (byte) main::$1_y +Adding struct value list initializer (byte) get::p_x ← (byte) get::i +Adding struct value list initializer (byte) get::p_y ← (number) 7 +Adding struct value member variable copy (byte) get::return_x ← (byte) get::p_x +Adding struct value member variable copy (byte) get::return_y ← (byte) get::p_y +Adding struct value member variable copy (byte) get::return_x ← (byte) get::return_x +Adding struct value member variable copy (byte) get::return_y ← (byte) get::return_y +Converted procedure struct return value to member unwinding return { (byte) get::return_x, (byte) get::return_y } +Identified constant variable (byte) idx +Identified constant variable (byte) get::p_y +Culled Empty Block (label) main::@2 +Culled Empty Block (label) @1 +Culled Empty Block (label) get::@1 +Unwinding list assignment { (byte) main::$0_x, (byte) main::$0_y } ← { (byte) get::return_x, (byte) get::return_y } +Unwinding list assignment { (byte) main::$1_x, (byte) main::$1_y } ← { (byte) get::return_x, (byte) get::return_y } +Unwinding list assignment { (byte) get::return_x#0, (byte) get::return_y#0 } ← { (byte) get::return_x#3, (byte) get::return_y#3 } +Unwinding list assignment { (byte) get::return_x#1, (byte) get::return_y#1 } ← { (byte) get::return_x#3, (byte) get::return_y#3 } +Adding versioned struct unwinding for (struct Point) get::return#0 +Adding versioned struct unwinding for (struct Point) get::return#1 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (struct Point*) SCREEN#0 ← ((struct Point*)) (number) $400 + to:@2 +main: scope:[main] from @2 + (byte) get::i#0 ← (number) 0 + call get + (byte) get::return_x#0 ← (byte) get::return_x#3 + (byte) get::return_y#0 ← (byte) get::return_y#3 + to:main::@3 +main::@3: scope:[main] from main + (byte) get::return_y#4 ← phi( main/(byte) get::return_y#0 ) + (byte) get::return_x#4 ← phi( main/(byte) get::return_x#0 ) + (byte) main::$0_x ← (byte) get::return_x#4 + (byte) main::$0_y ← (byte) get::return_y#4 + (byte*) main::$4 ← (byte*)(struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::$4) ← (byte) main::$0_x + (byte*) main::$5 ← (byte*)(struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::$5) ← (byte) main::$0_y + (byte) main::i#0 ← (byte) 1 + to:main::@1 +main::@1: scope:[main] from main::@3 main::@4 + (byte) main::i#2 ← phi( main::@3/(byte) main::i#0 main::@4/(byte) main::i#1 ) + (byte) get::i#1 ← (byte) main::i#2 + call get + (byte) get::return_x#1 ← (byte) get::return_x#3 + (byte) get::return_y#1 ← (byte) get::return_y#3 + to:main::@4 +main::@4: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + (byte) get::return_y#5 ← phi( main::@1/(byte) get::return_y#1 ) + (byte) get::return_x#5 ← phi( main::@1/(byte) get::return_x#1 ) + (byte) main::$1_x ← (byte) get::return_x#5 + (byte) main::$1_y ← (byte) get::return_y#5 + (byte~) main::$3 ← (byte) main::i#3 * (const byte) SIZEOF_STRUCT_POINT + (byte*) main::$6 ← (byte*)(struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::$6 + (byte~) main::$3) ← (byte) main::$1_x + (byte*) main::$7 ← (byte*)(struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::$7 + (byte~) main::$3) ← (byte) main::$1_y + (byte) main::i#1 ← (byte) main::i#3 + rangenext(1,2) + (bool~) main::$2 ← (byte) main::i#1 != rangelast(1,2) + if((bool~) main::$2) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@4 + return + to:@return +get: scope:[get] from main main::@1 + (byte) get::i#2 ← phi( main/(byte) get::i#0 main::@1/(byte) get::i#1 ) + (byte) get::p_x#0 ← (byte) get::i#2 + (byte) get::p_y#0 ← (number) 7 + (byte) get::return_x#2 ← (byte) get::p_x#0 + (byte) get::return_y#2 ← (byte) get::p_y#0 + (struct Point) get::return#0 ← struct-unwound {(byte) get::return_x#2, (byte) get::return_y#2} + to:get::@return +get::@return: scope:[get] from get + (byte) get::return_y#6 ← phi( get/(byte) get::return_y#2 ) + (byte) get::return_x#6 ← phi( get/(byte) get::return_x#2 ) + (byte) get::return_x#3 ← (byte) get::return_x#6 + (byte) get::return_y#3 ← (byte) get::return_y#6 + (struct Point) get::return#1 ← struct-unwound {(byte) get::return_x#3, (byte) get::return_y#3} + return + to:@return +@2: scope:[] from @begin + call main + to:@3 +@3: scope:[] from @2 + to:@end +@end: scope:[] from @3 + +SYMBOL TABLE SSA +(label) @2 +(label) @3 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_X = (byte) 0 +(const byte) OFFSET_STRUCT_POINT_Y = (byte) 1 +(byte) Point::x +(byte) Point::y +(struct Point*) SCREEN +(struct Point*) SCREEN#0 +(const byte) SIZEOF_STRUCT_POINT = (byte) 2 +(struct Point()) get((byte) get::i) +(label) get::@return +(byte) get::i +(byte) get::i#0 +(byte) get::i#1 +(byte) get::i#2 +(byte) get::p_x +(byte) get::p_x#0 +(byte) get::p_y +(byte) get::p_y#0 +(struct Point) get::return +(struct Point) get::return#0 +(struct Point) get::return#1 +(byte) get::return_x +(byte) get::return_x#0 +(byte) get::return_x#1 +(byte) get::return_x#2 +(byte) get::return_x#3 +(byte) get::return_x#4 +(byte) get::return_x#5 +(byte) get::return_x#6 +(byte) get::return_y +(byte) get::return_y#0 +(byte) get::return_y#1 +(byte) get::return_y#2 +(byte) get::return_y#3 +(byte) get::return_y#4 +(byte) get::return_y#5 +(byte) get::return_y#6 +(void()) main() +(struct Point~) main::$0 +(byte) main::$0_x +(byte) main::$0_y +(struct Point~) main::$1 +(byte) main::$1_x +(byte) main::$1_y +(bool~) main::$2 +(byte~) main::$3 +(byte*) main::$4 +(byte*) main::$5 +(byte*) main::$6 +(byte*) main::$7 +(label) main::@1 +(label) main::@3 +(label) main::@4 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 + +Adding number conversion cast (unumber) 0 in (byte) get::i#0 ← (number) 0 +Adding number conversion cast (unumber) 7 in (byte) get::p_y#0 ← (number) 7 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (struct Point*) SCREEN#0 ← (struct Point*)(number) $400 +Inlining cast (byte) get::i#0 ← (unumber)(number) 0 +Inlining cast (byte) get::p_y#0 ← (unumber)(number) 7 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (struct Point*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 7 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 7 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) get::return_x#0 = (byte) get::return_x#4 +Alias (byte) get::return_y#0 = (byte) get::return_y#4 +Alias (byte) get::return_x#1 = (byte) get::return_x#5 +Alias (byte) get::return_y#1 = (byte) get::return_y#5 +Alias (byte) main::i#2 = (byte) main::i#3 +Alias (byte) get::return_x#2 = (byte) get::p_x#0 (byte) get::i#2 (byte) get::return_x#6 (byte) get::return_x#3 +Alias (byte) get::p_y#0 = (byte) get::return_y#2 (byte) get::return_y#6 (byte) get::return_y#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$2 [28] if((byte) main::i#1!=rangelast(1,2)) goto main::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const struct Point*) SCREEN#0 = (struct Point*) 1024 +Constant (const byte) get::i#0 = 0 +Constant (const byte) main::i#0 = 1 +Constant (const byte) get::p_y#0 = 7 +Successful SSA optimization Pass2ConstantIdentification +Constant (const byte) get::return_y#0 = get::p_y#0 +Constant (const byte) get::return_y#1 = get::p_y#0 +Successful SSA optimization Pass2ConstantIdentification +Constant (const byte) main::$0_y = get::return_y#0 +Constant (const byte) main::$1_y = get::return_y#1 +Successful SSA optimization Pass2ConstantIdentification +Constant value identified (byte*)SCREEN#0 in [8] (byte*) main::$4 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)SCREEN#0 in [10] (byte*) main::$5 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant value identified (byte*)SCREEN#0 in [22] (byte*) main::$6 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)SCREEN#0 in [24] (byte*) main::$7 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantValues +Resolved ranged next value [26] main::i#1 ← ++ main::i#2 to ++ +Resolved ranged comparison value [28] if(main::i#1!=rangelast(1,2)) goto main::@1 to (number) 3 +Converting *(pointer+n) to pointer[n] [9] *((byte*) main::$4) ← (byte) main::$0_x -- *((byte*)SCREEN#0 + OFFSET_STRUCT_POINT_X) +Converting *(pointer+n) to pointer[n] [11] *((byte*) main::$5) ← (const byte) main::$0_y -- *((byte*)SCREEN#0 + OFFSET_STRUCT_POINT_Y) +Successful SSA optimization Pass2InlineDerefIdx +Simplifying expression containing zero (byte*)SCREEN#0 in [8] (byte*) main::$4 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)SCREEN#0 in [9] *((byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X) ← (byte) main::$0_x +Simplifying expression containing zero (byte*)SCREEN#0 in [22] (byte*) main::$6 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable (byte*) main::$4 and assignment [3] (byte*) main::$4 ← (byte*)(const struct Point*) SCREEN#0 +Eliminating unused variable (byte*) main::$5 and assignment [5] (byte*) main::$5 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y +Eliminating unused variable (struct Point) get::return#0 and assignment [21] (struct Point) get::return#0 ← struct-unwound {(byte) get::return_x#2, (const byte) get::p_y#0} +Eliminating unused variable (struct Point) get::return#1 and assignment [22] (struct Point) get::return#1 ← struct-unwound {(byte) get::return_x#2, (const byte) get::p_y#0} +Eliminating unused constant (const byte) OFFSET_STRUCT_POINT_X +Successful SSA optimization PassNEliminateUnusedVars +Adding number conversion cast (unumber) 3 in if((byte) main::i#1!=(number) 3) goto main::@1 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 3 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 3 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Constant right-side identified [13] (byte*) main::$7 ← (byte*)(const struct Point*) SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte*) main::$6 = (byte*)SCREEN#0 +Constant (const byte*) main::$7 = (byte*)SCREEN#0+OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantIdentification +Rewriting multiplication to use shift [10] (byte~) main::$3 ← (byte) main::i#2 * (const byte) SIZEOF_STRUCT_POINT +Successful SSA optimization Pass2MultiplyToShiftRewriting +Inlining constant with var siblings (const byte) main::i#0 +Inlining constant with var siblings (const byte) get::i#0 +Constant inlined main::$1_y = (const byte) get::p_y#0 +Constant inlined main::$0_y = (const byte) get::p_y#0 +Constant inlined main::i#0 = (byte) 1 +Constant inlined main::$6 = (byte*)(const struct Point*) SCREEN#0 +Constant inlined get::return_y#0 = (const byte) get::p_y#0 +Constant inlined get::i#0 = (byte) 0 +Constant inlined get::return_y#1 = (const byte) get::p_y#0 +Constant inlined main::$7 = (byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *((byte*)SCREEN#0+OFFSET_STRUCT_POINT_Y) +Successful SSA optimization Pass2ConstantAdditionElimination +Eliminating unused constant (const byte) SIZEOF_STRUCT_POINT +Successful SSA optimization PassNEliminateUnusedVars +Added new block during phi lifting main::@5(between main::@4 and main::@1) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @3 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +CALL GRAPH +Calls in [] to main:2 +Calls in [main] to get:6 get:14 + +Created 2 initial phi equivalence classes +Coalesced [13] get::return_x#7 ← get::i#1 +Coalesced [23] main::i#4 ← main::i#1 +Coalesced down to 2 phi equivalence classes +Culled Empty Block (label) @3 +Culled Empty Block (label) main::@5 +Renumbering block @2 to @1 +Renumbering block main::@3 to main::@2 +Renumbering block main::@4 to main::@3 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + [5] call get + [6] (byte) get::return_x#0 ← (byte) get::return_x#2 + to:main::@2 +main::@2: scope:[main] from main + [7] (byte) main::$0_x ← (byte) get::return_x#0 + [8] *((byte*)(const struct Point*) SCREEN#0) ← (byte) main::$0_x + [9] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (const byte) get::p_y#0 + to:main::@1 +main::@1: scope:[main] from main::@2 main::@3 + [10] (byte) main::i#2 ← phi( main::@2/(byte) 1 main::@3/(byte) main::i#1 ) + [11] (byte) get::i#1 ← (byte) main::i#2 + [12] call get + [13] (byte) get::return_x#1 ← (byte) get::return_x#2 + to:main::@3 +main::@3: scope:[main] from main::@1 + [14] (byte) main::$1_x ← (byte) get::return_x#1 + [15] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 + [16] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x + [17] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (const byte) get::p_y#0 + [18] (byte) main::i#1 ← ++ (byte) main::i#2 + [19] if((byte) main::i#1!=(byte) 3) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@3 + [20] return + to:@return +get: scope:[get] from main main::@1 + [21] (byte) get::return_x#2 ← phi( main/(byte) 0 main::@1/(byte) get::i#1 ) + to:get::@return +get::@return: scope:[get] from get + [22] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte) Point::x +(byte) Point::y +(struct Point*) SCREEN +(struct Point()) get((byte) get::i) +(byte) get::i +(byte) get::i#1 22.0 +(byte) get::p_x +(byte) get::p_y +(struct Point) get::return +(byte) get::return_x +(byte) get::return_x#0 4.0 +(byte) get::return_x#1 22.0 +(byte) get::return_x#2 6.0 +(byte) get::return_y +(void()) main() +(struct Point~) main::$0 +(byte) main::$0_x 4.0 +(struct Point~) main::$1 +(byte) main::$1_x 11.0 +(byte~) main::$3 16.5 +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#2 5.5 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ get::return_x#2 get::i#1 ] +Added variable get::return_x#0 to zero page equivalence class [ get::return_x#0 ] +Added variable main::$0_x to zero page equivalence class [ main::$0_x ] +Added variable get::return_x#1 to zero page equivalence class [ get::return_x#1 ] +Added variable main::$1_x to zero page equivalence class [ main::$1_x ] +Added variable main::$3 to zero page equivalence class [ main::$3 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ get::return_x#2 get::i#1 ] +[ get::return_x#0 ] +[ main::$0_x ] +[ get::return_x#1 ] +[ main::$1_x ] +[ main::$3 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Allocated zp ZP_BYTE:3 [ get::return_x#2 get::i#1 ] +Allocated zp ZP_BYTE:4 [ get::return_x#0 ] +Allocated zp ZP_BYTE:5 [ main::$0_x ] +Allocated zp ZP_BYTE:6 [ get::return_x#1 ] +Allocated zp ZP_BYTE:7 [ main::$1_x ] +Allocated zp ZP_BYTE:8 [ main::$3 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// Demonstrates problem with returning a struct into a dereferenced pointer to a struct + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label _3 = 8 + .label _0_x = 5 + .label _1_x = 7 + .label i = 2 + // [5] call get + // [21] phi from main to get [phi:main->get] + get_from_main: + // [21] phi (byte) get::return_x#2 = (byte) 0 [phi:main->get#0] -- vbuz1=vbuc1 + lda #0 + sta get.return_x_2 + jsr get + // [6] (byte) get::return_x#0 ← (byte) get::return_x#2 -- vbuz1=vbuz2 + lda get.return_x_2 + sta get.return_x + jmp b2 + // main::@2 + b2: + // [7] (byte) main::$0_x ← (byte) get::return_x#0 -- vbuz1=vbuz2 + lda get.return_x + sta _0_x + // [8] *((byte*)(const struct Point*) SCREEN#0) ← (byte) main::$0_x -- _deref_pbuc1=vbuz1 + lda _0_x + sta SCREEN + // [9] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (const byte) get::p_y#0 -- _deref_pbuc1=vbuc2 + lda #get.p_y + sta SCREEN+OFFSET_STRUCT_POINT_Y + // [10] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [10] phi (byte) main::i#2 = (byte) 1 [phi:main::@2->main::@1#0] -- vbuz1=vbuc1 + lda #1 + sta i + jmp b1 + // [10] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + // [10] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 + // main::@1 + b1: + // [11] (byte) get::i#1 ← (byte) main::i#2 -- vbuz1=vbuz2 + lda i + sta get.i + // [12] call get + // [21] phi from main::@1 to get [phi:main::@1->get] + get_from_b1: + // [21] phi (byte) get::return_x#2 = (byte) get::i#1 [phi:main::@1->get#0] -- register_copy + jsr get + // [13] (byte) get::return_x#1 ← (byte) get::return_x#2 -- vbuz1=vbuz2 + lda get.return_x_2 + sta get.return_x_1 + jmp b3 + // main::@3 + b3: + // [14] (byte) main::$1_x ← (byte) get::return_x#1 -- vbuz1=vbuz2 + lda get.return_x_1 + sta _1_x + // [15] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 -- vbuz1=vbuz2_rol_1 + lda i + asl + sta _3 + // [16] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x -- pbuc1_derefidx_vbuz1=vbuz2 + lda _1_x + ldy _3 + sta SCREEN,y + // [17] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (const byte) get::p_y#0 -- pbuc1_derefidx_vbuz1=vbuc2 + lda #get.p_y + ldy _3 + sta SCREEN+OFFSET_STRUCT_POINT_Y,y + // [18] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + // [19] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #3 + cmp i + bne b1_from_b3 + jmp breturn + // main::@return + breturn: + // [20] return + rts +} + // get +// get(byte zeropage(3) i) +get: { + .label p_y = 7 + .label return_x = 4 + .label i = 3 + .label return_x_1 = 6 + .label return_x_2 = 3 + jmp breturn + // get::@return + breturn: + // [22] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [9] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (const byte) get::p_y#0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [15] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 [ main::i#2 main::$1_x main::$3 ] ( main:2 [ main::i#2 main::$1_x main::$3 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:7 [ main::$1_x ] +Statement [16] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x [ main::i#2 main::$3 ] ( main:2 [ main::i#2 main::$3 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:8 [ main::$3 ] +Statement [17] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (const byte) get::p_y#0 [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Statement [9] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (const byte) get::p_y#0 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [15] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 [ main::i#2 main::$1_x main::$3 ] ( main:2 [ main::i#2 main::$1_x main::$3 ] ) always clobbers reg byte a +Statement [16] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x [ main::i#2 main::$3 ] ( main:2 [ main::i#2 main::$3 ] ) always clobbers reg byte a +Statement [17] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (const byte) get::p_y#0 [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:3 [ get::return_x#2 get::i#1 ] : zp ZP_BYTE:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:4 [ get::return_x#0 ] : zp ZP_BYTE:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:5 [ main::$0_x ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:6 [ get::return_x#1 ] : zp ZP_BYTE:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:7 [ main::$1_x ] : zp ZP_BYTE:7 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:8 [ main::$3 ] : zp ZP_BYTE:8 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [get] 28: zp ZP_BYTE:3 [ get::return_x#2 get::i#1 ] 22: zp ZP_BYTE:6 [ get::return_x#1 ] 4: zp ZP_BYTE:4 [ get::return_x#0 ] +Uplift Scope [main] 22: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 16.5: zp ZP_BYTE:8 [ main::$3 ] 11: zp ZP_BYTE:7 [ main::$1_x ] 4: zp ZP_BYTE:5 [ main::$0_x ] +Uplift Scope [Point] +Uplift Scope [] + +Uplifting [get] best 732 combination reg byte a [ get::return_x#2 get::i#1 ] reg byte a [ get::return_x#1 ] reg byte a [ get::return_x#0 ] +Uplifting [main] best 546 combination reg byte y [ main::i#2 main::i#1 ] reg byte x [ main::$3 ] zp ZP_BYTE:7 [ main::$1_x ] reg byte a [ main::$0_x ] +Limited combination testing to 100 combinations of 108 possible. +Uplifting [Point] best 546 combination +Uplifting [] best 546 combination +Attempting to uplift remaining variables inzp ZP_BYTE:7 [ main::$1_x ] +Uplifting [main] best 546 combination zp ZP_BYTE:7 [ main::$1_x ] +Allocated (was zp ZP_BYTE:7) zp ZP_BYTE:2 [ main::$1_x ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Demonstrates problem with returning a struct into a dereferenced pointer to a struct + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label _1_x = 2 + // [5] call get + // [21] phi from main to get [phi:main->get] + get_from_main: + // [21] phi (byte) get::return_x#2 = (byte) 0 [phi:main->get#0] -- vbuaa=vbuc1 + lda #0 + jsr get + // [6] (byte) get::return_x#0 ← (byte) get::return_x#2 + jmp b2 + // main::@2 + b2: + // [7] (byte) main::$0_x ← (byte) get::return_x#0 + // [8] *((byte*)(const struct Point*) SCREEN#0) ← (byte) main::$0_x -- _deref_pbuc1=vbuaa + sta SCREEN + // [9] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (const byte) get::p_y#0 -- _deref_pbuc1=vbuc2 + lda #get.p_y + sta SCREEN+OFFSET_STRUCT_POINT_Y + // [10] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [10] phi (byte) main::i#2 = (byte) 1 [phi:main::@2->main::@1#0] -- vbuyy=vbuc1 + ldy #1 + jmp b1 + // [10] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + // [10] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 + // main::@1 + b1: + // [11] (byte) get::i#1 ← (byte) main::i#2 -- vbuaa=vbuyy + tya + // [12] call get + // [21] phi from main::@1 to get [phi:main::@1->get] + get_from_b1: + // [21] phi (byte) get::return_x#2 = (byte) get::i#1 [phi:main::@1->get#0] -- register_copy + jsr get + // [13] (byte) get::return_x#1 ← (byte) get::return_x#2 + jmp b3 + // main::@3 + b3: + // [14] (byte) main::$1_x ← (byte) get::return_x#1 -- vbuz1=vbuaa + sta _1_x + // [15] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 -- vbuxx=vbuyy_rol_1 + tya + asl + tax + // [16] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x -- pbuc1_derefidx_vbuxx=vbuz1 + lda _1_x + sta SCREEN,x + // [17] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (const byte) get::p_y#0 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #get.p_y + sta SCREEN+OFFSET_STRUCT_POINT_Y,x + // [18] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuyy=_inc_vbuyy + iny + // [19] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuyy_neq_vbuc1_then_la1 + cpy #3 + bne b1_from_b3 + jmp breturn + // main::@return + breturn: + // [20] return + rts +} + // get +// get(byte register(A) i) +get: { + .label p_y = 7 + jmp breturn + // get::@return + breturn: + // [22] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b2 +Removing instruction jmp b1 +Removing instruction jmp b3 +Removing instruction jmp breturn +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b1_from_b3 with b1 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b3: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction get_from_main: +Removing instruction b2: +Removing instruction b1_from_b2: +Removing instruction get_from_b1: +Removing instruction b3: +Removing instruction breturn: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction jmp b1 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_Y OFFSET_STRUCT_POINT_Y = (byte) 1 +(byte) Point::x +(byte) Point::y +(struct Point*) SCREEN +(const struct Point*) SCREEN#0 SCREEN = (struct Point*) 1024 +(struct Point()) get((byte) get::i) +(label) get::@return +(byte) get::i +(byte) get::i#1 reg byte a 22.0 +(byte) get::p_x +(byte) get::p_y +(const byte) get::p_y#0 p_y = (byte) 7 +(struct Point) get::return +(byte) get::return_x +(byte) get::return_x#0 reg byte a 4.0 +(byte) get::return_x#1 reg byte a 22.0 +(byte) get::return_x#2 reg byte a 6.0 +(byte) get::return_y +(void()) main() +(struct Point~) main::$0 +(byte) main::$0_x reg byte a 4.0 +(struct Point~) main::$1 +(byte) main::$1_x $1_x zp ZP_BYTE:2 11.0 +(byte~) main::$3 reg byte x 16.5 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte y 16.5 +(byte) main::i#2 reg byte y 5.5 + +reg byte y [ main::i#2 main::i#1 ] +reg byte a [ get::return_x#2 get::i#1 ] +reg byte a [ get::return_x#0 ] +reg byte a [ main::$0_x ] +reg byte a [ get::return_x#1 ] +zp ZP_BYTE:2 [ main::$1_x ] +reg byte x [ main::$3 ] + + +FINAL ASSEMBLER +Score: 381 + + // File Comments +// Demonstrates problem with returning a struct into a dereferenced pointer to a struct + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + .label _1_x = 2 + // get(0) + // [5] call get + // [21] phi from main to get [phi:main->get] + // [21] phi (byte) get::return_x#2 = (byte) 0 [phi:main->get#0] -- vbuaa=vbuc1 + lda #0 + jsr get + // get(0) + // [6] (byte) get::return_x#0 ← (byte) get::return_x#2 + // main::@2 + // [7] (byte) main::$0_x ← (byte) get::return_x#0 + // *SCREEN = get(0) + // [8] *((byte*)(const struct Point*) SCREEN#0) ← (byte) main::$0_x -- _deref_pbuc1=vbuaa + sta SCREEN + // [9] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (const byte) get::p_y#0 -- _deref_pbuc1=vbuc2 + lda #get.p_y + sta SCREEN+OFFSET_STRUCT_POINT_Y + // [10] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [10] phi (byte) main::i#2 = (byte) 1 [phi:main::@2->main::@1#0] -- vbuyy=vbuc1 + ldy #1 + // [10] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + // [10] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + // main::@1 + b1: + // get(i) + // [11] (byte) get::i#1 ← (byte) main::i#2 -- vbuaa=vbuyy + tya + // [12] call get + // [21] phi from main::@1 to get [phi:main::@1->get] + // [21] phi (byte) get::return_x#2 = (byte) get::i#1 [phi:main::@1->get#0] -- register_copy + jsr get + // get(i) + // [13] (byte) get::return_x#1 ← (byte) get::return_x#2 + // main::@3 + // [14] (byte) main::$1_x ← (byte) get::return_x#1 -- vbuz1=vbuaa + sta _1_x + // SCREEN[i] = get(i) + // [15] (byte~) main::$3 ← (byte) main::i#2 << (byte) 1 -- vbuxx=vbuyy_rol_1 + tya + asl + tax + // [16] *((byte*)(const struct Point*) SCREEN#0 + (byte~) main::$3) ← (byte) main::$1_x -- pbuc1_derefidx_vbuxx=vbuz1 + lda _1_x + sta SCREEN,x + // [17] *((byte*)(const struct Point*) SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← (const byte) get::p_y#0 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #get.p_y + sta SCREEN+OFFSET_STRUCT_POINT_Y,x + // for ( char i: 1..2) + // [18] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuyy=_inc_vbuyy + iny + // [19] if((byte) main::i#1!=(byte) 3) goto main::@1 -- vbuyy_neq_vbuc1_then_la1 + cpy #3 + bne b1 + // main::@return + // } + // [20] return + rts +} + // get +// get(byte register(A) i) +get: { + .label p_y = 7 + // get::@return + // [22] return + rts +} + // File Data + diff --git a/src/test/ref/problem-struct-return-to-pointer.sym b/src/test/ref/problem-struct-return-to-pointer.sym new file mode 100644 index 000000000..ec97501b2 --- /dev/null +++ b/src/test/ref/problem-struct-return-to-pointer.sym @@ -0,0 +1,42 @@ +(label) @1 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_Y OFFSET_STRUCT_POINT_Y = (byte) 1 +(byte) Point::x +(byte) Point::y +(struct Point*) SCREEN +(const struct Point*) SCREEN#0 SCREEN = (struct Point*) 1024 +(struct Point()) get((byte) get::i) +(label) get::@return +(byte) get::i +(byte) get::i#1 reg byte a 22.0 +(byte) get::p_x +(byte) get::p_y +(const byte) get::p_y#0 p_y = (byte) 7 +(struct Point) get::return +(byte) get::return_x +(byte) get::return_x#0 reg byte a 4.0 +(byte) get::return_x#1 reg byte a 22.0 +(byte) get::return_x#2 reg byte a 6.0 +(byte) get::return_y +(void()) main() +(struct Point~) main::$0 +(byte) main::$0_x reg byte a 4.0 +(struct Point~) main::$1 +(byte) main::$1_x $1_x zp ZP_BYTE:2 11.0 +(byte~) main::$3 reg byte x 16.5 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte y 16.5 +(byte) main::i#2 reg byte y 5.5 + +reg byte y [ main::i#2 main::i#1 ] +reg byte a [ get::return_x#2 get::i#1 ] +reg byte a [ get::return_x#0 ] +reg byte a [ main::$0_x ] +reg byte a [ get::return_x#1 ] +zp ZP_BYTE:2 [ main::$1_x ] +reg byte x [ main::$3 ]