diff --git a/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 b/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 index 3084bc0c8..1a7e70da2 100644 --- a/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 +++ b/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 @@ -243,9 +243,10 @@ expr | expr ( '&&' ) expr #exprBinary | expr ( '||' ) expr #exprBinary | expr '?' expr COLON expr #exprTernary - | expr '=' expr #exprAssignment + | expr ASSIGN expr #exprAssignment | expr ASSIGN_COMPOUND expr #exprAssignmentCompound | CURLY_BEGIN expr (COMMA expr )* COMMA? CURLY_END #initList + | CURLY_BEGIN DOT NAME ASSIGN expr CURLY_END #initUnion | NAME #exprId | NUMBER #exprNumber | STRING+ #exprString diff --git a/src/main/java/dk/camelot64/kickc/model/Initializers.java b/src/main/java/dk/camelot64/kickc/model/Initializers.java index 6db8334eb..31f5cc2fe 100644 --- a/src/main/java/dk/camelot64/kickc/model/Initializers.java +++ b/src/main/java/dk/camelot64/kickc/model/Initializers.java @@ -89,6 +89,8 @@ public class Initializers { return new ConstantCastValue(toType, (ConstantValue) constantSub); } } + } else if(initValue instanceof UnionDesignator) { + initValue = constantifyUnion((UnionDesignator) initValue, (SymbolTypeStruct) typeSpec.getType(), program, source); } else if(initValue instanceof ValueList) { ValueList initList = (ValueList) initValue; if(typeSpec.getType() instanceof SymbolTypePointer && ((SymbolTypePointer) typeSpec.getType()).getArraySpec() != null) { @@ -96,7 +98,7 @@ public class Initializers { initValue = constantifyArray(initList, (SymbolTypePointer) typeSpec.getType(), program, source); } else if(typeSpec.getType() instanceof SymbolTypeStruct) { // Type is a struct - initValue = constantifyStruct(initList, (SymbolTypeStruct) typeSpec.getType(), program, source); + initValue = constantifyStructOrUnion(initList, (SymbolTypeStruct) typeSpec.getType(), program, source); } else { throw new CompileError("Value list cannot initialize type " + typeSpec.getType(), source); } @@ -127,6 +129,41 @@ public class Initializers { return initValue; } + /** + * Convert a union designator initializer to a constant. + * + * @param unionInit The union initializer + * @param structType The union type + * @param program The program + * @param source The source line + * @return The constantified value + */ + private static RValue constantifyUnion(UnionDesignator unionInit, SymbolTypeStruct structType, Program program, StatementSource source) { + StructDefinition structDefinition = structType.getStructDefinition(program.getScope()); + Collection memberDefinitions = structDefinition.getAllVars(false); + + final String memberName = unionInit.getMemberName(); + final RValue initValue = unionInit.getMemberValue(); + + Variable memberDef = null; + for(Variable definition : memberDefinitions) { + if(definition.getLocalName().equals(memberName)) { + memberDef = definition; + } + } + if(memberDef==null) + throw new CompileError( "Union member not found", source); + + RValue constantifiedMemberValue = constantify(initValue, new ValueTypeSpec(memberDef.getType()), program, source); + if(constantifiedMemberValue instanceof ConstantValue) { + LinkedHashMap constMemberMap = new LinkedHashMap<>(); + constMemberMap.put(memberDef.getRef(), (ConstantValue) constantifiedMemberValue); + return new ConstantStructValue(structType, constMemberMap); + } else { + throw new CompileError( "Union initializer is not constant", source); + } + } + /** * Convert as much as possible of a struct to constants. * @@ -136,7 +173,7 @@ public class Initializers { * @param source The source line * @return The constantified value */ - private static RValue constantifyStruct(ValueList valueList, SymbolTypeStruct structType, Program program, StatementSource source) { + private static RValue constantifyStructOrUnion(ValueList valueList, SymbolTypeStruct structType, Program program, StatementSource source) { // Recursively cast all sub-elements StructDefinition structDefinition = structType.getStructDefinition(program.getScope()); Collection memberDefinitions = structDefinition.getAllVars(false); @@ -144,7 +181,7 @@ public class Initializers { if(structInitNeedSize != valueList.getList().size()) { if(structDefinition.isUnion()) { throw new CompileError( - "Union initializer has too many values, since only one is allowed.\n" + + "Union initializer has wrong size. One value is required.\n" + " Union initializer: " + valueList.toString(program), source); } else { diff --git a/src/main/java/dk/camelot64/kickc/model/values/UnionDesignator.java b/src/main/java/dk/camelot64/kickc/model/values/UnionDesignator.java new file mode 100644 index 000000000..6811766db --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/values/UnionDesignator.java @@ -0,0 +1,40 @@ +package dk.camelot64.kickc.model.values; + +import dk.camelot64.kickc.model.Program; + +/** + * A union designator initializer. + */ +public class UnionDesignator implements RValue { + + private final String memberName; + + private final RValue rValue; + + public UnionDesignator(String memberName, RValue rValue) { + this.memberName = memberName; + this.rValue = rValue; + } + + public String getMemberName() { return memberName; } + + public RValue getMemberValue() { + return rValue; + } + + @Override + public String toString(Program program) { + StringBuilder out = new StringBuilder(); + out.append("{ "); + out.append(memberName); + out.append("="); + out.append(rValue.toString(program)); + out.append(" }"); + return out.toString(); + } + + @Override + public String toString() { + return toString(null); + } +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index e7a343ac6..3f570bead 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -1913,13 +1913,20 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor initValues = new ArrayList<>(); for(KickCParser.ExprContext initializer : ctx.expr()) { RValue rValue = (RValue) visit(initializer); - initValues.add(rValue); - } + initValues.add(rValue); + } return new ValueList(initValues); } diff --git a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java index 1107e9ce3..90670856d 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java +++ b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java @@ -2557,6 +2557,31 @@ public class TestProgramsFast extends TestPrograms { compileAndCompare("weeip-bbslist.c"); } + @Test + public void testUnion13() throws IOException { + compileAndCompare("union-13.c"); + } + + @Test + public void testUnion12() throws IOException { + compileAndCompare("union-12.c"); + } + + @Test + public void testUnion11() throws IOException { + compileAndCompare("union-11.c"); + } + + @Test + public void testUnion10() throws IOException { + compileAndCompare("union-10.c"); + } + + @Test + public void testUnion9() throws IOException { + compileAndCompare("union-9.c"); + } + @Test public void testUnion8() throws IOException { compileAndCompare("union-8.c"); diff --git a/src/test/kc/union-10.c b/src/test/kc/union-10.c new file mode 100644 index 000000000..e05a9d461 --- /dev/null +++ b/src/test/kc/union-10.c @@ -0,0 +1,27 @@ +// More extensive union with C99 style designator initialization behaviour of the second element. + +struct Move { + char f; + char t; + char s; +}; + +struct Turn { + char t; + char s; + char r; + char d; +}; + +union Data { + struct Move m; + struct Turn t; +}; + +union Data data = { .t={1,2,3,4} }; + +char* const SCREEN = (char*)0x0400; + +void main() { + SCREEN[0] = data.m.f; +} \ No newline at end of file diff --git a/src/test/kc/union-11.c b/src/test/kc/union-11.c new file mode 100644 index 000000000..840f4c9a0 --- /dev/null +++ b/src/test/kc/union-11.c @@ -0,0 +1,27 @@ +// More extensive union with C99 style designator initialization behaviour of the first element. + +struct Move { + char f; + char t; + char s; +}; + +struct Turn { + char t; + char s; + char r; + char d; +}; + +union Data { + struct Move m; + struct Turn t; +}; + +union Data data = { .m={1,2,3} }; + +char* const SCREEN = (char*)0x0400; + +void main() { + SCREEN[0] = data.m.f; +} \ No newline at end of file diff --git a/src/test/kc/union-12.c b/src/test/kc/union-12.c new file mode 100644 index 000000000..a6e084637 --- /dev/null +++ b/src/test/kc/union-12.c @@ -0,0 +1,29 @@ +// More extensive union with C99 style designator initialization behaviour using const expressions. + +struct Move { + char f; + char t; + char s; +}; + +struct Turn { + char t; + char s; + char r; + char d; +}; + +union Data { + struct Move m; + struct Turn t; +}; + +const struct Move move = {1,2,3}; + +union Data data = { .m=move }; + +char* const SCREEN = (char*)0x0400; + +void main() { + SCREEN[0] = data.m.f; +} \ No newline at end of file diff --git a/src/test/kc/union-13.c b/src/test/kc/union-13.c new file mode 100644 index 000000000..fbfb6d8f4 --- /dev/null +++ b/src/test/kc/union-13.c @@ -0,0 +1,20 @@ +// More extensive union with C99 style designator initialization behaviour using const expressions. + +union A { + unsigned char b; + unsigned int w; +}; + +union B { + union A a; + char b[4]; +}; + +union B b1 = { .a={ .b=1 } }; + + +char* const SCREEN = (char*)0x0400; + +void main() { + SCREEN[0] = b1.b[0]; +} \ No newline at end of file diff --git a/src/test/kc/union-9.c b/src/test/kc/union-9.c new file mode 100644 index 000000000..bf7a6dd30 --- /dev/null +++ b/src/test/kc/union-9.c @@ -0,0 +1,14 @@ +// Minimal union with C99 style designator initialization behaviour. + +union Data { + char b; + unsigned w; +}; + +union Data data = { .w=1234 }; + +char* const SCREEN = (char*)0x0400; + +void main() { + SCREEN[0] = data.b; +} \ No newline at end of file diff --git a/src/test/ref/union-10.asm b/src/test/ref/union-10.asm new file mode 100644 index 000000000..312006261 --- /dev/null +++ b/src/test/ref/union-10.asm @@ -0,0 +1,20 @@ +// More extensive union with C99 style designator initialization behaviour of the second element. + // Commodore 64 PRG executable file +.file [name="union-10.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) + .label SCREEN = $400 +.segment Code +main: { + // SCREEN[0] = data.m.f + lda data + sta SCREEN + // } + rts +} +.segment Data + data: .byte 1, 2, 3, 4 diff --git a/src/test/ref/union-10.cfg b/src/test/ref/union-10.cfg new file mode 100644 index 000000000..16c9227d5 --- /dev/null +++ b/src/test/ref/union-10.cfg @@ -0,0 +1,8 @@ + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return diff --git a/src/test/ref/union-10.log b/src/test/ref/union-10.log new file mode 100644 index 000000000..f48c5715f --- /dev/null +++ b/src/test/ref/union-10.log @@ -0,0 +1,166 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) + to:main::@return +main::@return: scope:[main] from main + 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 char OFFSET_STRUCT_MOVE_F = 0 +__constant char OFFSET_UNION_DATA_M = 0 +__constant char * const SCREEN = (char *)$400 +void __start() +__loadstore union Data data = { t: { t: 1, s: 2, r: 3, d: 4 } } +void main() + +Adding number conversion cast (unumber) 0 in SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero (char *)(struct Move *)&data+OFFSET_UNION_DATA_M in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Simplifying expression containing zero (struct Move *)&data in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M) +Simplifying expression containing zero SCREEN in [0] SCREEN[0] = *((char *)(struct Move *)&data) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant OFFSET_UNION_DATA_M +Eliminating unused constant OFFSET_STRUCT_MOVE_F +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 +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__loadstore union Data data = { t: { t: 1, s: 2, r: 3, d: 4 } } +void main() + +Initial phi equivalence classes +Added variable data to live range equivalence class [ data ] +Complete equivalence classes +[ data ] +Allocated mem[4] [ data ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *SCREEN = *((char *)(struct Move *)&data) [ ] ( [ ] { } ) always clobbers reg byte a +Potential registers mem[4] [ data ] : mem[4] , + +REGISTER UPLIFT SCOPES +Uplift Scope [Move] +Uplift Scope [Turn] +Uplift Scope [Data] +Uplift Scope [main] +Uplift Scope [] 0: mem[4] [ data ] + +Uplifting [Move] best 17 combination +Uplifting [Turn] best 17 combination +Uplifting [Data] best 17 combination +Uplifting [main] best 17 combination +Uplifting [] best 17 combination mem[4] [ data ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// More extensive union with C99 style designator initialization behaviour of the second element. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-10.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 + .label SCREEN = $400 +.segment Code + // main +main: { + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [1] return + rts +} + // File Data +.segment Data + data: .byte 1, 2, 3, 4 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { t: { t: 1, s: 2, r: 3, d: 4 } } // mem[4] +void main() + +mem[4] [ data ] + + +FINAL ASSEMBLER +Score: 14 + + // File Comments +// More extensive union with C99 style designator initialization behaviour of the second element. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-10.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 + .label SCREEN = $400 +.segment Code + // main +main: { + // SCREEN[0] = data.m.f + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + // main::@return + // } + // [1] return + rts +} + // File Data +.segment Data + data: .byte 1, 2, 3, 4 + diff --git a/src/test/ref/union-10.sym b/src/test/ref/union-10.sym new file mode 100644 index 000000000..74e440b35 --- /dev/null +++ b/src/test/ref/union-10.sym @@ -0,0 +1,5 @@ +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { t: { t: 1, s: 2, r: 3, d: 4 } } // mem[4] +void main() + +mem[4] [ data ] diff --git a/src/test/ref/union-11.asm b/src/test/ref/union-11.asm new file mode 100644 index 000000000..8cc1f3211 --- /dev/null +++ b/src/test/ref/union-11.asm @@ -0,0 +1,21 @@ +// More extensive union with C99 style designator initialization behaviour of the first element. + // Commodore 64 PRG executable file +.file [name="union-11.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) + .label SCREEN = $400 +.segment Code +main: { + // SCREEN[0] = data.m.f + lda data + sta SCREEN + // } + rts +} +.segment Data + data: .byte 1, 2, 3 + .fill 1, 0 diff --git a/src/test/ref/union-11.cfg b/src/test/ref/union-11.cfg new file mode 100644 index 000000000..16c9227d5 --- /dev/null +++ b/src/test/ref/union-11.cfg @@ -0,0 +1,8 @@ + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return diff --git a/src/test/ref/union-11.log b/src/test/ref/union-11.log new file mode 100644 index 000000000..a03a0dd9b --- /dev/null +++ b/src/test/ref/union-11.log @@ -0,0 +1,168 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) + to:main::@return +main::@return: scope:[main] from main + 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 char OFFSET_STRUCT_MOVE_F = 0 +__constant char OFFSET_UNION_DATA_M = 0 +__constant char * const SCREEN = (char *)$400 +void __start() +__loadstore union Data data = { m: { f: 1, t: 2, s: 3 } } +void main() + +Adding number conversion cast (unumber) 0 in SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero (char *)(struct Move *)&data+OFFSET_UNION_DATA_M in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Simplifying expression containing zero (struct Move *)&data in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M) +Simplifying expression containing zero SCREEN in [0] SCREEN[0] = *((char *)(struct Move *)&data) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant OFFSET_UNION_DATA_M +Eliminating unused constant OFFSET_STRUCT_MOVE_F +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 +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__loadstore union Data data = { m: { f: 1, t: 2, s: 3 } } +void main() + +Initial phi equivalence classes +Added variable data to live range equivalence class [ data ] +Complete equivalence classes +[ data ] +Allocated mem[4] [ data ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *SCREEN = *((char *)(struct Move *)&data) [ ] ( [ ] { } ) always clobbers reg byte a +Potential registers mem[4] [ data ] : mem[4] , + +REGISTER UPLIFT SCOPES +Uplift Scope [Move] +Uplift Scope [Turn] +Uplift Scope [Data] +Uplift Scope [main] +Uplift Scope [] 0: mem[4] [ data ] + +Uplifting [Move] best 17 combination +Uplifting [Turn] best 17 combination +Uplifting [Data] best 17 combination +Uplifting [main] best 17 combination +Uplifting [] best 17 combination mem[4] [ data ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// More extensive union with C99 style designator initialization behaviour of the first element. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-11.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 + .label SCREEN = $400 +.segment Code + // main +main: { + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [1] return + rts +} + // File Data +.segment Data + data: .byte 1, 2, 3 + .fill 1, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { m: { f: 1, t: 2, s: 3 } } // mem[4] +void main() + +mem[4] [ data ] + + +FINAL ASSEMBLER +Score: 14 + + // File Comments +// More extensive union with C99 style designator initialization behaviour of the first element. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-11.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 + .label SCREEN = $400 +.segment Code + // main +main: { + // SCREEN[0] = data.m.f + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + // main::@return + // } + // [1] return + rts +} + // File Data +.segment Data + data: .byte 1, 2, 3 + .fill 1, 0 + diff --git a/src/test/ref/union-11.sym b/src/test/ref/union-11.sym new file mode 100644 index 000000000..65428d3de --- /dev/null +++ b/src/test/ref/union-11.sym @@ -0,0 +1,5 @@ +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { m: { f: 1, t: 2, s: 3 } } // mem[4] +void main() + +mem[4] [ data ] diff --git a/src/test/ref/union-12.asm b/src/test/ref/union-12.asm new file mode 100644 index 000000000..cd836e561 --- /dev/null +++ b/src/test/ref/union-12.asm @@ -0,0 +1,21 @@ +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Commodore 64 PRG executable file +.file [name="union-12.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) + .label SCREEN = $400 +.segment Code +main: { + // SCREEN[0] = data.m.f + lda data + sta SCREEN + // } + rts +} +.segment Data + move: .byte 1, 2, 3 + data: .fill 1, 0 diff --git a/src/test/ref/union-12.cfg b/src/test/ref/union-12.cfg new file mode 100644 index 000000000..16c9227d5 --- /dev/null +++ b/src/test/ref/union-12.cfg @@ -0,0 +1,8 @@ + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return diff --git a/src/test/ref/union-12.log b/src/test/ref/union-12.log new file mode 100644 index 000000000..6d08ee7e8 --- /dev/null +++ b/src/test/ref/union-12.log @@ -0,0 +1,170 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) + to:main::@return +main::@return: scope:[main] from main + 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 char OFFSET_STRUCT_MOVE_F = 0 +__constant char OFFSET_UNION_DATA_M = 0 +__constant char * const SCREEN = (char *)$400 +void __start() +__loadstore union Data data = { m: move } +void main() +__constant const struct Move move = { f: 1, t: 2, s: 3 } + +Adding number conversion cast (unumber) 0 in SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero (char *)(struct Move *)&data+OFFSET_UNION_DATA_M in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Simplifying expression containing zero (struct Move *)&data in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M) +Simplifying expression containing zero SCREEN in [0] SCREEN[0] = *((char *)(struct Move *)&data) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant OFFSET_UNION_DATA_M +Eliminating unused constant OFFSET_STRUCT_MOVE_F +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 +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__loadstore union Data data = { m: move } +void main() + +Initial phi equivalence classes +Added variable data to live range equivalence class [ data ] +Complete equivalence classes +[ data ] +Allocated mem[4] [ data ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *SCREEN = *((char *)(struct Move *)&data) [ ] ( [ ] { } ) always clobbers reg byte a +Potential registers mem[4] [ data ] : mem[4] , + +REGISTER UPLIFT SCOPES +Uplift Scope [Move] +Uplift Scope [Turn] +Uplift Scope [Data] +Uplift Scope [main] +Uplift Scope [] 0: mem[4] [ data ] + +Uplifting [Move] best 17 combination +Uplifting [Turn] best 17 combination +Uplifting [Data] best 17 combination +Uplifting [main] best 17 combination +Uplifting [] best 17 combination mem[4] [ data ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-12.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 + .label SCREEN = $400 +.segment Code + // main +main: { + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [1] return + rts +} + // File Data +.segment Data + move: .byte 1, 2, 3 + data: .fill 1, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { m: move } // mem[4] +void main() +__constant const struct Move move = { f: 1, t: 2, s: 3 } + +mem[4] [ data ] + + +FINAL ASSEMBLER +Score: 14 + + // File Comments +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-12.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 + .label SCREEN = $400 +.segment Code + // main +main: { + // SCREEN[0] = data.m.f + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + // main::@return + // } + // [1] return + rts +} + // File Data +.segment Data + move: .byte 1, 2, 3 + data: .fill 1, 0 + diff --git a/src/test/ref/union-12.sym b/src/test/ref/union-12.sym new file mode 100644 index 000000000..262c82268 --- /dev/null +++ b/src/test/ref/union-12.sym @@ -0,0 +1,6 @@ +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { m: move } // mem[4] +void main() +__constant const struct Move move = { f: 1, t: 2, s: 3 } + +mem[4] [ data ] diff --git a/src/test/ref/union-13.asm b/src/test/ref/union-13.asm new file mode 100644 index 000000000..523f3d619 --- /dev/null +++ b/src/test/ref/union-13.asm @@ -0,0 +1,22 @@ +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Commodore 64 PRG executable file +.file [name="union-13.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) + .label SCREEN = $400 +.segment Code +main: { + // SCREEN[0] = b1.b[0] + lda b1 + sta SCREEN + // } + rts +} +.segment Data + b1: .byte 1 + .fill 1, 0 + .fill 2, 0 diff --git a/src/test/ref/union-13.cfg b/src/test/ref/union-13.cfg new file mode 100644 index 000000000..067c8eda8 --- /dev/null +++ b/src/test/ref/union-13.cfg @@ -0,0 +1,8 @@ + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)&b1) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return diff --git a/src/test/ref/union-13.log b/src/test/ref/union-13.log new file mode 100644 index 000000000..70ec969ae --- /dev/null +++ b/src/test/ref/union-13.log @@ -0,0 +1,176 @@ +Fixing struct type size union B to 4 +Fixing struct type size union B to 4 +Fixing struct type SIZE_OF union B to 4 +Fixing struct type SIZE_OF union B to 4 + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + SCREEN[0] = ((char *)&b1+OFFSET_UNION_B_B)[0] + to:main::@return +main::@return: scope:[main] from main + 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 char OFFSET_UNION_B_B = 0 +__constant char * const SCREEN = (char *)$400 +void __start() +__loadstore union B b1 = { a: { b: 1 } } +void main() + +Adding number conversion cast (unumber) 0 in SCREEN[0] = ((char *)&b1+OFFSET_UNION_B_B)[0] +Adding number conversion cast (unumber) 0 in SCREEN[0] = ((char *)&b1+OFFSET_UNION_B_B)[(unumber)0] +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero (char *)&b1+OFFSET_UNION_B_B in [0] SCREEN[0] = ((char *)&b1+OFFSET_UNION_B_B)[0] +Simplifying expression containing zero (char *)&b1 in [0] SCREEN[0] = *((char *)&b1+OFFSET_UNION_B_B) +Simplifying expression containing zero SCREEN in [0] SCREEN[0] = *((char *)&b1) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant OFFSET_UNION_B_B +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 +Finalized unsigned number type (char) 4 +Finalized unsigned number type (char) 4 +Successful SSA optimization PassNFinalizeNumberTypeConversions +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)&b1) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__loadstore union B b1 = { a: { b: 1 } } +void main() + +Initial phi equivalence classes +Added variable b1 to live range equivalence class [ b1 ] +Complete equivalence classes +[ b1 ] +Allocated mem[4] [ b1 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *SCREEN = *((char *)&b1) [ ] ( [ ] { } ) always clobbers reg byte a +Potential registers mem[4] [ b1 ] : mem[4] , + +REGISTER UPLIFT SCOPES +Uplift Scope [A] +Uplift Scope [B] +Uplift Scope [main] +Uplift Scope [] 0: mem[4] [ b1 ] + +Uplifting [A] best 17 combination +Uplifting [B] best 17 combination +Uplifting [main] best 17 combination +Uplifting [] best 17 combination mem[4] [ b1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-13.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 + .label SCREEN = $400 +.segment Code + // main +main: { + // [0] *SCREEN = *((char *)&b1) -- _deref_pbuc1=_deref_pbuc2 + lda b1 + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [1] return + rts +} + // File Data +.segment Data + b1: .byte 1 + .fill 1, 0 + .fill 2, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__loadstore union B b1 = { a: { b: 1 } } // mem[4] +void main() + +mem[4] [ b1 ] + + +FINAL ASSEMBLER +Score: 14 + + // File Comments +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-13.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 + .label SCREEN = $400 +.segment Code + // main +main: { + // SCREEN[0] = b1.b[0] + // [0] *SCREEN = *((char *)&b1) -- _deref_pbuc1=_deref_pbuc2 + lda b1 + sta SCREEN + // main::@return + // } + // [1] return + rts +} + // File Data +.segment Data + b1: .byte 1 + .fill 1, 0 + .fill 2, 0 + diff --git a/src/test/ref/union-13.sym b/src/test/ref/union-13.sym new file mode 100644 index 000000000..12c6996e2 --- /dev/null +++ b/src/test/ref/union-13.sym @@ -0,0 +1,5 @@ +__constant char * const SCREEN = (char *) 1024 +__loadstore union B b1 = { a: { b: 1 } } // mem[4] +void main() + +mem[4] [ b1 ] diff --git a/src/test/ref/union-9.asm b/src/test/ref/union-9.asm new file mode 100644 index 000000000..7963534cd --- /dev/null +++ b/src/test/ref/union-9.asm @@ -0,0 +1,20 @@ +// Minimal union with C99 style designator initialization behaviour. + // Commodore 64 PRG executable file +.file [name="union-9.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) + .label SCREEN = $400 +.segment Code +main: { + // SCREEN[0] = data.b + lda data + sta SCREEN + // } + rts +} +.segment Data + data: .word $4d2 diff --git a/src/test/ref/union-9.cfg b/src/test/ref/union-9.cfg new file mode 100644 index 000000000..dcb6487cf --- /dev/null +++ b/src/test/ref/union-9.cfg @@ -0,0 +1,8 @@ + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return diff --git a/src/test/ref/union-9.log b/src/test/ref/union-9.log new file mode 100644 index 000000000..7f4e6a7d1 --- /dev/null +++ b/src/test/ref/union-9.log @@ -0,0 +1,159 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + SCREEN[0] = *((char *)&data+OFFSET_UNION_DATA_B) + to:main::@return +main::@return: scope:[main] from main + 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 char OFFSET_UNION_DATA_B = 0 +__constant char * const SCREEN = (char *)$400 +void __start() +__loadstore union Data data = { w: $4d2 } +void main() + +Adding number conversion cast (unumber) 0 in SCREEN[0] = *((char *)&data+OFFSET_UNION_DATA_B) +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero (char *)&data in [0] SCREEN[0] = *((char *)&data+OFFSET_UNION_DATA_B) +Simplifying expression containing zero SCREEN in [0] SCREEN[0] = *((char *)&data) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant OFFSET_UNION_DATA_B +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 +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__loadstore union Data data = { w: $4d2 } +void main() + +Initial phi equivalence classes +Added variable data to live range equivalence class [ data ] +Complete equivalence classes +[ data ] +Allocated mem[2] [ data ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *SCREEN = *((char *)&data) [ ] ( [ ] { } ) always clobbers reg byte a +Potential registers mem[2] [ data ] : mem[2] , + +REGISTER UPLIFT SCOPES +Uplift Scope [Data] +Uplift Scope [main] +Uplift Scope [] 0: mem[2] [ data ] + +Uplifting [Data] best 17 combination +Uplifting [main] best 17 combination +Uplifting [] best 17 combination mem[2] [ data ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Minimal union with C99 style designator initialization behaviour. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-9.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 + .label SCREEN = $400 +.segment Code + // main +main: { + // [0] *SCREEN = *((char *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [1] return + rts +} + // File Data +.segment Data + data: .word $4d2 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { w: $4d2 } // mem[2] +void main() + +mem[2] [ data ] + + +FINAL ASSEMBLER +Score: 14 + + // File Comments +// Minimal union with C99 style designator initialization behaviour. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-9.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 + .label SCREEN = $400 +.segment Code + // main +main: { + // SCREEN[0] = data.b + // [0] *SCREEN = *((char *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + // main::@return + // } + // [1] return + rts +} + // File Data +.segment Data + data: .word $4d2 + diff --git a/src/test/ref/union-9.sym b/src/test/ref/union-9.sym new file mode 100644 index 000000000..3f15e7831 --- /dev/null +++ b/src/test/ref/union-9.sym @@ -0,0 +1,5 @@ +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { w: $4d2 } // mem[2] +void main() + +mem[2] [ data ]