diff --git a/src/main/fragment/mos6502-common/_deref_pwuc1=_deref_pwuc1_plus_vwuc2.asm b/src/main/fragment/mos6502-common/_deref_pwuc1=_deref_pwuc1_plus_vwuc2.asm new file mode 100644 index 000000000..a90c2f42a --- /dev/null +++ b/src/main/fragment/mos6502-common/_deref_pwuc1=_deref_pwuc1_plus_vwuc2.asm @@ -0,0 +1,7 @@ +lda #<{c2} +clc +adc {c1} +sta {c1} +lda #>{c2} +adc {c1}+1 +sta {c1}+1 \ No newline at end of file diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 7396ce557..210bbe9aa 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -42,6 +42,11 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testStructPointerInts() throws IOException, URISyntaxException { + compileAndCompare("struct-pointer-ints.c"); + } + @Test public void testPolygon() throws IOException, URISyntaxException { compileAndCompare("complex/polygon/polygon.c"); diff --git a/src/test/kc/struct-pointer-ints.c b/src/test/kc/struct-pointer-ints.c new file mode 100644 index 000000000..47b0f261c --- /dev/null +++ b/src/test/kc/struct-pointer-ints.c @@ -0,0 +1,19 @@ +// Demonstrates missing fragment _deref_pwuc1=_deref_pwuc1_plus_vwuc2 +// https://gitlab.com/camelot/kickc/-/issues/435 reported by G.B. + +typedef struct myStruct +{ + unsigned int a, b; +} myStruct; + +void update(myStruct *s, unsigned int size) +{ + s->a += size; +} + +int main(void) +{ + myStruct s; + update(&s, 1000); + return 0; +} diff --git a/src/test/ref/struct-pointer-ints.asm b/src/test/ref/struct-pointer-ints.asm new file mode 100644 index 000000000..b5de9aac1 --- /dev/null +++ b/src/test/ref/struct-pointer-ints.asm @@ -0,0 +1,34 @@ +// Demonstrates missing fragment _deref_pwuc1=_deref_pwuc1_plus_vwuc2 +// https://gitlab.com/camelot/kickc/-/issues/435 reported by G.B. +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const SIZEOF_STRUCT_MYSTRUCT = 4 +main: { + .label s = 2 + // s + ldy #SIZEOF_STRUCT_MYSTRUCT + lda #0 + !: + dey + sta s,y + bne !- + // update(&s, 1000) + jsr update + // } + rts +} +update: { + .const size = $3e8 + .label s = main.s + // s->a += size + lda #size + adc.z s+1 + sta.z s+1 + // } + rts +} diff --git a/src/test/ref/struct-pointer-ints.cfg b/src/test/ref/struct-pointer-ints.cfg new file mode 100644 index 000000000..dfc5c35c8 --- /dev/null +++ b/src/test/ref/struct-pointer-ints.cfg @@ -0,0 +1,26 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() + +(signed word()) main() +main: scope:[main] from @1 + [4] *(&(struct myStruct) main::s) ← memset(struct myStruct, (const byte) SIZEOF_STRUCT_MYSTRUCT) + [5] call update + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return + +(void()) update((struct myStruct*) update::s , (word) update::size) +update: scope:[update] from main + [7] *((word*)(const struct myStruct*) update::s#0) ← *((word*)(const struct myStruct*) update::s#0) + (const word) update::size#0 + to:update::@return +update::@return: scope:[update] from update + [8] return + to:@return diff --git a/src/test/ref/struct-pointer-ints.log b/src/test/ref/struct-pointer-ints.log new file mode 100644 index 000000000..471b9db3e --- /dev/null +++ b/src/test/ref/struct-pointer-ints.log @@ -0,0 +1,418 @@ +Setting struct to load/store in variable affected by address-of (void~) main::$0 ← call update &(struct myStruct) main::s (number) $3e8 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 + +(void()) update((struct myStruct*) update::s , (word) update::size) +update: scope:[update] from main + (word) update::size#1 ← phi( main/(word) update::size#0 ) + (struct myStruct*) update::s#1 ← phi( main/(struct myStruct*) update::s#0 ) + (word*~) update::$2 ← (word*)(struct myStruct*) update::s#1 + (word*~) update::$0 ← (word*~) update::$2 + (const byte) OFFSET_STRUCT_MYSTRUCT_A + (word*~) update::$3 ← (word*)(struct myStruct*) update::s#1 + (word*~) update::$1 ← (word*~) update::$3 + (const byte) OFFSET_STRUCT_MYSTRUCT_A + *((word*~) update::$1) ← *((word*~) update::$0) + (word) update::size#1 + to:update::@return +update::@return: scope:[update] from update + return + to:@return + +(signed word()) main() +main: scope:[main] from @1 + *(&(struct myStruct) main::s) ← memset(struct myStruct, (const byte) SIZEOF_STRUCT_MYSTRUCT) + (struct myStruct) main::s ← struct-unwound {*(&(struct myStruct) main::s)} + (struct myStruct*) update::s#0 ← &(struct myStruct) main::s + (word) update::size#0 ← (number) $3e8 + call update + to:main::@1 +main::@1: scope:[main] from main + (signed word) main::return#0 ← (number) 0 + to:main::@return +main::@return: scope:[main] from main::@1 + (signed word) main::return#3 ← phi( main::@1/(signed word) main::return#0 ) + (signed word) main::return#1 ← (signed word) main::return#3 + return + to:@return +@1: scope:[] from @begin + call main + (signed word) main::return#2 ← (signed word) main::return#1 + 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_MYSTRUCT_A = (byte) 0 +(const byte) SIZEOF_STRUCT_MYSTRUCT = (byte) 4 +(signed word()) main() +(label) main::@1 +(label) main::@return +(signed word) main::return +(signed word) main::return#0 +(signed word) main::return#1 +(signed word) main::return#2 +(signed word) main::return#3 +(struct myStruct) main::s loadstore +(word) myStruct::a +(word) myStruct::b +(void()) update((struct myStruct*) update::s , (word) update::size) +(word*~) update::$0 +(word*~) update::$1 +(word*~) update::$2 +(word*~) update::$3 +(label) update::@return +(struct myStruct*) update::s +(struct myStruct*) update::s#0 +(struct myStruct*) update::s#1 +(word) update::size +(word) update::size#0 +(word) update::size#1 + +Adding number conversion cast (unumber) $3e8 in (word) update::size#0 ← (number) $3e8 +Adding number conversion cast (snumber) 0 in (signed word) main::return#0 ← (number) 0 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (word) update::size#0 ← (unumber)(number) $3e8 +Inlining cast (signed word) main::return#0 ← (snumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant integer cast $3e8 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (word) $3e8 +Finalized signed number type (signed byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias main::return#0 = main::return#3 main::return#1 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (struct myStruct*) update::s#1 (struct myStruct*) update::s#0 +Identical Phi Values (word) update::size#1 (word) update::size#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Removing C-classic struct-unwound assignment [8] (struct myStruct) main::s ← struct-unwound {*(&(struct myStruct) main::s)} +Constant (const struct myStruct*) update::s#0 = &main::s +Constant (const word) update::size#0 = $3e8 +Constant (const signed word) main::return#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Constant (const word*) update::$2 = (word*)update::s#0 +Constant (const word*) update::$3 = (word*)update::s#0 +Constant (const signed word) main::return#2 = main::return#0 +Successful SSA optimization Pass2ConstantIdentification +Converting *(pointer+n) to pointer[n] [5] *((word*~) update::$1) ← *((word*~) update::$0) + (const word) update::size#0 -- *(update::$2 + OFFSET_STRUCT_MYSTRUCT_A) +Converting *(pointer+n) to pointer[n] [5] *((word*~) update::$1) ← *((const word*) update::$2 + (const byte) OFFSET_STRUCT_MYSTRUCT_A) + (const word) update::size#0 -- *(update::$3 + OFFSET_STRUCT_MYSTRUCT_A) +Successful SSA optimization Pass2InlineDerefIdx +Simplifying expression containing zero update::$2 in [2] (word*~) update::$0 ← (const word*) update::$2 + (const byte) OFFSET_STRUCT_MYSTRUCT_A +Simplifying expression containing zero update::$3 in [4] (word*~) update::$1 ← (const word*) update::$3 + (const byte) OFFSET_STRUCT_MYSTRUCT_A +Simplifying expression containing zero update::$2 in [5] *((const word*) update::$3 + (const byte) OFFSET_STRUCT_MYSTRUCT_A) ← *((const word*) update::$2 + (const byte) OFFSET_STRUCT_MYSTRUCT_A) + (const word) update::size#0 +Simplifying expression containing zero update::$3 in [5] *((const word*) update::$3 + (const byte) OFFSET_STRUCT_MYSTRUCT_A) ← *((const word*) update::$2) + (const word) update::size#0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable (word*~) update::$0 and assignment [0] (word*~) update::$0 ← (const word*) update::$2 +Eliminating unused variable (word*~) update::$1 and assignment [1] (word*~) update::$1 ← (const word*) update::$3 +Eliminating unused constant (const signed word) main::return#2 +Eliminating unused constant (const byte) OFFSET_STRUCT_MYSTRUCT_A +Successful SSA optimization PassNEliminateUnusedVars +Eliminating unused constant (const signed word) main::return#0 +Successful SSA optimization PassNEliminateUnusedVars +Constant inlined update::$2 = (word*)(const struct myStruct*) update::s#0 +Constant inlined update::$3 = (word*)(const struct myStruct*) update::s#0 +Successful SSA optimization Pass2ConstantInlining +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 +Adding NOP phi() at start of main::@1 +CALL GRAPH +Calls in [] to main:2 +Calls in [main] to update:6 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Culled Empty Block (label) @2 +Culled Empty Block (label) main::@1 +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() + +(signed word()) main() +main: scope:[main] from @1 + [4] *(&(struct myStruct) main::s) ← memset(struct myStruct, (const byte) SIZEOF_STRUCT_MYSTRUCT) + [5] call update + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return + +(void()) update((struct myStruct*) update::s , (word) update::size) +update: scope:[update] from main + [7] *((word*)(const struct myStruct*) update::s#0) ← *((word*)(const struct myStruct*) update::s#0) + (const word) update::size#0 + to:update::@return +update::@return: scope:[update] from update + [8] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(signed word()) main() +(signed word) main::return +(struct myStruct) main::s loadstore +(word) myStruct::a +(word) myStruct::b +(void()) update((struct myStruct*) update::s , (word) update::size) +(struct myStruct*) update::s +(word) update::size + +Initial phi equivalence classes +Added variable main::s to live range equivalence class [ main::s ] +Complete equivalence classes +[ main::s ] +Allocated zp[4]:2 [ main::s ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Demonstrates missing fragment _deref_pwuc1=_deref_pwuc1_plus_vwuc2 +// https://gitlab.com/camelot/kickc/-/issues/435 reported by G.B. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_STRUCT_MYSTRUCT = 4 + // @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 s = 2 + // [4] *(&(struct myStruct) main::s) ← memset(struct myStruct, (const byte) SIZEOF_STRUCT_MYSTRUCT) -- _deref_pssc1=_memset_vbuc2 + ldy #SIZEOF_STRUCT_MYSTRUCT + lda #0 + !: + dey + sta s,y + bne !- + // [5] call update + jsr update + jmp __breturn + // main::@return + __breturn: + // [6] return + rts +} + // update +update: { + .const size = $3e8 + .label s = main.s + // [7] *((word*)(const struct myStruct*) update::s#0) ← *((word*)(const struct myStruct*) update::s#0) + (const word) update::size#0 -- _deref_pwuc1=_deref_pwuc1_plus_vwuc2 + lda #size + adc.z s+1 + sta.z s+1 + jmp __breturn + // update::@return + __breturn: + // [8] return + rts +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *(&(struct myStruct) main::s) ← memset(struct myStruct, (const byte) SIZEOF_STRUCT_MYSTRUCT) [ ] ( main:2 [ ] { } ) always clobbers reg byte a reg byte y +Statement [7] *((word*)(const struct myStruct*) update::s#0) ← *((word*)(const struct myStruct*) update::s#0) + (const word) update::size#0 [ ] ( main:2::update:5 [ ] { } ) always clobbers reg byte a +Potential registers zp[4]:2 [ main::s ] : zp[4]:2 , + +REGISTER UPLIFT SCOPES +Uplift Scope [myStruct] +Uplift Scope [update] +Uplift Scope [main] 0: zp[4]:2 [ main::s ] +Uplift Scope [] + +Uplifting [myStruct] best 67 combination +Uplifting [update] best 67 combination +Uplifting [main] best 67 combination zp[4]:2 [ main::s ] +Uplifting [] best 67 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Demonstrates missing fragment _deref_pwuc1=_deref_pwuc1_plus_vwuc2 +// https://gitlab.com/camelot/kickc/-/issues/435 reported by G.B. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_STRUCT_MYSTRUCT = 4 + // @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 s = 2 + // [4] *(&(struct myStruct) main::s) ← memset(struct myStruct, (const byte) SIZEOF_STRUCT_MYSTRUCT) -- _deref_pssc1=_memset_vbuc2 + ldy #SIZEOF_STRUCT_MYSTRUCT + lda #0 + !: + dey + sta s,y + bne !- + // [5] call update + jsr update + jmp __breturn + // main::@return + __breturn: + // [6] return + rts +} + // update +update: { + .const size = $3e8 + .label s = main.s + // [7] *((word*)(const struct myStruct*) update::s#0) ← *((word*)(const struct myStruct*) update::s#0) + (const word) update::size#0 -- _deref_pwuc1=_deref_pwuc1_plus_vwuc2 + lda #size + adc.z s+1 + sta.z s+1 + jmp __breturn + // update::@return + __breturn: + // [8] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __bend +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b1_from___bbegin: +Removing instruction __b1: +Removing instruction __bend_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __bbegin: +Removing instruction __bend: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const byte) SIZEOF_STRUCT_MYSTRUCT = (byte) 4 +(signed word()) main() +(label) main::@return +(signed word) main::return +(struct myStruct) main::s loadstore zp[4]:2 +(word) myStruct::a +(word) myStruct::b +(void()) update((struct myStruct*) update::s , (word) update::size) +(label) update::@return +(struct myStruct*) update::s +(const struct myStruct*) update::s#0 s = &(struct myStruct) main::s +(word) update::size +(const word) update::size#0 size = (word) $3e8 + +zp[4]:2 [ main::s ] + + +FINAL ASSEMBLER +Score: 49 + + // File Comments +// Demonstrates missing fragment _deref_pwuc1=_deref_pwuc1_plus_vwuc2 +// https://gitlab.com/camelot/kickc/-/issues/435 reported by G.B. + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .const SIZEOF_STRUCT_MYSTRUCT = 4 + // @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: { + .label s = 2 + // s + // [4] *(&(struct myStruct) main::s) ← memset(struct myStruct, (const byte) SIZEOF_STRUCT_MYSTRUCT) -- _deref_pssc1=_memset_vbuc2 + ldy #SIZEOF_STRUCT_MYSTRUCT + lda #0 + !: + dey + sta s,y + bne !- + // update(&s, 1000) + // [5] call update + jsr update + // main::@return + // } + // [6] return + rts +} + // update +update: { + .const size = $3e8 + .label s = main.s + // s->a += size + // [7] *((word*)(const struct myStruct*) update::s#0) ← *((word*)(const struct myStruct*) update::s#0) + (const word) update::size#0 -- _deref_pwuc1=_deref_pwuc1_plus_vwuc2 + lda #size + adc.z s+1 + sta.z s+1 + // update::@return + // } + // [8] return + rts +} + // File Data + diff --git a/src/test/ref/struct-pointer-ints.sym b/src/test/ref/struct-pointer-ints.sym new file mode 100644 index 000000000..cd1469db7 --- /dev/null +++ b/src/test/ref/struct-pointer-ints.sym @@ -0,0 +1,18 @@ +(label) @1 +(label) @begin +(label) @end +(const byte) SIZEOF_STRUCT_MYSTRUCT = (byte) 4 +(signed word()) main() +(label) main::@return +(signed word) main::return +(struct myStruct) main::s loadstore zp[4]:2 +(word) myStruct::a +(word) myStruct::b +(void()) update((struct myStruct*) update::s , (word) update::size) +(label) update::@return +(struct myStruct*) update::s +(const struct myStruct*) update::s#0 s = &(struct myStruct) main::s +(word) update::size +(const word) update::size#0 size = (word) $3e8 + +zp[4]:2 [ main::s ]