diff --git a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java index 377e393b6..3ed59dc8e 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java +++ b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java @@ -2318,6 +2318,11 @@ public class TestProgramsFast extends TestPrograms { } + @Test + public void testStruct45() throws IOException { + compileAndCompare("struct-45.c"); + } + @Test public void testStruct44() throws IOException { compileAndCompare("struct-44.c"); diff --git a/src/test/kc/struct-45.c b/src/test/kc/struct-45.c new file mode 100644 index 000000000..eb6c8a866 --- /dev/null +++ b/src/test/kc/struct-45.c @@ -0,0 +1,33 @@ +// https://gitlab.com/camelot/kickc/-/issues/590 + +#pragma var_model(mem) +#pragma struct_model(classic) + +struct deviceslot { + unsigned char hostSlot; + unsigned char mode; + unsigned char file[64]; +}; + +typedef struct deviceslot deviceslot_t; + +typedef struct deviceslotsA { + deviceslot_t slot[2]; +} DeviceSlotsA; + +char * const OUT = (char *)0x8000; + +void main() { + deviceslot_t s1 = {'A', 'R', "f1"}; + deviceslot_t s2 = {'B', 'W', "f2"}; + + DeviceSlotsA ssA; + DeviceSlotsA *slotsA = &ssA; + slotsA->slot[0] = s1; + slotsA->slot[1] = s2; + + for(char i: 0..1) { + deviceslot_t ds = slotsA->slot[i]; + *(OUT + i) = ds.mode; + } +} diff --git a/src/test/ref/struct-45.asm b/src/test/ref/struct-45.asm new file mode 100644 index 000000000..eaaa53826 --- /dev/null +++ b/src/test/ref/struct-45.asm @@ -0,0 +1,99 @@ +// https://gitlab.com/camelot/kickc/-/issues/590 + // Commodore 64 PRG executable file +.file [name="struct-45.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .const SIZEOF_STRUCT_DEVICESLOT = $42 + .const SIZEOF_STRUCT_DEVICESLOTSA = $84 + .const OFFSET_STRUCT_DEVICESLOT_MODE = 1 + .label OUT = $8000 +.segment Code +main: { + .label slotsA = ssA + // deviceslot_t s1 = {'A', 'R', "f1"} + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda __0-1,y + sta s1-1,y + dey + bne !- + // deviceslot_t s2 = {'B', 'W', "f2"} + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda __1-1,y + sta s2-1,y + dey + bne !- + // DeviceSlotsA ssA + ldy #SIZEOF_STRUCT_DEVICESLOTSA + lda #0 + !: + dey + sta ssA,y + bne !- + // slotsA->slot[0] = s1 + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda s1-1,y + sta slotsA-1,y + dey + bne !- + // slotsA->slot[1] = s2 + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda s2-1,y + sta slotsA+1*SIZEOF_STRUCT_DEVICESLOT-1,y + dey + bne !- + lda #0 + sta i + __b1: + // deviceslot_t ds = slotsA->slot[i] + lda i + asl + asl + asl + asl + asl + clc + adc i + asl + tax + ldy #0 + !: + lda slotsA,x + sta ds,y + inx + iny + cpy #SIZEOF_STRUCT_DEVICESLOT + bne !- + // *(OUT + i) = ds.mode + lda ds+OFFSET_STRUCT_DEVICESLOT_MODE + ldy i + sta OUT,y + // for(char i: 0..1) + inc i + lda #2 + cmp i + bne __b1 + // } + rts + .segment Data + s1: .fill SIZEOF_STRUCT_DEVICESLOT, 0 + s2: .fill SIZEOF_STRUCT_DEVICESLOT, 0 + ssA: .fill SIZEOF_STRUCT_DEVICESLOTSA, 0 + ds: .fill SIZEOF_STRUCT_DEVICESLOT, 0 + i: .byte 0 +} + __0: .byte 'A', 'R' + .text "f1" + .byte 0 + .fill $3d, 0 + __1: .byte 'B', 'W' + .text "f2" + .byte 0 + .fill $3d, 0 diff --git a/src/test/ref/struct-45.cfg b/src/test/ref/struct-45.cfg new file mode 100644 index 000000000..0fe9a0ab8 --- /dev/null +++ b/src/test/ref/struct-45.cfg @@ -0,0 +1,22 @@ + +void main() +main: scope:[main] from + [0] *(&main::s1) = memcpy(*(&$0), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + [1] *(&main::s2) = memcpy(*(&$1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + [2] *(&main::ssA) = memset(struct deviceslotsA, SIZEOF_STRUCT_DEVICESLOTSA) + [3] *((struct deviceslot*)main::slotsA) = memcpy(*(&main::s1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + [4] *((struct deviceslot*)main::slotsA+1*SIZEOF_STRUCT_DEVICESLOT) = memcpy(*(&main::s2), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + to:main::@1 +main::@1: scope:[main] from main main::@1 + [5] main::i#2 = phi( main/0, main::@1/main::i#1 ) + [6] main::$11 = main::i#2 << 5 + [7] main::$12 = main::$11 + main::i#2 + [8] main::$4 = main::$12 << 1 + [9] *(&main::ds) = memcpy(((struct deviceslot*)main::slotsA)[main::$4], struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + [10] OUT[main::i#2] = *((byte*)&main::ds+OFFSET_STRUCT_DEVICESLOT_MODE) + [11] main::i#1 = ++ main::i#2 + [12] if(main::i#1!=2) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 + [13] return + to:@return diff --git a/src/test/ref/struct-45.log b/src/test/ref/struct-45.log new file mode 100644 index 000000000..996b5d636 --- /dev/null +++ b/src/test/ref/struct-45.log @@ -0,0 +1,581 @@ +Updating intermediate variable memory area to MAIN_MEMORY main::$1 +Fixing struct type size struct deviceslot to 66 +Fixing struct type size struct deviceslotsA to 132 +Fixing struct type size struct deviceslot to 66 +Fixing struct type size struct deviceslot to 66 +Fixing struct type size struct deviceslotsA to 132 +Fixing struct type SIZE_OF struct deviceslot to 66 +Fixing struct type SIZE_OF struct deviceslotsA to 132 +Fixing struct type SIZE_OF struct deviceslot to 66 +Fixing struct type SIZE_OF struct deviceslotsA to 132 +Setting struct to load/store in variable affected by address-of main::slotsA = &main::ssA +Removing C-classic struct-unwound assignment main::s1 = struct-unwound {*(&main::s1)} +Removing C-classic struct-unwound assignment main::s2 = struct-unwound {*(&main::s2)} +Removing C-classic struct-unwound assignment main::ssA = struct-unwound {*(&main::ssA)} +Removing C-classic struct-unwound assignment main::ds = struct-unwound {*(&main::ds)} + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + *(&main::s1) = memcpy(*(&$0), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + *(&main::s2) = memcpy(*(&$1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + *(&main::ssA) = memset(struct deviceslotsA, SIZEOF_STRUCT_DEVICESLOTSA) + main::$2 = 0 * SIZEOF_STRUCT_DEVICESLOT + main::$8 = (struct deviceslot*)main::slotsA + main::$5 = main::$8 + OFFSET_STRUCT_DEVICESLOTSA_SLOT + main::$5[main::$2] = memcpy(*(&main::s1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + main::$3 = 1 * SIZEOF_STRUCT_DEVICESLOT + main::$9 = (struct deviceslot*)main::slotsA + main::$6 = main::$9 + OFFSET_STRUCT_DEVICESLOTSA_SLOT + main::$6[main::$3] = memcpy(*(&main::s2), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + main::i#0 = 0 + to:main::@1 +main::@1: scope:[main] from main main::@1 + main::i#2 = phi( main/main::i#0, main::@1/main::i#1 ) + main::$4 = main::i#2 * SIZEOF_STRUCT_DEVICESLOT + main::$10 = (struct deviceslot*)main::slotsA + main::$7 = main::$10 + OFFSET_STRUCT_DEVICESLOTSA_SLOT + *(&main::ds) = memcpy(main::$7[main::$4], struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + main::$0 = OUT + main::i#2 + *main::$0 = *((byte*)&main::ds+OFFSET_STRUCT_DEVICESLOT_MODE) + main::i#1 = main::i#2 + rangenext(0,1) + main::$1 = main::i#1 != rangelast(0,1) + if(main::$1) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +constant struct deviceslot $0 = { hostSlot: 'A', mode: 'R', file: "f1" } +constant struct deviceslot $1 = { hostSlot: 'B', mode: 'W', file: "f2" } +constant byte OFFSET_STRUCT_DEVICESLOTSA_SLOT = 0 +constant byte OFFSET_STRUCT_DEVICESLOT_MODE = 1 +constant byte* const OUT = (byte*)$8000 +constant byte SIZEOF_STRUCT_DEVICESLOT = $42 +constant byte SIZEOF_STRUCT_DEVICESLOTSA = $84 +void __start() +void main() +byte*~ main::$0 +bool~ main::$1 +struct deviceslot*~ main::$10 +number~ main::$2 +number~ main::$3 +byte~ main::$4 +struct deviceslot*~ main::$5 +struct deviceslot*~ main::$6 +struct deviceslot*~ main::$7 +struct deviceslot*~ main::$8 +struct deviceslot*~ main::$9 +struct deviceslot main::ds loadstore +byte main::i +byte main::i#0 +byte main::i#1 +byte main::i#2 +struct deviceslot main::s1 loadstore +struct deviceslot main::s2 loadstore +constant struct deviceslotsA* main::slotsA = &main::ssA +volatile struct deviceslotsA main::ssA loadstore + +Adding number conversion cast (unumber) 0 in main::$2 = 0 * SIZEOF_STRUCT_DEVICESLOT +Adding number conversion cast (unumber) main::$2 in main::$2 = (unumber)0 * SIZEOF_STRUCT_DEVICESLOT +Adding number conversion cast (unumber) 1 in main::$3 = 1 * SIZEOF_STRUCT_DEVICESLOT +Adding number conversion cast (unumber) main::$3 in main::$3 = (unumber)1 * SIZEOF_STRUCT_DEVICESLOT +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 32768 +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) 1 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inferred type updated to byte in main::$2 = 0 * SIZEOF_STRUCT_DEVICESLOT +Inferred type updated to byte in main::$3 = 1 * SIZEOF_STRUCT_DEVICESLOT +Simple Condition main::$1 [21] if(main::i#1!=rangelast(0,1)) goto main::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant right-side identified [3] main::$2 = 0 * SIZEOF_STRUCT_DEVICESLOT +Constant right-side identified [4] main::$8 = (struct deviceslot*)main::slotsA +Constant right-side identified [7] main::$3 = 1 * SIZEOF_STRUCT_DEVICESLOT +Constant right-side identified [8] main::$9 = (struct deviceslot*)main::slotsA +Constant right-side identified [14] main::$10 = (struct deviceslot*)main::slotsA +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant main::$2 = 0*SIZEOF_STRUCT_DEVICESLOT +Constant main::$8 = (struct deviceslot*)main::slotsA +Constant main::$3 = 1*SIZEOF_STRUCT_DEVICESLOT +Constant main::$9 = (struct deviceslot*)main::slotsA +Constant main::i#0 = 0 +Constant main::$10 = (struct deviceslot*)main::slotsA +Successful SSA optimization Pass2ConstantIdentification +Resolved ranged next value [19] main::i#1 = ++ main::i#2 to ++ +Resolved ranged comparison value [21] if(main::i#1!=rangelast(0,1)) goto main::@1 to 2 +Converting *(pointer+n) to pointer[n] [18] *main::$0 = *((byte*)&main::ds+OFFSET_STRUCT_DEVICESLOT_MODE) -- OUT[main::i#2] +Successful SSA optimization Pass2InlineDerefIdx +Simplifying constant evaluating to zero 0*SIZEOF_STRUCT_DEVICESLOT in +Successful SSA optimization PassNSimplifyConstantZero +Simplifying expression containing zero main::$8 in [5] main::$5 = main::$8 + OFFSET_STRUCT_DEVICESLOTSA_SLOT +Simplifying expression containing zero main::$5 in [6] main::$5[main::$2] = memcpy(*(&main::s1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) +Simplifying expression containing zero main::$9 in [9] main::$6 = main::$9 + OFFSET_STRUCT_DEVICESLOTSA_SLOT +Simplifying expression containing zero main::$10 in [15] main::$7 = main::$10 + OFFSET_STRUCT_DEVICESLOTSA_SLOT +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable main::$0 and assignment [11] main::$0 = OUT + main::i#2 +Eliminating unused constant main::$2 +Eliminating unused constant OFFSET_STRUCT_DEVICESLOTSA_SLOT +Successful SSA optimization PassNEliminateUnusedVars +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Adding number conversion cast (unumber) 2 in [13] if(main::i#1!=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 main::$5 = main::$8 +Constant main::$6 = main::$9 +Constant main::$7 = main::$10 +Successful SSA optimization Pass2ConstantIdentification +Rewriting multiplication to use shift and addition[6] main::$4 = main::i#2 * SIZEOF_STRUCT_DEVICESLOT +Inlining constant with var siblings main::i#0 +Constant inlined main::$5 = (struct deviceslot*)main::slotsA +Constant inlined main::i#0 = 0 +Constant inlined main::$6 = (struct deviceslot*)main::slotsA +Constant inlined main::$3 = 1*SIZEOF_STRUCT_DEVICESLOT +Constant inlined main::$9 = (struct deviceslot*)main::slotsA +Constant inlined main::$10 = (struct deviceslot*)main::slotsA +Constant inlined main::$7 = (struct deviceslot*)main::slotsA +Constant inlined main::$8 = (struct deviceslot*)main::slotsA +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *((struct deviceslot*)main::slotsA+1*SIZEOF_STRUCT_DEVICESLOT) +Successful SSA optimization Pass2ConstantAdditionElimination +Alias main::$4 = main::$13 +Successful SSA optimization Pass2AliasElimination +Finalized unsigned number type (byte) $40 +Finalized unsigned number type (byte) $40 +Finalized unsigned number type (byte) 2 +Finalized unsigned number type (byte) 2 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Added new block during phi lifting main::@2(between main::@1 and main::@1) +CALL GRAPH + +Created 1 initial phi equivalence classes +Coalesced [14] main::i#3 = main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block label main::@2 + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *(&main::s1) = memcpy(*(&$0), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + [1] *(&main::s2) = memcpy(*(&$1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + [2] *(&main::ssA) = memset(struct deviceslotsA, SIZEOF_STRUCT_DEVICESLOTSA) + [3] *((struct deviceslot*)main::slotsA) = memcpy(*(&main::s1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + [4] *((struct deviceslot*)main::slotsA+1*SIZEOF_STRUCT_DEVICESLOT) = memcpy(*(&main::s2), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + to:main::@1 +main::@1: scope:[main] from main main::@1 + [5] main::i#2 = phi( main/0, main::@1/main::i#1 ) + [6] main::$11 = main::i#2 << 5 + [7] main::$12 = main::$11 + main::i#2 + [8] main::$4 = main::$12 << 1 + [9] *(&main::ds) = memcpy(((struct deviceslot*)main::slotsA)[main::$4], struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) + [10] OUT[main::i#2] = *((byte*)&main::ds+OFFSET_STRUCT_DEVICESLOT_MODE) + [11] main::i#1 = ++ main::i#2 + [12] if(main::i#1!=2) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@1 + [13] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +byte~ main::$11 22.0 +byte~ main::$12 22.0 +byte~ main::$4 11.0 +struct deviceslot main::ds loadstore +byte main::i +byte main::i#1 16.5 +byte main::i#2 9.166666666666666 +struct deviceslot main::s1 loadstore +struct deviceslot main::s2 loadstore +volatile struct deviceslotsA main::ssA loadstore + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Added variable main::$11 to live range equivalence class [ main::$11 ] +Added variable main::$12 to live range equivalence class [ main::$12 ] +Added variable main::$4 to live range equivalence class [ main::$4 ] +Added variable main::s1 to live range equivalence class [ main::s1 ] +Added variable main::s2 to live range equivalence class [ main::s2 ] +Added variable main::ssA to live range equivalence class [ main::ssA ] +Added variable main::ds to live range equivalence class [ main::ds ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ main::$11 ] +[ main::$12 ] +[ main::$4 ] +[ main::s1 ] +[ main::s2 ] +[ main::ssA ] +[ main::ds ] +Allocated mem[1] [ main::i#2 main::i#1 ] +Allocated mem[1] [ main::$11 ] +Allocated mem[1] [ main::$12 ] +Allocated mem[1] [ main::$4 ] +Allocated mem[66] [ main::s1 ] +Allocated mem[66] [ main::s2 ] +Allocated mem[132] [ main::ssA ] +Allocated mem[66] [ main::ds ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *(&main::s1) = memcpy(*(&$0), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) [ main::s2 main::ssA main::s1 main::ds ] ( [ main::s2 main::ssA main::s1 main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [1] *(&main::s2) = memcpy(*(&$1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) [ main::s2 main::ssA main::s1 main::ds ] ( [ main::s2 main::ssA main::s1 main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [2] *(&main::ssA) = memset(struct deviceslotsA, SIZEOF_STRUCT_DEVICESLOTSA) [ main::s2 main::s1 main::ds ] ( [ main::s2 main::s1 main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [3] *((struct deviceslot*)main::slotsA) = memcpy(*(&main::s1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) [ main::s2 main::ds ] ( [ main::s2 main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [4] *((struct deviceslot*)main::slotsA+1*SIZEOF_STRUCT_DEVICESLOT) = memcpy(*(&main::s2), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) [ main::ds ] ( [ main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [6] main::$11 = main::i#2 << 5 [ main::i#2 main::$11 main::ds ] ( [ main::i#2 main::$11 main::ds ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for mem[1] [ main::i#2 main::i#1 ] +Statement [7] main::$12 = main::$11 + main::i#2 [ main::i#2 main::$12 main::ds ] ( [ main::i#2 main::$12 main::ds ] { } ) always clobbers reg byte a +Statement [8] main::$4 = main::$12 << 1 [ main::i#2 main::$4 main::ds ] ( [ main::i#2 main::$4 main::ds ] { } ) always clobbers reg byte a +Statement [9] *(&main::ds) = memcpy(((struct deviceslot*)main::slotsA)[main::$4], struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) [ main::i#2 main::ds ] ( [ main::i#2 main::ds ] { } ) always clobbers reg byte a reg byte x reg byte y +Removing always clobbered register reg byte x as potential for mem[1] [ main::i#2 main::i#1 ] +Removing always clobbered register reg byte y as potential for mem[1] [ main::i#2 main::i#1 ] +Statement [10] OUT[main::i#2] = *((byte*)&main::ds+OFFSET_STRUCT_DEVICESLOT_MODE) [ main::i#2 main::ds ] ( [ main::i#2 main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [12] if(main::i#1!=2) goto main::@1 [ main::i#1 main::ds ] ( [ main::i#1 main::ds ] { } ) always clobbers reg byte a +Statement [0] *(&main::s1) = memcpy(*(&$0), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) [ main::s2 main::ssA main::s1 main::ds ] ( [ main::s2 main::ssA main::s1 main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [1] *(&main::s2) = memcpy(*(&$1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) [ main::s2 main::ssA main::s1 main::ds ] ( [ main::s2 main::ssA main::s1 main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [2] *(&main::ssA) = memset(struct deviceslotsA, SIZEOF_STRUCT_DEVICESLOTSA) [ main::s2 main::s1 main::ds ] ( [ main::s2 main::s1 main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [3] *((struct deviceslot*)main::slotsA) = memcpy(*(&main::s1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) [ main::s2 main::ds ] ( [ main::s2 main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [4] *((struct deviceslot*)main::slotsA+1*SIZEOF_STRUCT_DEVICESLOT) = memcpy(*(&main::s2), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) [ main::ds ] ( [ main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [6] main::$11 = main::i#2 << 5 [ main::i#2 main::$11 main::ds ] ( [ main::i#2 main::$11 main::ds ] { } ) always clobbers reg byte a +Statement [7] main::$12 = main::$11 + main::i#2 [ main::i#2 main::$12 main::ds ] ( [ main::i#2 main::$12 main::ds ] { } ) always clobbers reg byte a +Statement [8] main::$4 = main::$12 << 1 [ main::i#2 main::$4 main::ds ] ( [ main::i#2 main::$4 main::ds ] { } ) always clobbers reg byte a +Statement [9] *(&main::ds) = memcpy(((struct deviceslot*)main::slotsA)[main::$4], struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) [ main::i#2 main::ds ] ( [ main::i#2 main::ds ] { } ) always clobbers reg byte a reg byte x reg byte y +Statement [10] OUT[main::i#2] = *((byte*)&main::ds+OFFSET_STRUCT_DEVICESLOT_MODE) [ main::i#2 main::ds ] ( [ main::i#2 main::ds ] { } ) always clobbers reg byte a reg byte y +Statement [12] if(main::i#1!=2) goto main::@1 [ main::i#1 main::ds ] ( [ main::i#1 main::ds ] { } ) always clobbers reg byte a +Potential registers mem[1] [ main::i#2 main::i#1 ] : mem[1] , +Potential registers mem[1] [ main::$11 ] : mem[1] , reg byte a , reg byte x , reg byte y , +Potential registers mem[1] [ main::$12 ] : mem[1] , reg byte a , reg byte x , reg byte y , +Potential registers mem[1] [ main::$4 ] : mem[1] , reg byte a , reg byte x , reg byte y , +Potential registers mem[66] [ main::s1 ] : mem[66] , +Potential registers mem[66] [ main::s2 ] : mem[66] , +Potential registers mem[132] [ main::ssA ] : mem[132] , +Potential registers mem[66] [ main::ds ] : mem[66] , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 25.67: mem[1] [ main::i#2 main::i#1 ] 22: mem[1] [ main::$11 ] 22: mem[1] [ main::$12 ] 11: mem[1] [ main::$4 ] 0: mem[66] [ main::s1 ] 0: mem[66] [ main::s2 ] 0: mem[132] [ main::ssA ] 0: mem[66] [ main::ds ] +Uplift Scope [deviceslot] +Uplift Scope [deviceslotsA] +Uplift Scope [] + +Uplifting [main] best 948 combination mem[1] [ main::i#2 main::i#1 ] reg byte a [ main::$11 ] reg byte a [ main::$12 ] reg byte a [ main::$4 ] mem[66] [ main::s1 ] mem[66] [ main::s2 ] mem[132] [ main::ssA ] mem[66] [ main::ds ] +Uplifting [deviceslot] best 948 combination +Uplifting [deviceslotsA] best 948 combination +Uplifting [] best 948 combination +Attempting to uplift remaining variables inmem[1] [ main::i#2 main::i#1 ] +Uplifting [main] best 948 combination mem[1] [ main::i#2 main::i#1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// https://gitlab.com/camelot/kickc/-/issues/590 + // Upstart + // Commodore 64 PRG executable file +.file [name="struct-45.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const SIZEOF_STRUCT_DEVICESLOT = $42 + .const SIZEOF_STRUCT_DEVICESLOTSA = $84 + .const OFFSET_STRUCT_DEVICESLOT_MODE = 1 + .label OUT = $8000 +.segment Code + // main +main: { + .label slotsA = ssA + // [0] *(&main::s1) = memcpy(*(&$0), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda __0-1,y + sta s1-1,y + dey + bne !- + // [1] *(&main::s2) = memcpy(*(&$1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda __1-1,y + sta s2-1,y + dey + bne !- + // [2] *(&main::ssA) = memset(struct deviceslotsA, SIZEOF_STRUCT_DEVICESLOTSA) -- _deref_pssc1=_memset_vbuc2 + ldy #SIZEOF_STRUCT_DEVICESLOTSA + lda #0 + !: + dey + sta ssA,y + bne !- + // [3] *((struct deviceslot*)main::slotsA) = memcpy(*(&main::s1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda s1-1,y + sta slotsA-1,y + dey + bne !- + // [4] *((struct deviceslot*)main::slotsA+1*SIZEOF_STRUCT_DEVICESLOT) = memcpy(*(&main::s2), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda s2-1,y + sta slotsA+1*SIZEOF_STRUCT_DEVICESLOT-1,y + dey + bne !- + // [5] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [5] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbum1=vbuc1 + lda #0 + sta i + jmp __b1 + // [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + __b1_from___b1: + // [5] phi main::i#2 = main::i#1 [phi:main::@1->main::@1#0] -- register_copy + jmp __b1 + // main::@1 + __b1: + // [6] main::$11 = main::i#2 << 5 -- vbuaa=vbum1_rol_5 + lda i + asl + asl + asl + asl + asl + // [7] main::$12 = main::$11 + main::i#2 -- vbuaa=vbuaa_plus_vbum1 + clc + adc i + // [8] main::$4 = main::$12 << 1 -- vbuaa=vbuaa_rol_1 + asl + // [9] *(&main::ds) = memcpy(((struct deviceslot*)main::slotsA)[main::$4], struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) -- _deref_pssc1=pssc2_derefidx_vbuaa_memcpy_vbuc3 + tax + ldy #0 + !: + lda slotsA,x + sta ds,y + inx + iny + cpy #SIZEOF_STRUCT_DEVICESLOT + bne !- + // [10] OUT[main::i#2] = *((byte*)&main::ds+OFFSET_STRUCT_DEVICESLOT_MODE) -- pbuc1_derefidx_vbum1=_deref_pbuc2 + lda ds+OFFSET_STRUCT_DEVICESLOT_MODE + ldy i + sta OUT,y + // [11] main::i#1 = ++ main::i#2 -- vbum1=_inc_vbum1 + inc i + // [12] if(main::i#1!=2) goto main::@1 -- vbum1_neq_vbuc1_then_la1 + lda #2 + cmp i + bne __b1_from___b1 + jmp __breturn + // main::@return + __breturn: + // [13] return + rts + .segment Data + s1: .fill SIZEOF_STRUCT_DEVICESLOT, 0 + s2: .fill SIZEOF_STRUCT_DEVICESLOT, 0 + ssA: .fill SIZEOF_STRUCT_DEVICESLOTSA, 0 + ds: .fill SIZEOF_STRUCT_DEVICESLOT, 0 + i: .byte 0 +} + // File Data + __0: .byte 'A', 'R' + .text "f1" + .byte 0 + .fill $3d, 0 + __1: .byte 'B', 'W' + .text "f2" + .byte 0 + .fill $3d, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label __b1_from___b1 with __b1 +Removing instruction __b1_from___b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __b1_from_main: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jmp __b1 +Succesful ASM optimization Pass5NextJumpElimination + +FINAL SYMBOL TABLE +constant struct deviceslot $0 = { hostSlot: 'A', mode: 'R', file: "f1" } +constant struct deviceslot $1 = { hostSlot: 'B', mode: 'W', file: "f2" } +constant byte OFFSET_STRUCT_DEVICESLOT_MODE = 1 +constant byte* const OUT = (byte*) 32768 +constant byte SIZEOF_STRUCT_DEVICESLOT = $42 +constant byte SIZEOF_STRUCT_DEVICESLOTSA = $84 +void main() +byte~ main::$11 reg byte a 22.0 +byte~ main::$12 reg byte a 22.0 +byte~ main::$4 reg byte a 11.0 +struct deviceslot main::ds loadstore mem[66] +byte main::i +byte main::i#1 i mem[1] 16.5 +byte main::i#2 i mem[1] 9.166666666666666 +struct deviceslot main::s1 loadstore mem[66] +struct deviceslot main::s2 loadstore mem[66] +constant struct deviceslotsA* main::slotsA = &main::ssA +volatile struct deviceslotsA main::ssA loadstore mem[132] + +mem[1] [ main::i#2 main::i#1 ] +reg byte a [ main::$11 ] +reg byte a [ main::$12 ] +reg byte a [ main::$4 ] +mem[66] [ main::s1 ] +mem[66] [ main::s2 ] +mem[132] [ main::ssA ] +mem[66] [ main::ds ] + + +FINAL ASSEMBLER +Score: 858 + + // File Comments +// https://gitlab.com/camelot/kickc/-/issues/590 + // Upstart + // Commodore 64 PRG executable file +.file [name="struct-45.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const SIZEOF_STRUCT_DEVICESLOT = $42 + .const SIZEOF_STRUCT_DEVICESLOTSA = $84 + .const OFFSET_STRUCT_DEVICESLOT_MODE = 1 + .label OUT = $8000 +.segment Code + // main +main: { + .label slotsA = ssA + // deviceslot_t s1 = {'A', 'R', "f1"} + // [0] *(&main::s1) = memcpy(*(&$0), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda __0-1,y + sta s1-1,y + dey + bne !- + // deviceslot_t s2 = {'B', 'W', "f2"} + // [1] *(&main::s2) = memcpy(*(&$1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda __1-1,y + sta s2-1,y + dey + bne !- + // DeviceSlotsA ssA + // [2] *(&main::ssA) = memset(struct deviceslotsA, SIZEOF_STRUCT_DEVICESLOTSA) -- _deref_pssc1=_memset_vbuc2 + ldy #SIZEOF_STRUCT_DEVICESLOTSA + lda #0 + !: + dey + sta ssA,y + bne !- + // slotsA->slot[0] = s1 + // [3] *((struct deviceslot*)main::slotsA) = memcpy(*(&main::s1), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda s1-1,y + sta slotsA-1,y + dey + bne !- + // slotsA->slot[1] = s2 + // [4] *((struct deviceslot*)main::slotsA+1*SIZEOF_STRUCT_DEVICESLOT) = memcpy(*(&main::s2), struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_DEVICESLOT + !: + lda s2-1,y + sta slotsA+1*SIZEOF_STRUCT_DEVICESLOT-1,y + dey + bne !- + // [5] phi from main to main::@1 [phi:main->main::@1] + // [5] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbum1=vbuc1 + lda #0 + sta i + // [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + // [5] phi main::i#2 = main::i#1 [phi:main::@1->main::@1#0] -- register_copy + // main::@1 + __b1: + // deviceslot_t ds = slotsA->slot[i] + // [6] main::$11 = main::i#2 << 5 -- vbuaa=vbum1_rol_5 + lda i + asl + asl + asl + asl + asl + // [7] main::$12 = main::$11 + main::i#2 -- vbuaa=vbuaa_plus_vbum1 + clc + adc i + // [8] main::$4 = main::$12 << 1 -- vbuaa=vbuaa_rol_1 + asl + // [9] *(&main::ds) = memcpy(((struct deviceslot*)main::slotsA)[main::$4], struct deviceslot, SIZEOF_STRUCT_DEVICESLOT) -- _deref_pssc1=pssc2_derefidx_vbuaa_memcpy_vbuc3 + tax + ldy #0 + !: + lda slotsA,x + sta ds,y + inx + iny + cpy #SIZEOF_STRUCT_DEVICESLOT + bne !- + // *(OUT + i) = ds.mode + // [10] OUT[main::i#2] = *((byte*)&main::ds+OFFSET_STRUCT_DEVICESLOT_MODE) -- pbuc1_derefidx_vbum1=_deref_pbuc2 + lda ds+OFFSET_STRUCT_DEVICESLOT_MODE + ldy i + sta OUT,y + // for(char i: 0..1) + // [11] main::i#1 = ++ main::i#2 -- vbum1=_inc_vbum1 + inc i + // [12] if(main::i#1!=2) goto main::@1 -- vbum1_neq_vbuc1_then_la1 + lda #2 + cmp i + bne __b1 + // main::@return + // } + // [13] return + rts + .segment Data + s1: .fill SIZEOF_STRUCT_DEVICESLOT, 0 + s2: .fill SIZEOF_STRUCT_DEVICESLOT, 0 + ssA: .fill SIZEOF_STRUCT_DEVICESLOTSA, 0 + ds: .fill SIZEOF_STRUCT_DEVICESLOT, 0 + i: .byte 0 +} + // File Data + __0: .byte 'A', 'R' + .text "f1" + .byte 0 + .fill $3d, 0 + __1: .byte 'B', 'W' + .text "f2" + .byte 0 + .fill $3d, 0 + diff --git a/src/test/ref/struct-45.sym b/src/test/ref/struct-45.sym new file mode 100644 index 000000000..ec548a971 --- /dev/null +++ b/src/test/ref/struct-45.sym @@ -0,0 +1,27 @@ +constant struct deviceslot $0 = { hostSlot: 'A', mode: 'R', file: "f1" } +constant struct deviceslot $1 = { hostSlot: 'B', mode: 'W', file: "f2" } +constant byte OFFSET_STRUCT_DEVICESLOT_MODE = 1 +constant byte* const OUT = (byte*) 32768 +constant byte SIZEOF_STRUCT_DEVICESLOT = $42 +constant byte SIZEOF_STRUCT_DEVICESLOTSA = $84 +void main() +byte~ main::$11 reg byte a 22.0 +byte~ main::$12 reg byte a 22.0 +byte~ main::$4 reg byte a 11.0 +struct deviceslot main::ds loadstore mem[66] +byte main::i +byte main::i#1 i mem[1] 16.5 +byte main::i#2 i mem[1] 9.166666666666666 +struct deviceslot main::s1 loadstore mem[66] +struct deviceslot main::s2 loadstore mem[66] +constant struct deviceslotsA* main::slotsA = &main::ssA +volatile struct deviceslotsA main::ssA loadstore mem[132] + +mem[1] [ main::i#2 main::i#1 ] +reg byte a [ main::$11 ] +reg byte a [ main::$12 ] +reg byte a [ main::$4 ] +mem[66] [ main::s1 ] +mem[66] [ main::s2 ] +mem[132] [ main::ssA ] +mem[66] [ main::ds ]