diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 32637d08b..fa20d95a3 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -1153,6 +1153,11 @@ public class TestPrograms { assertError("struct-err-0", "Unknown struct type"); } + @Test + public void testStruct36() throws IOException, URISyntaxException { + compileAndCompare("struct-36"); + } + @Test public void testStruct35() throws IOException, URISyntaxException { compileAndCompare("struct-35"); diff --git a/src/test/kc/struct-32.kc b/src/test/kc/struct-32.kc index 839a9c00c..df0883eec 100644 --- a/src/test/kc/struct-32.kc +++ b/src/test/kc/struct-32.kc @@ -1,4 +1,4 @@ -// Minimal struct with C-Standard behavior - member is array, copy assignment +// Minimal struct with C-Standard behavior - copy assignment struct Point { char x; diff --git a/src/test/kc/struct-36.kc b/src/test/kc/struct-36.kc new file mode 100644 index 000000000..f9687871d --- /dev/null +++ b/src/test/kc/struct-36.kc @@ -0,0 +1,16 @@ +// Minimal struct with C-Standard behavior - global main-mem struct should be initialized in data, not code + +struct Point { + char x; + char[2] initials; +}; + +__mem __ma struct Point point1 = { 2, "jg" }; + +const char* SCREEN = 0x0400; + +void main() { + SCREEN[0] = point1.x; + SCREEN[1] = point1.initials[0]; + SCREEN[2] = point1.initials[1]; +} \ No newline at end of file diff --git a/src/test/ref/struct-32.asm b/src/test/ref/struct-32.asm index 637c52e8e..82c352534 100644 --- a/src/test/ref/struct-32.asm +++ b/src/test/ref/struct-32.asm @@ -1,4 +1,4 @@ -// Minimal struct with C-Standard behavior - member is array, copy assignment +// Minimal struct with C-Standard behavior - copy assignment .pc = $801 "Basic" :BasicUpstart(main) .pc = $80d "Program" diff --git a/src/test/ref/struct-32.log b/src/test/ref/struct-32.log index 1273d4a8e..bdb4a8b16 100644 --- a/src/test/ref/struct-32.log +++ b/src/test/ref/struct-32.log @@ -129,7 +129,7 @@ Allocated zp[2]:4 [ main::point2 ] INITIAL ASM Target platform is c64basic / MOS6502X // File Comments -// Minimal struct with C-Standard behavior - member is array, copy assignment +// Minimal struct with C-Standard behavior - copy assignment // Upstart .pc = $801 "Basic" :BasicUpstart(__bbegin) @@ -211,7 +211,7 @@ Uplifting [] best 76 combination ASSEMBLER BEFORE OPTIMIZATION // File Comments -// Minimal struct with C-Standard behavior - member is array, copy assignment +// Minimal struct with C-Standard behavior - copy assignment // Upstart .pc = $801 "Basic" :BasicUpstart(__bbegin) @@ -313,7 +313,7 @@ FINAL ASSEMBLER Score: 61 // File Comments -// Minimal struct with C-Standard behavior - member is array, copy assignment +// Minimal struct with C-Standard behavior - copy assignment // Upstart .pc = $801 "Basic" :BasicUpstart(main) diff --git a/src/test/ref/struct-36.asm b/src/test/ref/struct-36.asm new file mode 100644 index 000000000..36e27b5d6 --- /dev/null +++ b/src/test/ref/struct-36.asm @@ -0,0 +1,18 @@ +// Minimal struct with C-Standard behavior - global main-mem struct should be initialized in data, not code +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 + .const OFFSET_STRUCT_POINT_INITIALS = 1 +main: { + lda point1 + sta SCREEN + lda point1+OFFSET_STRUCT_POINT_INITIALS + sta SCREEN+1 + lda point1+OFFSET_STRUCT_POINT_INITIALS+1 + sta SCREEN+2 + rts +} + point1: .byte 2 + .text "jg" + .byte 0 diff --git a/src/test/ref/struct-36.cfg b/src/test/ref/struct-36.cfg new file mode 100644 index 000000000..d808df9be --- /dev/null +++ b/src/test/ref/struct-36.cfg @@ -0,0 +1,19 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(void()) main() +main: scope:[main] from @1 + [4] *((const byte*) SCREEN) ← *((byte*)&(struct Point) point1) + [5] *((const byte*) SCREEN+(byte) 1) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS) + [6] *((const byte*) SCREEN+(byte) 2) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS+(byte) 1) + to:main::@return +main::@return: scope:[main] from main + [7] return + to:@return diff --git a/src/test/ref/struct-36.log b/src/test/ref/struct-36.log new file mode 100644 index 000000000..1f98c2c13 --- /dev/null +++ b/src/test/ref/struct-36.log @@ -0,0 +1,301 @@ +Fixing struct type size struct Point to 3 +Fixing struct type SIZE_OF struct Point to 3 +Fixing struct type SIZE_OF struct Point to 3 +Replacing struct member reference (struct Point) point1.x with member unwinding reference *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_X) +Replacing struct member reference (struct Point) point1.initials with member unwinding reference (byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS +Replacing struct member reference (struct Point) point1.initials with member unwinding reference (byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 + +(void()) main() +main: scope:[main] from @1 + *((const byte*) SCREEN + (number) 0) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_X) + *((const byte*) SCREEN + (number) 1) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 0) + *((const byte*) SCREEN + (number) 2) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 1) + to:main::@return +main::@return: scope:[main] from main + return + to:@return +@1: scope:[] from @begin + call main + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_INITIALS = (byte) 1 +(const byte) OFFSET_STRUCT_POINT_X = (byte) 0 +(const byte*) Point::initials[(number) 2] = { fill( 2, 0) } +(byte) Point::x +(const byte*) SCREEN = (byte*)(number) $400 +(void()) main() +(label) main::@return +(struct Point) point1 loadstore = { x: (byte) 2, initials: (string) "jg" } + +Adding number conversion cast (unumber) 0 in *((const byte*) SCREEN + (number) 0) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_X) +Adding number conversion cast (unumber) 0 in *((const byte*) SCREEN + (number) 1) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 0) +Adding number conversion cast (unumber) 1 in *((const byte*) SCREEN + (number) 1) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (unumber)(number) 0) +Adding number conversion cast (unumber) 1 in *((const byte*) SCREEN + (number) 2) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (number) 1) +Adding number conversion cast (unumber) 2 in *((const byte*) SCREEN + (number) 2) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (unumber)(number) 1) +Successful SSA optimization PassNAddNumberTypeConversions +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 1 +Simplifying constant integer cast 2 +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) 1 +Finalized unsigned number type (byte) 2 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero (byte*)&point1 in [0] *((const byte*) SCREEN + (byte) 0) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_X) +Simplifying expression containing zero SCREEN in [0] *((const byte*) SCREEN + (byte) 0) ← *((byte*)&(struct Point) point1) +Simplifying expression containing zero (byte*)&point1+OFFSET_STRUCT_POINT_INITIALS in [1] *((const byte*) SCREEN + (byte) 1) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS + (byte) 0) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant (const byte) OFFSET_STRUCT_POINT_X +Successful SSA optimization PassNEliminateUnusedVars +Consolidated array index constant in *(SCREEN+1) +Consolidated array index constant in *((byte*)&point1+OFFSET_STRUCT_POINT_INITIALS+1) +Consolidated array index constant in *(SCREEN+2) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:2 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Culled Empty Block (label) @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() + +(void()) main() +main: scope:[main] from @1 + [4] *((const byte*) SCREEN) ← *((byte*)&(struct Point) point1) + [5] *((const byte*) SCREEN+(byte) 1) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS) + [6] *((const byte*) SCREEN+(byte) 2) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS+(byte) 1) + to:main::@return +main::@return: scope:[main] from main + [7] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte) Point::x +(void()) main() +(struct Point) point1 loadstore = { x: (byte) 2, initials: (string) "jg" } + +Initial phi equivalence classes +Added variable point1 to live range equivalence class [ point1 ] +Complete equivalence classes +[ point1 ] +Allocated mem[3] [ point1 ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Minimal struct with C-Standard behavior - global main-mem struct should be initialized in data, not code + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .const OFFSET_STRUCT_POINT_INITIALS = 1 + // @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] *((const byte*) SCREEN) ← *((byte*)&(struct Point) point1) -- _deref_pbuc1=_deref_pbuc2 + lda point1 + sta SCREEN + // [5] *((const byte*) SCREEN+(byte) 1) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS) -- _deref_pbuc1=_deref_pbuc2 + lda point1+OFFSET_STRUCT_POINT_INITIALS + sta SCREEN+1 + // [6] *((const byte*) SCREEN+(byte) 2) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS+(byte) 1) -- _deref_pbuc1=_deref_pbuc2 + lda point1+OFFSET_STRUCT_POINT_INITIALS+1 + sta SCREEN+2 + jmp __breturn + // main::@return + __breturn: + // [7] return + rts +} + // File Data + point1: .byte 2 + .text "jg" + .byte 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((const byte*) SCREEN) ← *((byte*)&(struct Point) point1) [ point1 ] ( main:2 [ point1 ] ) always clobbers reg byte a +Statement [5] *((const byte*) SCREEN+(byte) 1) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS) [ point1 ] ( main:2 [ point1 ] ) always clobbers reg byte a +Statement [6] *((const byte*) SCREEN+(byte) 2) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS+(byte) 1) [ ] ( main:2 [ ] ) always clobbers reg byte a +Potential registers mem[3] [ point1 ] : mem[3] , + +REGISTER UPLIFT SCOPES +Uplift Scope [Point] +Uplift Scope [main] +Uplift Scope [] 0: mem[3] [ point1 ] + +Uplifting [Point] best 45 combination +Uplifting [main] best 45 combination +Uplifting [] best 45 combination mem[3] [ point1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Minimal struct with C-Standard behavior - global main-mem struct should be initialized in data, not code + // Upstart +.pc = $801 "Basic" +:BasicUpstart(__bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .const OFFSET_STRUCT_POINT_INITIALS = 1 + // @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] *((const byte*) SCREEN) ← *((byte*)&(struct Point) point1) -- _deref_pbuc1=_deref_pbuc2 + lda point1 + sta SCREEN + // [5] *((const byte*) SCREEN+(byte) 1) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS) -- _deref_pbuc1=_deref_pbuc2 + lda point1+OFFSET_STRUCT_POINT_INITIALS + sta SCREEN+1 + // [6] *((const byte*) SCREEN+(byte) 2) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS+(byte) 1) -- _deref_pbuc1=_deref_pbuc2 + lda point1+OFFSET_STRUCT_POINT_INITIALS+1 + sta SCREEN+2 + jmp __breturn + // main::@return + __breturn: + // [7] return + rts +} + // File Data + point1: .byte 2 + .text "jg" + .byte 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label __bbegin with __b1 +Removing instruction __bbegin: +Removing instruction __b1_from___bbegin: +Removing instruction __bend_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __bend: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction __b1: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_INITIALS = (byte) 1 +(const byte*) Point::initials[(number) 2] = { fill( 2, 0) } +(byte) Point::x +(const byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@return +(struct Point) point1 loadstore mem[3] = { x: (byte) 2, initials: (string) "jg" } + +mem[3] [ point1 ] + + +FINAL ASSEMBLER +Score: 30 + + // File Comments +// Minimal struct with C-Standard behavior - global main-mem struct should be initialized in data, not code + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .const OFFSET_STRUCT_POINT_INITIALS = 1 + // @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: { + // SCREEN[0] = point1.x + // [4] *((const byte*) SCREEN) ← *((byte*)&(struct Point) point1) -- _deref_pbuc1=_deref_pbuc2 + lda point1 + sta SCREEN + // SCREEN[1] = point1.initials[0] + // [5] *((const byte*) SCREEN+(byte) 1) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS) -- _deref_pbuc1=_deref_pbuc2 + lda point1+OFFSET_STRUCT_POINT_INITIALS + sta SCREEN+1 + // SCREEN[2] = point1.initials[1] + // [6] *((const byte*) SCREEN+(byte) 2) ← *((byte*)&(struct Point) point1+(const byte) OFFSET_STRUCT_POINT_INITIALS+(byte) 1) -- _deref_pbuc1=_deref_pbuc2 + lda point1+OFFSET_STRUCT_POINT_INITIALS+1 + sta SCREEN+2 + // main::@return + // } + // [7] return + rts +} + // File Data + point1: .byte 2 + .text "jg" + .byte 0 + diff --git a/src/test/ref/struct-36.sym b/src/test/ref/struct-36.sym new file mode 100644 index 000000000..4b57a0bbc --- /dev/null +++ b/src/test/ref/struct-36.sym @@ -0,0 +1,12 @@ +(label) @1 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_INITIALS = (byte) 1 +(const byte*) Point::initials[(number) 2] = { fill( 2, 0) } +(byte) Point::x +(const byte*) SCREEN = (byte*) 1024 +(void()) main() +(label) main::@return +(struct Point) point1 loadstore mem[3] = { x: (byte) 2, initials: (string) "jg" } + +mem[3] [ point1 ]