diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 66502f30a..25a220e10 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -85,12 +85,10 @@ public class TestPrograms { compileAndCompare("problem-struct-pointer-param"); } - /* @Test public void testProblemArrayStructParam() throws IOException, URISyntaxException { compileAndCompare("problem-array-struct-param"); } - */ /* @Test diff --git a/src/test/ref/problem-array-struct-param.asm b/src/test/ref/problem-array-struct-param.asm new file mode 100644 index 000000000..0a3a492e2 --- /dev/null +++ b/src/test/ref/problem-array-struct-param.asm @@ -0,0 +1,49 @@ +// Demonstrates problem with passing struct array element as parameter to call +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + .label idx = 2 +main: { + lda #1 + sta points + lda #2 + sta points+OFFSET_STRUCT_POINT_Y + lda #3 + sta points+1*SIZEOF_STRUCT_POINT + lda #4 + sta points+OFFSET_STRUCT_POINT_Y+1*SIZEOF_STRUCT_POINT + lda #0 + sta idx + tax + b1: + txa + asl + tay + lda points,y + sta print.p_x + lda points+OFFSET_STRUCT_POINT_Y,y + sta print.p_y + jsr print + inx + cpx #2 + bne b1 + rts +} +// print(byte zeropage(3) p_x, byte zeropage(4) p_y) +print: { + .label p_x = 3 + .label p_y = 4 + lda p_x + ldy idx + sta SCREEN,y + iny + lda p_y + sta SCREEN,y + iny + sty idx + rts +} + points: .fill 2*2, 0 diff --git a/src/test/ref/problem-array-struct-param.cfg b/src/test/ref/problem-array-struct-param.cfg new file mode 100644 index 000000000..93fd16ad5 --- /dev/null +++ b/src/test/ref/problem-array-struct-param.cfg @@ -0,0 +1,39 @@ +@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] *((byte*)(const struct Point[2]) points#0) ← (byte) 1 + [5] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) 2 + [6] *((byte*)(const struct Point[2]) points#0+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 3 + [7] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 4 + to:main::@1 +main::@1: scope:[main] from main main::@2 + [8] (byte) idx#12 ← phi( main/(byte) 0 main::@2/(byte) idx#10 ) + [8] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [9] (byte~) main::$4 ← (byte) main::i#2 << (byte) 1 + [10] (byte) print::p_x#0 ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$4) + [11] (byte) print::p_y#0 ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$4) + [12] call print + to:main::@2 +main::@2: scope:[main] from main::@1 + [13] (byte) main::i#1 ← ++ (byte) main::i#2 + [14] if((byte) main::i#1!=(byte) 2) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@2 + [15] return + to:@return +print: scope:[print] from main::@1 + [16] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) print::p_x#0 + [17] (byte) idx#3 ← ++ (byte) idx#12 + [18] *((const byte*) SCREEN#0 + (byte) idx#3) ← (byte) print::p_y#0 + [19] (byte) idx#10 ← ++ (byte) idx#3 + to:print::@return +print::@return: scope:[print] from print + [20] return + to:@return diff --git a/src/test/ref/problem-array-struct-param.log b/src/test/ref/problem-array-struct-param.log new file mode 100644 index 000000000..c75610e7e --- /dev/null +++ b/src/test/ref/problem-array-struct-param.log @@ -0,0 +1,804 @@ +Fixing pointer array-indexing *((struct Point[2]) points + (number) 0) +Fixing pointer array-indexing *((struct Point[2]) points + (number) 1) +Fixing pointer array-indexing *((struct Point[2]) points + (byte) main::i) +Created struct value member variable (byte) print::p_x +Created struct value member variable (byte) print::p_y +Converted struct value to member variables (struct Point) print::p +Converted procedure struct value parameter to member variables (void()) print((byte) print::p_x , (byte) print::p_y) +Adding struct value list initializer *((byte*) main::$5 + (number~) main::$2) ← (number) 1 +Adding struct value list initializer *((byte*) main::$6 + (number~) main::$2) ← (number) 2 +Adding struct value list initializer *((byte*) main::$7 + (number~) main::$3) ← (number) 3 +Adding struct value list initializer *((byte*) main::$8 + (number~) main::$3) ← (number) 4 +Converted procedure struct value parameter to member variables in call (void~) main::$0 ← call print *((byte*) main::$9 + (byte~) main::$4) *((byte*) main::$10 + (byte~) main::$4) +Replacing struct member reference (struct Point) print::p.x with member variable reference (byte) print::p_x +Replacing struct member reference (struct Point) print::p.y with member variable reference (byte) print::p_y +Culled Empty Block (label) main::@2 +Culled Empty Block (label) @1 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte*) SCREEN#0 ← ((byte*)) (number) $400 + (byte) idx#0 ← (number) 0 + (struct Point[2]) points#0 ← { fill( 2, 0) } + to:@2 +main: scope:[main] from @2 + (byte) idx#14 ← phi( @2/(byte) idx#13 ) + (number~) main::$2 ← (number) 0 * (const byte) SIZEOF_STRUCT_POINT + (byte*) main::$5 ← (byte*)(struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::$5 + (number~) main::$2) ← (number) 1 + (byte*) main::$6 ← (byte*)(struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::$6 + (number~) main::$2) ← (number) 2 + (number~) main::$3 ← (number) 1 * (const byte) SIZEOF_STRUCT_POINT + (byte*) main::$7 ← (byte*)(struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::$7 + (number~) main::$3) ← (number) 3 + (byte*) main::$8 ← (byte*)(struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::$8 + (number~) main::$3) ← (number) 4 + (byte) main::i#0 ← (byte) 0 + to:main::@1 +main::@1: scope:[main] from main main::@3 + (byte) idx#12 ← phi( main/(byte) idx#14 main::@3/(byte) idx#1 ) + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@3/(byte) main::i#1 ) + (byte~) main::$4 ← (byte) main::i#2 * (const byte) SIZEOF_STRUCT_POINT + (byte) print::p_x#0 ← *((byte*) main::$9 + (byte~) main::$4) + (byte) print::p_y#0 ← *((byte*) main::$10 + (byte~) main::$4) + call print + to:main::@3 +main::@3: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + (byte) idx#7 ← phi( main::@1/(byte) idx#5 ) + (byte) idx#1 ← (byte) idx#7 + (byte*) main::$9 ← (byte*)(struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X + (byte*) main::$10 ← (byte*)(struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y + (byte) main::i#1 ← (byte) main::i#3 + rangenext(0,1) + (bool~) main::$1 ← (byte) main::i#1 != rangelast(0,1) + if((bool~) main::$1) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@3 + (byte) idx#8 ← phi( main::@3/(byte) idx#1 ) + (byte) idx#2 ← (byte) idx#8 + return + to:@return +print: scope:[print] from main::@1 + (byte) print::p_y#1 ← phi( main::@1/(byte) print::p_y#0 ) + (byte) idx#9 ← phi( main::@1/(byte) idx#12 ) + (byte) print::p_x#1 ← phi( main::@1/(byte) print::p_x#0 ) + *((byte*) SCREEN#0 + (byte) idx#9) ← (byte) print::p_x#1 + (byte) idx#3 ← ++ (byte) idx#9 + *((byte*) SCREEN#0 + (byte) idx#3) ← (byte) print::p_y#1 + (byte) idx#4 ← ++ (byte) idx#3 + to:print::@return +print::@return: scope:[print] from print + (byte) idx#10 ← phi( print/(byte) idx#4 ) + (byte) idx#5 ← (byte) idx#10 + return + to:@return +@2: scope:[] from @begin + (byte) idx#13 ← phi( @begin/(byte) idx#0 ) + call main + to:@3 +@3: scope:[] from @2 + (byte) idx#11 ← phi( @2/(byte) idx#2 ) + (byte) idx#6 ← (byte) idx#11 + 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 +(byte*) SCREEN +(byte*) SCREEN#0 +(const byte) SIZEOF_STRUCT_POINT = (byte) 2 +(byte) idx +(byte) idx#0 +(byte) idx#1 +(byte) idx#10 +(byte) idx#11 +(byte) idx#12 +(byte) idx#13 +(byte) idx#14 +(byte) idx#2 +(byte) idx#3 +(byte) idx#4 +(byte) idx#5 +(byte) idx#6 +(byte) idx#7 +(byte) idx#8 +(byte) idx#9 +(void()) main() +(bool~) main::$1 +(byte*) main::$10 +(number~) main::$2 +(number~) main::$3 +(byte~) main::$4 +(byte*) main::$5 +(byte*) main::$6 +(byte*) main::$7 +(byte*) main::$8 +(byte*) main::$9 +(label) main::@1 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(struct Point[2]) points +(struct Point[2]) points#0 +(void()) print((byte) print::p_x , (byte) print::p_y) +(label) print::@return +(struct Point) print::p +(byte) print::p_x +(byte) print::p_x#0 +(byte) print::p_x#1 +(byte) print::p_y +(byte) print::p_y#0 +(byte) print::p_y#1 + +Adding number conversion cast (unumber) 0 in (byte) idx#0 ← (number) 0 +Adding number conversion cast (unumber) 0 in (number~) main::$2 ← (number) 0 * (const byte) SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) main::$2 in (number~) main::$2 ← (unumber)(number) 0 * (const byte) SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) 1 in *((byte*) main::$5 + (unumber~) main::$2) ← (number) 1 +Adding number conversion cast (unumber) 2 in *((byte*) main::$6 + (unumber~) main::$2) ← (number) 2 +Adding number conversion cast (unumber) 1 in (number~) main::$3 ← (number) 1 * (const byte) SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) main::$3 in (number~) main::$3 ← (unumber)(number) 1 * (const byte) SIZEOF_STRUCT_POINT +Adding number conversion cast (unumber) 3 in *((byte*) main::$7 + (unumber~) main::$3) ← (number) 3 +Adding number conversion cast (unumber) 4 in *((byte*) main::$8 + (unumber~) main::$3) ← (number) 4 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400 +Inlining cast (byte) idx#0 ← (unumber)(number) 0 +Inlining cast *((byte*) main::$5 + (unumber~) main::$2) ← (unumber)(number) 1 +Inlining cast *((byte*) main::$6 + (unumber~) main::$2) ← (unumber)(number) 2 +Inlining cast *((byte*) main::$7 + (unumber~) main::$3) ← (unumber)(number) 3 +Inlining cast *((byte*) main::$8 + (unumber~) main::$3) ← (unumber)(number) 4 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Simplifying constant integer cast 2 +Simplifying constant integer cast 1 +Simplifying constant integer cast 3 +Simplifying constant integer cast 4 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 2 +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 3 +Finalized unsigned number type (byte) 4 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inferred type updated to byte in (unumber~) main::$2 ← (byte) 0 * (const byte) SIZEOF_STRUCT_POINT +Inferred type updated to byte in (unumber~) main::$3 ← (byte) 1 * (const byte) SIZEOF_STRUCT_POINT +Alias (byte) main::i#2 = (byte) main::i#3 +Alias (byte) idx#1 = (byte) idx#7 (byte) idx#8 (byte) idx#2 +Alias (byte) idx#10 = (byte) idx#4 (byte) idx#5 +Alias (byte) idx#0 = (byte) idx#13 +Alias (byte) idx#11 = (byte) idx#6 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (byte) idx#14 (byte) idx#0 +Identical Phi Values (byte) idx#1 (byte) idx#10 +Identical Phi Values (byte) print::p_x#1 (byte) print::p_x#0 +Identical Phi Values (byte) idx#9 (byte) idx#12 +Identical Phi Values (byte) print::p_y#1 (byte) print::p_y#0 +Identical Phi Values (byte) idx#11 (byte) idx#1 +Successful SSA optimization Pass2IdenticalPhiElimination +Simple Condition (bool~) main::$1 [26] if((byte) main::i#1!=rangelast(0,1)) goto main::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant right-side identified [2] (struct Point[2]) points#0 ← { fill( 2, 0) } +Constant right-side identified [4] (byte~) main::$2 ← (byte) 0 * (const byte) SIZEOF_STRUCT_POINT +Constant right-side identified [9] (byte~) main::$3 ← (byte) 1 * (const byte) SIZEOF_STRUCT_POINT +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte*) SCREEN#0 = (byte*) 1024 +Constant (const byte) idx#0 = 0 +Constant (const struct Point[2]) points#0 = { fill( 2, 0) } +Constant (const byte) main::$2 = 0*SIZEOF_STRUCT_POINT +Constant (const byte) main::$3 = 1*SIZEOF_STRUCT_POINT +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Constant value identified (byte*)points#0 in [5] (byte*) main::$5 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)points#0 in [7] (byte*) main::$6 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant value identified (byte*)points#0 in [10] (byte*) main::$7 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)points#0 in [12] (byte*) main::$8 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant value identified (byte*)points#0 in [22] (byte*) main::$9 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)points#0 in [23] (byte*) main::$10 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantValues +Resolved ranged next value [24] main::i#1 ← ++ main::i#2 to ++ +Resolved ranged comparison value [26] if(main::i#1!=rangelast(0,1)) goto main::@1 to (number) 2 +Simplifying constant evaluating to zero (byte) 0*(const byte) SIZEOF_STRUCT_POINT in +Successful SSA optimization PassNSimplifyConstantZero +Simplifying expression containing zero (byte*)points#0 in [5] (byte*) main::$5 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero main::$5 in [6] *((byte*) main::$5 + (const byte) main::$2) ← (byte) 1 +Simplifying expression containing zero main::$6 in [8] *((byte*) main::$6 + (const byte) main::$2) ← (byte) 2 +Simplifying expression containing zero (byte*)points#0 in [10] (byte*) main::$7 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)points#0 in [22] (byte*) main::$9 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant (const byte) main::$2 +Eliminating unused constant (const byte) OFFSET_STRUCT_POINT_X +Successful SSA optimization PassNEliminateUnusedVars +Adding number conversion cast (unumber) 2 in if((byte) main::i#1!=(number) 2) goto main::@1 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 2 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 2 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Constant right-side identified [2] (byte*) main::$6 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant right-side identified [6] (byte*) main::$8 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant right-side identified [14] (byte*) main::$10 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte*) main::$5 = (byte*)points#0 +Constant (const byte*) main::$6 = (byte*)points#0+OFFSET_STRUCT_POINT_Y +Constant (const byte*) main::$7 = (byte*)points#0 +Constant (const byte*) main::$8 = (byte*)points#0+OFFSET_STRUCT_POINT_Y +Constant (const byte*) main::$9 = (byte*)points#0 +Constant (const byte*) main::$10 = (byte*)points#0+OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantIdentification +Rewriting multiplication to use shift [5] (byte~) main::$4 ← (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) idx#0 +Constant inlined main::$5 = (byte*)(const struct Point[2]) points#0 +Constant inlined main::i#0 = (byte) 0 +Constant inlined main::$6 = (byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y +Constant inlined main::$3 = (byte) 1*(const byte) SIZEOF_STRUCT_POINT +Constant inlined main::$9 = (byte*)(const struct Point[2]) points#0 +Constant inlined idx#0 = (byte) 0 +Constant inlined main::$7 = (byte*)(const struct Point[2]) points#0 +Constant inlined main::$10 = (byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y +Constant inlined main::$8 = (byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *((byte*)points#0+1*SIZEOF_STRUCT_POINT) +Consolidated array index constant in *((byte*)points#0+OFFSET_STRUCT_POINT_Y+1*SIZEOF_STRUCT_POINT) +Successful SSA optimization Pass2ConstantAdditionElimination +Added new block during phi lifting main::@4(between main::@3 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 +CALL GRAPH +Calls in [] to main:2 +Calls in [main] to print:13 + +Created 2 initial phi equivalence classes +Coalesced [17] main::i#4 ← main::i#1 +Coalesced [18] idx#15 ← idx#10 +Coalesced down to 2 phi equivalence classes +Culled Empty Block (label) @3 +Culled Empty Block (label) main::@4 +Renumbering block @2 to @1 +Renumbering block main::@3 to main::@2 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] *((byte*)(const struct Point[2]) points#0) ← (byte) 1 + [5] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) 2 + [6] *((byte*)(const struct Point[2]) points#0+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 3 + [7] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 4 + to:main::@1 +main::@1: scope:[main] from main main::@2 + [8] (byte) idx#12 ← phi( main/(byte) 0 main::@2/(byte) idx#10 ) + [8] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [9] (byte~) main::$4 ← (byte) main::i#2 << (byte) 1 + [10] (byte) print::p_x#0 ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$4) + [11] (byte) print::p_y#0 ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$4) + [12] call print + to:main::@2 +main::@2: scope:[main] from main::@1 + [13] (byte) main::i#1 ← ++ (byte) main::i#2 + [14] if((byte) main::i#1!=(byte) 2) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@2 + [15] return + to:@return +print: scope:[print] from main::@1 + [16] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) print::p_x#0 + [17] (byte) idx#3 ← ++ (byte) idx#12 + [18] *((const byte*) SCREEN#0 + (byte) idx#3) ← (byte) print::p_y#0 + [19] (byte) idx#10 ← ++ (byte) idx#3 + to:print::@return +print::@return: scope:[print] from print + [20] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte) Point::x +(byte) Point::y +(byte*) SCREEN +(byte) idx +(byte) idx#10 2.6 +(byte) idx#12 3.0 +(byte) idx#3 3.0 +(void()) main() +(byte~) main::$4 16.5 +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#2 6.6000000000000005 +(struct Point[2]) points +(void()) print((byte) print::p_x , (byte) print::p_y) +(struct Point) print::p +(byte) print::p_x +(byte) print::p_x#0 6.5 +(byte) print::p_y +(byte) print::p_y#0 4.333333333333333 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ idx#12 idx#10 ] +Added variable main::$4 to zero page equivalence class [ main::$4 ] +Added variable print::p_x#0 to zero page equivalence class [ print::p_x#0 ] +Added variable print::p_y#0 to zero page equivalence class [ print::p_y#0 ] +Added variable idx#3 to zero page equivalence class [ idx#3 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ idx#12 idx#10 ] +[ main::$4 ] +[ print::p_x#0 ] +[ print::p_y#0 ] +[ idx#3 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Allocated zp ZP_BYTE:3 [ idx#12 idx#10 ] +Allocated zp ZP_BYTE:4 [ main::$4 ] +Allocated zp ZP_BYTE:5 [ print::p_x#0 ] +Allocated zp ZP_BYTE:6 [ print::p_y#0 ] +Allocated zp ZP_BYTE:7 [ idx#3 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// Demonstrates problem with passing struct array element as parameter to call + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + .label idx = 7 + .label idx_10 = 3 + .label idx_12 = 3 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label _4 = 4 + .label i = 2 + // [4] *((byte*)(const struct Point[2]) points#0) ← (byte) 1 -- _deref_pbuc1=vbuc2 + lda #1 + sta points + // [5] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) 2 -- _deref_pbuc1=vbuc2 + lda #2 + sta points+OFFSET_STRUCT_POINT_Y + // [6] *((byte*)(const struct Point[2]) points#0+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 3 -- _deref_pbuc1=vbuc2 + lda #3 + sta points+1*SIZEOF_STRUCT_POINT + // [7] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 4 -- _deref_pbuc1=vbuc2 + lda #4 + sta points+OFFSET_STRUCT_POINT_Y+1*SIZEOF_STRUCT_POINT + // [8] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [8] phi (byte) idx#12 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta idx_12 + // [8] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuz1=vbuc1 + lda #0 + sta i + jmp b1 + // [8] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [8] phi (byte) idx#12 = (byte) idx#10 [phi:main::@2->main::@1#0] -- register_copy + // [8] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#1] -- register_copy + jmp b1 + // main::@1 + b1: + // [9] (byte~) main::$4 ← (byte) main::i#2 << (byte) 1 -- vbuz1=vbuz2_rol_1 + lda i + asl + sta _4 + // [10] (byte) print::p_x#0 ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$4) -- vbuz1=pbuc1_derefidx_vbuz2 + ldy _4 + lda points,y + sta print.p_x + // [11] (byte) print::p_y#0 ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$4) -- vbuz1=pbuc1_derefidx_vbuz2 + ldy _4 + lda points+OFFSET_STRUCT_POINT_Y,y + sta print.p_y + // [12] call print + jsr print + jmp b2 + // main::@2 + b2: + // [13] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + // [14] if((byte) main::i#1!=(byte) 2) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #2 + cmp i + bne b1_from_b2 + jmp breturn + // main::@return + breturn: + // [15] return + rts +} + // print +// print(byte zeropage(5) p_x, byte zeropage(6) p_y) +print: { + .label p_x = 5 + .label p_y = 6 + // [16] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) print::p_x#0 -- pbuc1_derefidx_vbuz1=vbuz2 + lda p_x + ldy idx_12 + sta SCREEN,y + // [17] (byte) idx#3 ← ++ (byte) idx#12 -- vbuz1=_inc_vbuz2 + ldy idx_12 + iny + sty idx + // [18] *((const byte*) SCREEN#0 + (byte) idx#3) ← (byte) print::p_y#0 -- pbuc1_derefidx_vbuz1=vbuz2 + lda p_y + ldy idx + sta SCREEN,y + // [19] (byte) idx#10 ← ++ (byte) idx#3 -- vbuz1=_inc_vbuz2 + ldy idx + iny + sty idx_10 + jmp breturn + // print::@return + breturn: + // [20] return + rts +} + // File Data + points: .fill 2*2, 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((byte*)(const struct Point[2]) points#0) ← (byte) 1 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [5] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) 2 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [6] *((byte*)(const struct Point[2]) points#0+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 3 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [7] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 4 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [9] (byte~) main::$4 ← (byte) main::i#2 << (byte) 1 [ main::i#2 idx#12 main::$4 ] ( main:2 [ main::i#2 idx#12 main::$4 ] ) 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:3 [ idx#12 idx#10 ] +Statement [4] *((byte*)(const struct Point[2]) points#0) ← (byte) 1 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [5] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) 2 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [6] *((byte*)(const struct Point[2]) points#0+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 3 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [7] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 4 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [9] (byte~) main::$4 ← (byte) main::i#2 << (byte) 1 [ main::i#2 idx#12 main::$4 ] ( main:2 [ main::i#2 idx#12 main::$4 ] ) 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 [ idx#12 idx#10 ] : zp ZP_BYTE:3 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:4 [ main::$4 ] : zp ZP_BYTE:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:5 [ print::p_x#0 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:6 [ print::p_y#0 ] : zp ZP_BYTE:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:7 [ idx#3 ] : zp ZP_BYTE:7 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 23.1: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 16.5: zp ZP_BYTE:4 [ main::$4 ] +Uplift Scope [print] 6.5: zp ZP_BYTE:5 [ print::p_x#0 ] 4.33: zp ZP_BYTE:6 [ print::p_y#0 ] +Uplift Scope [] 5.6: zp ZP_BYTE:3 [ idx#12 idx#10 ] 3: zp ZP_BYTE:7 [ idx#3 ] +Uplift Scope [Point] + +Uplifting [main] best 614 combination reg byte x [ main::i#2 main::i#1 ] reg byte y [ main::$4 ] +Uplifting [print] best 614 combination zp ZP_BYTE:5 [ print::p_x#0 ] zp ZP_BYTE:6 [ print::p_y#0 ] +Uplifting [] best 605 combination zp ZP_BYTE:3 [ idx#12 idx#10 ] reg byte y [ idx#3 ] +Uplifting [Point] best 605 combination +Attempting to uplift remaining variables inzp ZP_BYTE:5 [ print::p_x#0 ] +Uplifting [print] best 605 combination zp ZP_BYTE:5 [ print::p_x#0 ] +Attempting to uplift remaining variables inzp ZP_BYTE:3 [ idx#12 idx#10 ] +Uplifting [] best 605 combination zp ZP_BYTE:3 [ idx#12 idx#10 ] +Attempting to uplift remaining variables inzp ZP_BYTE:6 [ print::p_y#0 ] +Uplifting [print] best 605 combination zp ZP_BYTE:6 [ print::p_y#0 ] +Allocated (was zp ZP_BYTE:3) zp ZP_BYTE:2 [ idx#12 idx#10 ] +Allocated (was zp ZP_BYTE:5) zp ZP_BYTE:3 [ print::p_x#0 ] +Allocated (was zp ZP_BYTE:6) zp ZP_BYTE:4 [ print::p_y#0 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Demonstrates problem with passing struct array element as parameter to call + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + .label idx = 2 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + // [4] *((byte*)(const struct Point[2]) points#0) ← (byte) 1 -- _deref_pbuc1=vbuc2 + lda #1 + sta points + // [5] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) 2 -- _deref_pbuc1=vbuc2 + lda #2 + sta points+OFFSET_STRUCT_POINT_Y + // [6] *((byte*)(const struct Point[2]) points#0+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 3 -- _deref_pbuc1=vbuc2 + lda #3 + sta points+1*SIZEOF_STRUCT_POINT + // [7] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 4 -- _deref_pbuc1=vbuc2 + lda #4 + sta points+OFFSET_STRUCT_POINT_Y+1*SIZEOF_STRUCT_POINT + // [8] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [8] phi (byte) idx#12 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta idx + // [8] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + // [8] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [8] phi (byte) idx#12 = (byte) idx#10 [phi:main::@2->main::@1#0] -- register_copy + // [8] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#1] -- register_copy + jmp b1 + // main::@1 + b1: + // [9] (byte~) main::$4 ← (byte) main::i#2 << (byte) 1 -- vbuyy=vbuxx_rol_1 + txa + asl + tay + // [10] (byte) print::p_x#0 ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$4) -- vbuz1=pbuc1_derefidx_vbuyy + lda points,y + sta print.p_x + // [11] (byte) print::p_y#0 ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$4) -- vbuz1=pbuc1_derefidx_vbuyy + lda points+OFFSET_STRUCT_POINT_Y,y + sta print.p_y + // [12] call print + jsr print + jmp b2 + // main::@2 + b2: + // [13] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [14] if((byte) main::i#1!=(byte) 2) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #2 + bne b1_from_b2 + jmp breturn + // main::@return + breturn: + // [15] return + rts +} + // print +// print(byte zeropage(3) p_x, byte zeropage(4) p_y) +print: { + .label p_x = 3 + .label p_y = 4 + // [16] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) print::p_x#0 -- pbuc1_derefidx_vbuz1=vbuz2 + lda p_x + ldy idx + sta SCREEN,y + // [17] (byte) idx#3 ← ++ (byte) idx#12 -- vbuyy=_inc_vbuz1 + ldy idx + iny + // [18] *((const byte*) SCREEN#0 + (byte) idx#3) ← (byte) print::p_y#0 -- pbuc1_derefidx_vbuyy=vbuz1 + lda p_y + sta SCREEN,y + // [19] (byte) idx#10 ← ++ (byte) idx#3 -- vbuz1=_inc_vbuyy + iny + sty idx + jmp breturn + // print::@return + breturn: + // [20] return + rts +} + // File Data + points: .fill 2*2, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp b2 +Removing instruction jmp breturn +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing instruction ldx #0 with TAX +Removing instruction ldy idx +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Replacing label b1_from_b2 with b1 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b2: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction b2: +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 +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(const byte) SIZEOF_STRUCT_POINT SIZEOF_STRUCT_POINT = (byte) 2 +(byte) idx +(byte) idx#10 idx zp ZP_BYTE:2 2.6 +(byte) idx#12 idx zp ZP_BYTE:2 3.0 +(byte) idx#3 reg byte y 3.0 +(void()) main() +(byte~) main::$4 reg byte y 16.5 +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 6.6000000000000005 +(struct Point[2]) points +(const struct Point[2]) points#0 points = { fill( 2, 0) } +(void()) print((byte) print::p_x , (byte) print::p_y) +(label) print::@return +(struct Point) print::p +(byte) print::p_x +(byte) print::p_x#0 p_x zp ZP_BYTE:3 6.5 +(byte) print::p_y +(byte) print::p_y#0 p_y zp ZP_BYTE:4 4.333333333333333 + +reg byte x [ main::i#2 main::i#1 ] +zp ZP_BYTE:2 [ idx#12 idx#10 ] +reg byte y [ main::$4 ] +zp ZP_BYTE:3 [ print::p_x#0 ] +zp ZP_BYTE:4 [ print::p_y#0 ] +reg byte y [ idx#3 ] + + +FINAL ASSEMBLER +Score: 467 + + // File Comments +// Demonstrates problem with passing struct array element as parameter to call + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label SCREEN = $400 + .label idx = 2 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + // points[0] = { 1, 2 } + // [4] *((byte*)(const struct Point[2]) points#0) ← (byte) 1 -- _deref_pbuc1=vbuc2 + lda #1 + sta points + // [5] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y) ← (byte) 2 -- _deref_pbuc1=vbuc2 + lda #2 + sta points+OFFSET_STRUCT_POINT_Y + // points[1] = { 3, 4 } + // [6] *((byte*)(const struct Point[2]) points#0+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 3 -- _deref_pbuc1=vbuc2 + lda #3 + sta points+1*SIZEOF_STRUCT_POINT + // [7] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y+(byte) 1*(const byte) SIZEOF_STRUCT_POINT) ← (byte) 4 -- _deref_pbuc1=vbuc2 + lda #4 + sta points+OFFSET_STRUCT_POINT_Y+1*SIZEOF_STRUCT_POINT + // [8] phi from main to main::@1 [phi:main->main::@1] + // [8] phi (byte) idx#12 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta idx + // [8] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#1] -- vbuxx=vbuc1 + tax + // [8] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [8] phi (byte) idx#12 = (byte) idx#10 [phi:main::@2->main::@1#0] -- register_copy + // [8] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#1] -- register_copy + // main::@1 + b1: + // print(points[i]) + // [9] (byte~) main::$4 ← (byte) main::i#2 << (byte) 1 -- vbuyy=vbuxx_rol_1 + txa + asl + tay + // [10] (byte) print::p_x#0 ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$4) -- vbuz1=pbuc1_derefidx_vbuyy + lda points,y + sta print.p_x + // [11] (byte) print::p_y#0 ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$4) -- vbuz1=pbuc1_derefidx_vbuyy + lda points+OFFSET_STRUCT_POINT_Y,y + sta print.p_y + // [12] call print + jsr print + // main::@2 + // for ( char i: 0..1) + // [13] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [14] if((byte) main::i#1!=(byte) 2) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #2 + bne b1 + // main::@return + // } + // [15] return + rts +} + // print +// print(byte zeropage(3) p_x, byte zeropage(4) p_y) +print: { + .label p_x = 3 + .label p_y = 4 + // SCREEN[idx++] = p.x + // [16] *((const byte*) SCREEN#0 + (byte) idx#12) ← (byte) print::p_x#0 -- pbuc1_derefidx_vbuz1=vbuz2 + lda p_x + ldy idx + sta SCREEN,y + // SCREEN[idx++] = p.x; + // [17] (byte) idx#3 ← ++ (byte) idx#12 -- vbuyy=_inc_vbuz1 + iny + // SCREEN[idx++] = p.y + // [18] *((const byte*) SCREEN#0 + (byte) idx#3) ← (byte) print::p_y#0 -- pbuc1_derefidx_vbuyy=vbuz1 + lda p_y + sta SCREEN,y + // SCREEN[idx++] = p.y; + // [19] (byte) idx#10 ← ++ (byte) idx#3 -- vbuz1=_inc_vbuyy + iny + sty idx + // print::@return + // } + // [20] return + rts +} + // File Data + points: .fill 2*2, 0 + diff --git a/src/test/ref/problem-array-struct-param.sym b/src/test/ref/problem-array-struct-param.sym new file mode 100644 index 000000000..78e4d97bc --- /dev/null +++ b/src/test/ref/problem-array-struct-param.sym @@ -0,0 +1,37 @@ +(label) @1 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_Y OFFSET_STRUCT_POINT_Y = (byte) 1 +(byte) Point::x +(byte) Point::y +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(const byte) SIZEOF_STRUCT_POINT SIZEOF_STRUCT_POINT = (byte) 2 +(byte) idx +(byte) idx#10 idx zp ZP_BYTE:2 2.6 +(byte) idx#12 idx zp ZP_BYTE:2 3.0 +(byte) idx#3 reg byte y 3.0 +(void()) main() +(byte~) main::$4 reg byte y 16.5 +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 6.6000000000000005 +(struct Point[2]) points +(const struct Point[2]) points#0 points = { fill( 2, 0) } +(void()) print((byte) print::p_x , (byte) print::p_y) +(label) print::@return +(struct Point) print::p +(byte) print::p_x +(byte) print::p_x#0 p_x zp ZP_BYTE:3 6.5 +(byte) print::p_y +(byte) print::p_y#0 p_y zp ZP_BYTE:4 4.333333333333333 + +reg byte x [ main::i#2 main::i#1 ] +zp ZP_BYTE:2 [ idx#12 idx#10 ] +reg byte y [ main::$4 ] +zp ZP_BYTE:3 [ print::p_x#0 ] +zp ZP_BYTE:4 [ print::p_y#0 ] +reg byte y [ idx#3 ]