diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 6ddf9b1b9..e8189ba87 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -364,6 +364,7 @@ public class Compiler { constantOptimizations.add(new Pass2NopCastInlining(program)); constantOptimizations.add(new Pass2MultiplyToShiftRewriting(program)); constantOptimizations.add(new Pass2ConstantInlining(program)); + constantOptimizations.add(new Pass2ArrayInStructInlining(program)); constantOptimizations.add(new Pass2ConstantAdditionElimination(program)); constantOptimizations.add(new Pass2ConstantSimplification(program)); constantOptimizations.addAll(getPass2Optimizations()); diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java index ba282792e..f8ab54230 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java @@ -477,6 +477,10 @@ public interface ProgramValue { public void set(Value value) { structValue.setValue(memberRef, (ConstantValue) value); } + + public VariableRef getMemberRef() { + return memberRef; + } } /** Value inside a constant array filled expression. */ diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ArrayInStructInlining.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ArrayInStructInlining.java new file mode 100644 index 000000000..ce7c40619 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ArrayInStructInlining.java @@ -0,0 +1,80 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.iterator.ProgramValue; +import dk.camelot64.kickc.model.iterator.ProgramValueIterator; +import dk.camelot64.kickc.model.symbols.ConstantVar; +import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.types.SymbolTypeArray; +import dk.camelot64.kickc.model.values.ConstantRef; +import dk.camelot64.kickc.model.values.ConstantValue; +import dk.camelot64.kickc.model.values.Value; +import dk.camelot64.kickc.model.values.VariableRef; + +import java.util.HashMap; +import java.util.Map; + +/** + * Compiler Pass inlining arrays inside literal structs + */ +public class Pass2ArrayInStructInlining extends Pass2SsaOptimization { + + public Pass2ArrayInStructInlining(Program program) { + super(program); + } + + /** + * Consolidate unnamed constants into other constants value + * + * @return true optimization was performed. false if no optimization was possible. + */ + @Override + public boolean step() { + Map inline = new HashMap<>(); + inline.putAll(findArrayInStruct()); + + // Replace all usages of the constants in the control flow graph or symbol table + replaceVariables(inline); + // Remove from symbol table + deleteSymbols(getScope(), inline.keySet()); + + for(ConstantRef constantRef : inline.keySet()) { + getLog().append("Constant array inlined into struct " + constantRef.toString() + " = " + inline.get(constantRef).toString(getProgram())); + } + + return inline.size() > 0; + + } + + /** + * Find constant fixed size arrays inside structs. + * + * @return Map from constant name to constant value + */ + private Map findArrayInStruct() { + Map inline = new HashMap<>(); + ProgramValueIterator.execute(getProgram(), (programValue, currentStmt, stmtIt, currentBlock) -> { + Value value = programValue.get(); + if(programValue instanceof ProgramValue.ProgramValueConstantStructMember) { + VariableRef memberRef = ((ProgramValue.ProgramValueConstantStructMember) programValue).getMemberRef(); + Variable structMemberVar = getScope().getVariable(memberRef); + if(structMemberVar.getType() instanceof SymbolTypeArray) { + if(((SymbolTypeArray) structMemberVar.getType()).getSize() != null) { + if(value instanceof ConstantValue) { + ConstantValue constantValue = (ConstantValue) value; + if(constantValue.getType(getProgram().getScope()).equals(SymbolType.STRING)) { + if(constantValue instanceof ConstantRef) { + ConstantVar constantStringVar = getScope().getConstant((ConstantRef) constantValue); + inline.put((ConstantRef) constantValue, constantStringVar.getValue()); + } + } + } + } + } + } + }); + return inline; + } + +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java index afe14a284..d84f903c5 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantIdentification.java @@ -231,17 +231,6 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization { static ConstantValue createBinary(ConstantValue c1, OperatorBinary operator, ConstantValue c2, ProgramScope programScope) { - // Special handling of string append using + - if(Operators.PLUS.equals(operator) && SymbolType.STRING.equals(c1.getType(programScope))) { - if(c1 instanceof ConstantRef) { - c1 = programScope.getConstant((ConstantRef) c1).getValue(); - } - if(c2 instanceof ConstantRef) { - c2 = programScope.getConstant((ConstantRef) c2).getValue(); - } - return new ConstantBinary(c1, operator, c2); - } - if(Operators.PLUS.equals(operator)) { return new ConstantBinary(c1, operator, c2); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java index 39ccdcd86..0a97ad5ee 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java @@ -36,8 +36,7 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization { @Override public boolean step() { Map inline = new HashMap<>(); - Map aliasConstants = findAliasConstants(); - inline.putAll(aliasConstants); + inline.putAll(findAliasConstants()); inline.putAll(findUnnamedConstants()); inline.putAll(findConstVarVersions()); diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 8dae58d00..05486dfc7 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -655,9 +655,14 @@ public class TestPrograms { } */ + @Test + public void testStructPtr34() throws IOException, URISyntaxException { + compileAndCompare("struct-ptr-34"); + } + @Test public void testStructPtr33() throws IOException, URISyntaxException { - compileAndCompare("struct-ptr-33", log()); + compileAndCompare("struct-ptr-33"); } @Test diff --git a/src/test/kc/struct-ptr-29.kc b/src/test/kc/struct-ptr-29.kc index 0e26ca867..d1221314f 100644 --- a/src/test/kc/struct-ptr-29.kc +++ b/src/test/kc/struct-ptr-29.kc @@ -5,12 +5,9 @@ struct Person { char* name; }; -char* name1 = "jesper"; -char* name2 = "repsej"; - struct Person[2] persons = { - { 4, name1 }, - { 7, name2 } + { 4, "jesper" }, + { 7, "repsej" } }; void main() { diff --git a/src/test/kc/struct-ptr-34.kc b/src/test/kc/struct-ptr-34.kc new file mode 100644 index 000000000..e3aa81ed4 --- /dev/null +++ b/src/test/kc/struct-ptr-34.kc @@ -0,0 +1,29 @@ +// Example of a struct containing an array + +struct Person { + char id; + char[16] name; +}; + +void main() { + struct Person jesper = { 4, "jesper" }; + print_person(jesper); + + struct Person henriette = { 7, "henriette" }; + print_person(henriette); +} + +const char* SCREEN = 0x0400; +char idx = 0; +char[] DIGIT = "0123456789"; + +void print_person(struct Person person) { + SCREEN[idx++] = DIGIT[person.id]; + SCREEN[idx++] = ' '; + for(byte i=0; person.name[i]; i++) + SCREEN[idx++] = person.name[i]; + SCREEN[idx++] = ' '; +} + + + diff --git a/src/test/ref/struct-ptr-23.asm b/src/test/ref/struct-ptr-23.asm index a23458c19..cd5f6c1fb 100644 --- a/src/test/ref/struct-ptr-23.asm +++ b/src/test/ref/struct-ptr-23.asm @@ -65,10 +65,6 @@ print_person: { inx rts } - _0: .text "jgr" - .byte 0 - _1: .text "hbg" - .byte 0 persons: .byte 1 .text "jgr" .byte 0, 8 diff --git a/src/test/ref/struct-ptr-23.log b/src/test/ref/struct-ptr-23.log index 9f7be812c..ca002ef74 100644 --- a/src/test/ref/struct-ptr-23.log +++ b/src/test/ref/struct-ptr-23.log @@ -208,6 +208,9 @@ Constant inlined print_person::person#0 = (const struct Person[]) persons#0 Constant inlined main::person#0 = (const struct Person[]) persons#0 Constant inlined print_person::person#1 = (const struct Person*) main::person#1 Successful SSA optimization Pass2ConstantInlining +Constant array inlined into struct $0 = (string) "jgr" +Constant array inlined into struct $1 = (string) "hbg" +Successful SSA optimization Pass2ArrayInStructInlining Adding NOP phi() at start of @begin Adding NOP phi() at start of @1 Adding NOP phi() at start of @2 @@ -490,10 +493,6 @@ print_person: { rts } // File Data - _0: .text "jgr" - .byte 0 - _1: .text "hbg" - .byte 0 persons: .byte 1 .text "jgr" .byte 0, 8 @@ -682,10 +681,6 @@ print_person: { rts } // File Data - _0: .text "jgr" - .byte 0 - _1: .text "hbg" - .byte 0 persons: .byte 1 .text "jgr" .byte 0, 8 @@ -720,8 +715,6 @@ Removing instruction bbegin: Succesful ASM optimization Pass5UnusedLabelElimination FINAL SYMBOL TABLE -(const string) $0 $0 = (string) "jgr" -(const string) $1 $1 = (string) "hbg" (label) @1 (label) @begin (label) @end @@ -745,7 +738,7 @@ FINAL SYMBOL TABLE (struct Person*) main::person (const struct Person*) main::person#1 person = (const struct Person[]) persons#0+(const byte) SIZEOF_STRUCT_PERSON (struct Person[]) persons -(const struct Person[]) persons#0 persons = { { id: (byte) 1, initials: (const string) $0 }, { id: (byte) 8, initials: (const string) $1 } } +(const struct Person[]) persons#0 persons = { { id: (byte) 1, initials: (string) "jgr" }, { id: (byte) 8, initials: (string) "hbg" } } (void()) print_person((struct Person*) print_person::person) (byte~) print_person::$0 reg byte a 4.0 (byte[4]) print_person::$3 $3 zp ZP_WORD:4 4.0 @@ -894,10 +887,6 @@ print_person: { rts } // File Data - _0: .text "jgr" - .byte 0 - _1: .text "hbg" - .byte 0 persons: .byte 1 .text "jgr" .byte 0, 8 diff --git a/src/test/ref/struct-ptr-23.sym b/src/test/ref/struct-ptr-23.sym index 200b59cfc..7d7e2b76c 100644 --- a/src/test/ref/struct-ptr-23.sym +++ b/src/test/ref/struct-ptr-23.sym @@ -1,5 +1,3 @@ -(const string) $0 $0 = (string) "jgr" -(const string) $1 $1 = (string) "hbg" (label) @1 (label) @begin (label) @end @@ -23,7 +21,7 @@ (struct Person*) main::person (const struct Person*) main::person#1 person = (const struct Person[]) persons#0+(const byte) SIZEOF_STRUCT_PERSON (struct Person[]) persons -(const struct Person[]) persons#0 persons = { { id: (byte) 1, initials: (const string) $0 }, { id: (byte) 8, initials: (const string) $1 } } +(const struct Person[]) persons#0 persons = { { id: (byte) 1, initials: (string) "jgr" }, { id: (byte) 8, initials: (string) "hbg" } } (void()) print_person((struct Person*) print_person::person) (byte~) print_person::$0 reg byte a 4.0 (byte[4]) print_person::$3 $3 zp ZP_WORD:4 4.0 diff --git a/src/test/ref/struct-ptr-29.asm b/src/test/ref/struct-ptr-29.asm index 97d56d84e..b0d80830f 100644 --- a/src/test/ref/struct-ptr-29.asm +++ b/src/test/ref/struct-ptr-29.asm @@ -63,13 +63,13 @@ print_person: { inc.z i jmp b1 } - name1: .text "jesper" + _0: .text "jesper" .byte 0 - name2: .text "repsej" - .byte 0 - DIGIT: .text "0123456789" + _1: .text "repsej" .byte 0 persons: .byte 4 - .word name1 + .word _0 .byte 7 - .word name2 + .word _1 + DIGIT: .text "0123456789" + .byte 0 diff --git a/src/test/ref/struct-ptr-29.log b/src/test/ref/struct-ptr-29.log index 87646d079..ebad59ccd 100644 --- a/src/test/ref/struct-ptr-29.log +++ b/src/test/ref/struct-ptr-29.log @@ -4,17 +4,13 @@ Rewriting struct pointer member access *((struct Person*) print_person::person). Rewriting struct pointer member access *((struct Person*) print_person::person).name Rewriting struct pointer member access *((struct Person*) print_person::person).name Warning! Adding boolean cast to non-boolean condition *(*((byte**) print_person::$1) + (byte) print_person::i) -Identified constant variable (byte*) name1 -Identified constant variable (byte*) name2 Culled Empty Block (label) print_person::@4 Culled Empty Block (label) print_person::@5 Culled Empty Block (label) print_person::@6 CONTROL FLOW GRAPH SSA @begin: scope:[] from - (byte*) name1#0 ← (const string) $0 - (byte*) name2#0 ← (const string) $1 - (struct Person[2]) persons#0 ← { { (number) 4, (byte*) name1#0 }, { (number) 7, (byte*) name2#0 } } + (struct Person[2]) persons#0 ← { { (number) 4, (const string) $0 }, { (number) 7, (const string) $1 } } to:@1 main: scope:[main] from @2 (byte) idx#18 ← phi( @2/(byte) idx#20 ) @@ -140,10 +136,6 @@ SYMBOL TABLE SSA (label) main::@1 (label) main::@2 (label) main::@return -(byte*) name1 -(byte*) name1#0 -(byte*) name2 -(byte*) name2#0 (struct Person[2]) persons (struct Person[2]) persons#0 (void()) print_person((struct Person*) print_person::person) @@ -175,7 +167,7 @@ Adding number conversion cast (unumber) 0 in (byte) idx#3 ← (number) 0 Adding number conversion cast (unumber) 0 in (byte) print_person::i#0 ← (number) 0 Adding number conversion cast (unumber) 0 in (bool~) print_person::$3 ← (number) 0 != *(*((byte**) print_person::$1) + (byte) print_person::i#2) Successful SSA optimization PassNAddNumberTypeConversions -Added casts to value list in (struct Person[2]) persons#0 ← (struct Person[2]){ (struct Person){ (byte)(number) 4, (byte*) name1#0 }, (struct Person){ (byte)(number) 7, (byte*) name2#0 } } +Added casts to value list in (struct Person[2]) persons#0 ← (struct Person[2]){ (struct Person){ (byte)(number) 4, (const string) $0 }, (struct Person){ (byte)(number) 7, (const string) $1 } } Successful SSA optimization PassNAddInitializerValueListTypeCasts Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400 Inlining cast (byte) idx#3 ← (unumber)(number) 0 @@ -215,16 +207,20 @@ Identical Phi Values (byte) idx#1 (byte) idx#16 Identical Phi Values (struct Person*) print_person::person#3 (struct Person*) print_person::person#2 Identical Phi Values (byte) idx#17 (byte) idx#1 Successful SSA optimization Pass2IdenticalPhiElimination -Simple Condition (bool~) print_person::$3 [32] if((byte) 0!=*(*((byte**) print_person::$1) + (byte) print_person::i#2)) goto print_person::@2 +Simple Condition (bool~) print_person::$3 [30] if((byte) 0!=*(*((byte**) print_person::$1) + (byte) print_person::i#2)) goto print_person::@2 Successful SSA optimization Pass2ConditionalJumpSimplification -Rewriting array member address-of to pointer addition [5] (struct Person*) print_person::person#0 ← (struct Person[2]) persons#0 + (byte~) main::$4 -Rewriting array member address-of to pointer addition [11] (struct Person*) print_person::person#1 ← (struct Person[2]) persons#0 + (byte~) main::$5 +Rewriting array member address-of to pointer addition [3] (struct Person*) print_person::person#0 ← (struct Person[2]) persons#0 + (byte~) main::$4 +Rewriting array member address-of to pointer addition [9] (struct Person*) print_person::person#1 ← (struct Person[2]) persons#0 + (byte~) main::$5 Successful SSA optimization PassNArrayElementAddressOfRewriting -Constant right-side identified [4] (byte~) main::$4 ← (byte) 0 * (const byte) SIZEOF_STRUCT_PERSON -Constant right-side identified [10] (byte~) main::$5 ← (byte) 1 * (const byte) SIZEOF_STRUCT_PERSON +Constant right-side identified [2] (byte~) main::$4 ← (byte) 0 * (const byte) SIZEOF_STRUCT_PERSON +Constant right-side identified [8] (byte~) main::$5 ← (byte) 1 * (const byte) SIZEOF_STRUCT_PERSON Successful SSA optimization Pass2ConstantRValueConsolidation -Constant (const byte*) name1#0 = $0 -Constant (const byte*) name2#0 = $1 +Identified constant from value list (struct Person) { id: (byte) 4, name: (const string) $0 } +Identified constant from value list (struct Person) { id: (byte) 7, name: (const string) $1 } +Successful SSA optimization Pass2ConstantInitializerValueLists +Identified constant from value list (struct Person[2]) { { id: (byte) 4, name: (const string) $0 }, { id: (byte) 7, name: (const string) $1 } } +Successful SSA optimization Pass2ConstantInitializerValueLists +Constant (const struct Person[2]) persons#0 = { { id: 4, name: $0 }, { id: 7, name: $1 } } Constant (const byte) main::$4 = 0*SIZEOF_STRUCT_PERSON Constant (const byte) main::$5 = 1*SIZEOF_STRUCT_PERSON Constant (const byte*) SCREEN#0 = (byte*) 1024 @@ -232,45 +228,35 @@ Constant (const byte) idx#20 = 0 Constant (const byte[]) DIGIT#0 = $2 Constant (const byte) print_person::i#0 = 0 Successful SSA optimization Pass2ConstantIdentification -Converting *(pointer+n) to pointer[n] [24] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + *((byte*) print_person::$0)) -- *((byte*)print_person::person#2 + OFFSET_STRUCT_PERSON_ID) -Converting *(pointer+n) to pointer[n] [32] if((byte) 0!=*(*((byte**) print_person::$1) + (byte) print_person::i#2)) goto print_person::@2 -- *((byte**)print_person::person#2 + OFFSET_STRUCT_PERSON_NAME) -Converting *(pointer+n) to pointer[n] [35] *((const byte*) SCREEN#0 + (byte) idx#14) ← *(*((byte**) print_person::$2) + (byte) print_person::i#2) -- *((byte**)print_person::person#2 + OFFSET_STRUCT_PERSON_NAME) +Converting *(pointer+n) to pointer[n] [22] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + *((byte*) print_person::$0)) -- *((byte*)print_person::person#2 + OFFSET_STRUCT_PERSON_ID) +Converting *(pointer+n) to pointer[n] [30] if((byte) 0!=*(*((byte**) print_person::$1) + (byte) print_person::i#2)) goto print_person::@2 -- *((byte**)print_person::person#2 + OFFSET_STRUCT_PERSON_NAME) +Converting *(pointer+n) to pointer[n] [33] *((const byte*) SCREEN#0 + (byte) idx#14) ← *(*((byte**) print_person::$2) + (byte) print_person::i#2) -- *((byte**)print_person::person#2 + OFFSET_STRUCT_PERSON_NAME) Successful SSA optimization Pass2InlineDerefIdx Simplifying constant evaluating to zero (byte) 0*(const byte) SIZEOF_STRUCT_PERSON in Successful SSA optimization PassNSimplifyConstantZero -Simplifying expression containing zero persons#0 in [5] (struct Person*) print_person::person#0 ← (struct Person[2]) persons#0 + (const byte) main::$4 -Simplifying expression containing zero (byte*)print_person::person#2 in [23] (byte*) print_person::$0 ← (byte*)(struct Person*) print_person::person#2 + (const byte) OFFSET_STRUCT_PERSON_ID -Simplifying expression containing zero (byte*)print_person::person#2 in [24] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + *((byte*)(struct Person*) print_person::person#2 + (const byte) OFFSET_STRUCT_PERSON_ID)) +Simplifying expression containing zero persons#0 in [3] (struct Person*) print_person::person#0 ← (const struct Person[2]) persons#0 + (const byte) main::$4 +Simplifying expression containing zero (byte*)print_person::person#2 in [21] (byte*) print_person::$0 ← (byte*)(struct Person*) print_person::person#2 + (const byte) OFFSET_STRUCT_PERSON_ID +Simplifying expression containing zero (byte*)print_person::person#2 in [22] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + *((byte*)(struct Person*) print_person::person#2 + (const byte) OFFSET_STRUCT_PERSON_ID)) Successful SSA optimization PassNSimplifyExpressionWithZero -Eliminating unused variable (byte*) print_person::$0 and assignment [7] (byte*) print_person::$0 ← (byte*)(struct Person*) print_person::person#2 -Eliminating unused variable (byte**) print_person::$1 and assignment [13] (byte**) print_person::$1 ← (byte**)(struct Person*) print_person::person#2 + (const byte) OFFSET_STRUCT_PERSON_NAME -Eliminating unused variable (byte**) print_person::$2 and assignment [15] (byte**) print_person::$2 ← (byte**)(struct Person*) print_person::person#2 + (const byte) OFFSET_STRUCT_PERSON_NAME +Eliminating unused variable (byte*) print_person::$0 and assignment [6] (byte*) print_person::$0 ← (byte*)(struct Person*) print_person::person#2 +Eliminating unused variable (byte**) print_person::$1 and assignment [12] (byte**) print_person::$1 ← (byte**)(struct Person*) print_person::person#2 + (const byte) OFFSET_STRUCT_PERSON_NAME +Eliminating unused variable (byte**) print_person::$2 and assignment [14] (byte**) print_person::$2 ← (byte**)(struct Person*) print_person::person#2 + (const byte) OFFSET_STRUCT_PERSON_NAME Eliminating unused constant (const byte) main::$4 Eliminating unused constant (const byte) OFFSET_STRUCT_PERSON_ID Successful SSA optimization PassNEliminateUnusedVars -Identified constant from value list (struct Person) { id: (byte) 4, name: (const byte*) name1#0 } -Identified constant from value list (struct Person) { id: (byte) 7, name: (const byte*) name2#0 } -Successful SSA optimization Pass2ConstantInitializerValueLists -Identified constant from value list (struct Person[2]) { { id: (byte) 4, name: (const byte*) name1#0 }, { id: (byte) 7, name: (const byte*) name2#0 } } -Successful SSA optimization Pass2ConstantInitializerValueLists -Constant (const struct Person[2]) persons#0 = { { id: 4, name: name1#0 }, { id: 7, name: name2#0 } } -Successful SSA optimization Pass2ConstantIdentification -Constant (const struct Person*) print_person::person#0 = persons#0 -Successful SSA optimization Pass2ConstantIdentification -Constant right-side identified [1] (struct Person*) print_person::person#1 ← (const struct Person[2]) persons#0 + (const byte) main::$5 +Constant right-side identified [2] (struct Person*) print_person::person#1 ← (const struct Person[2]) persons#0 + (const byte) main::$5 Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const struct Person*) print_person::person#0 = persons#0 Constant (const struct Person*) print_person::person#1 = persons#0+main::$5 Successful SSA optimization Pass2ConstantIdentification Inlining constant with var siblings (const byte) print_person::i#0 Inlining constant with var siblings (const struct Person*) print_person::person#0 Inlining constant with var siblings (const struct Person*) print_person::person#1 Inlining constant with var siblings (const byte) idx#20 -Constant inlined idx#20 = (byte) 0 -Constant inlined print_person::i#0 = (byte) 0 Constant inlined main::$5 = (byte) 1*(const byte) SIZEOF_STRUCT_PERSON +Constant inlined idx#20 = (byte) 0 Constant inlined print_person::person#0 = (const struct Person[2]) persons#0 -Constant inlined $0 = (const byte*) name1#0 -Constant inlined $1 = (const byte*) name2#0 +Constant inlined print_person::i#0 = (byte) 0 Constant inlined $2 = (const byte[]) DIGIT#0 Constant inlined print_person::person#1 = (const struct Person[2]) persons#0+(byte) 1*(const byte) SIZEOF_STRUCT_PERSON Successful SSA optimization Pass2ConstantInlining @@ -362,8 +348,6 @@ VARIABLE REGISTER WEIGHTS (byte) idx#5 4.0 (byte) idx#6 11.0 (void()) main() -(byte*) name1 -(byte*) name2 (struct Person[2]) persons (void()) print_person((struct Person*) print_person::person) (byte) print_person::i @@ -543,16 +527,16 @@ print_person: { jmp b1 } // File Data - name1: .text "jesper" + _0: .text "jesper" .byte 0 - name2: .text "repsej" - .byte 0 - DIGIT: .text "0123456789" + _1: .text "repsej" .byte 0 persons: .byte 4 - .word name1 + .word _0 .byte 7 - .word name2 + .word _1 + DIGIT: .text "0123456789" + .byte 0 REGISTER UPLIFT POTENTIAL REGISTERS Statement [10] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + *((byte*)(struct Person*) print_person::person#2)) [ print_person::person#2 idx#13 ] ( main:2::print_person:5 [ print_person::person#2 idx#13 ] main:2::print_person:7 [ print_person::person#2 idx#13 ] ) always clobbers reg byte a reg byte y @@ -727,16 +711,16 @@ print_person: { jmp b1 } // File Data - name1: .text "jesper" + _0: .text "jesper" .byte 0 - name2: .text "repsej" - .byte 0 - DIGIT: .text "0123456789" + _1: .text "repsej" .byte 0 persons: .byte 4 - .word name1 + .word _0 .byte 7 - .word name2 + .word _1 + DIGIT: .text "0123456789" + .byte 0 ASSEMBLER OPTIMIZATIONS Removing instruction jmp b1 @@ -770,6 +754,8 @@ Removing instruction bbegin: Succesful ASM optimization Pass5UnusedLabelElimination FINAL SYMBOL TABLE +(const string) $0 $0 = (string) "jesper" +(const string) $1 $1 = (string) "repsej" (label) @1 (label) @begin (label) @end @@ -791,12 +777,8 @@ FINAL SYMBOL TABLE (void()) main() (label) main::@1 (label) main::@return -(byte*) name1 -(const byte*) name1#0 name1 = (string) "jesper" -(byte*) name2 -(const byte*) name2#0 name2 = (string) "repsej" (struct Person[2]) persons -(const struct Person[2]) persons#0 persons = { { id: (byte) 4, name: (const byte*) name1#0 }, { id: (byte) 7, name: (const byte*) name2#0 } } +(const struct Person[2]) persons#0 persons = { { id: (byte) 4, name: (const string) $0 }, { id: (byte) 7, name: (const string) $1 } } (void()) print_person((struct Person*) print_person::person) (label) print_person::@1 (label) print_person::@2 @@ -943,14 +925,14 @@ print_person: { jmp b1 } // File Data - name1: .text "jesper" + _0: .text "jesper" .byte 0 - name2: .text "repsej" - .byte 0 - DIGIT: .text "0123456789" + _1: .text "repsej" .byte 0 persons: .byte 4 - .word name1 + .word _0 .byte 7 - .word name2 + .word _1 + DIGIT: .text "0123456789" + .byte 0 diff --git a/src/test/ref/struct-ptr-29.sym b/src/test/ref/struct-ptr-29.sym index 01ee691e6..f72e3ac80 100644 --- a/src/test/ref/struct-ptr-29.sym +++ b/src/test/ref/struct-ptr-29.sym @@ -1,3 +1,5 @@ +(const string) $0 $0 = (string) "jesper" +(const string) $1 $1 = (string) "repsej" (label) @1 (label) @begin (label) @end @@ -19,12 +21,8 @@ (void()) main() (label) main::@1 (label) main::@return -(byte*) name1 -(const byte*) name1#0 name1 = (string) "jesper" -(byte*) name2 -(const byte*) name2#0 name2 = (string) "repsej" (struct Person[2]) persons -(const struct Person[2]) persons#0 persons = { { id: (byte) 4, name: (const byte*) name1#0 }, { id: (byte) 7, name: (const byte*) name2#0 } } +(const struct Person[2]) persons#0 persons = { { id: (byte) 4, name: (const string) $0 }, { id: (byte) 7, name: (const string) $1 } } (void()) print_person((struct Person*) print_person::person) (label) print_person::@1 (label) print_person::@2 diff --git a/src/test/ref/struct-ptr-31.asm b/src/test/ref/struct-ptr-31.asm index bdc9e809c..ceaa13df6 100644 --- a/src/test/ref/struct-ptr-31.asm +++ b/src/test/ref/struct-ptr-31.asm @@ -63,10 +63,6 @@ print_person: { iny jmp b1 } - _0: .text "jesper" - .byte 0 - _1: .text "henriette" - .byte 0 persons: .byte 4 .text "jesper" .byte 0 diff --git a/src/test/ref/struct-ptr-31.log b/src/test/ref/struct-ptr-31.log index e44ee0693..cc8cbd6e9 100644 --- a/src/test/ref/struct-ptr-31.log +++ b/src/test/ref/struct-ptr-31.log @@ -239,6 +239,9 @@ Constant inlined print_person::i#0 = (byte) 0 Constant inlined $2 = (const byte[]) DIGIT#0 Constant inlined print_person::person#1 = (const struct Person[]) persons#0+(byte) 1*(const byte) SIZEOF_STRUCT_PERSON Successful SSA optimization Pass2ConstantInlining +Constant array inlined into struct $0 = (string) "jesper" +Constant array inlined into struct $1 = (string) "henriette" +Successful SSA optimization Pass2ArrayInStructInlining Adding NOP phi() at start of @begin Adding NOP phi() at start of @1 Adding NOP phi() at start of @2 @@ -522,10 +525,6 @@ print_person: { jmp b1 } // File Data - _0: .text "jesper" - .byte 0 - _1: .text "henriette" - .byte 0 persons: .byte 4 .text "jesper" .byte 0 @@ -715,10 +714,6 @@ print_person: { jmp b1 } // File Data - _0: .text "jesper" - .byte 0 - _1: .text "henriette" - .byte 0 persons: .byte 4 .text "jesper" .byte 0 @@ -762,8 +757,6 @@ Removing instruction bbegin: Succesful ASM optimization Pass5UnusedLabelElimination FINAL SYMBOL TABLE -(const string) $0 $0 = (string) "jesper" -(const string) $1 $1 = (string) "henriette" (label) @1 (label) @begin (label) @end @@ -786,7 +779,7 @@ FINAL SYMBOL TABLE (label) main::@1 (label) main::@return (struct Person[]) persons -(const struct Person[]) persons#0 persons = { { id: (byte) 4, name: (const string) $0 }, { id: (byte) 7, name: (const string) $1 } } +(const struct Person[]) persons#0 persons = { { id: (byte) 4, name: (string) "jesper" }, { id: (byte) 7, name: (string) "henriette" } } (void()) print_person((struct Person*) print_person::person) (byte[$10]) print_person::$1 $1 zp ZP_WORD:4 22.0 (byte[$10]) print_person::$2 $2 zp ZP_WORD:6 22.0 @@ -939,10 +932,6 @@ print_person: { jmp b1 } // File Data - _0: .text "jesper" - .byte 0 - _1: .text "henriette" - .byte 0 persons: .byte 4 .text "jesper" .byte 0 diff --git a/src/test/ref/struct-ptr-31.sym b/src/test/ref/struct-ptr-31.sym index 11a609275..df56ffe0c 100644 --- a/src/test/ref/struct-ptr-31.sym +++ b/src/test/ref/struct-ptr-31.sym @@ -1,5 +1,3 @@ -(const string) $0 $0 = (string) "jesper" -(const string) $1 $1 = (string) "henriette" (label) @1 (label) @begin (label) @end @@ -22,7 +20,7 @@ (label) main::@1 (label) main::@return (struct Person[]) persons -(const struct Person[]) persons#0 persons = { { id: (byte) 4, name: (const string) $0 }, { id: (byte) 7, name: (const string) $1 } } +(const struct Person[]) persons#0 persons = { { id: (byte) 4, name: (string) "jesper" }, { id: (byte) 7, name: (string) "henriette" } } (void()) print_person((struct Person*) print_person::person) (byte[$10]) print_person::$1 $1 zp ZP_WORD:4 22.0 (byte[$10]) print_person::$2 $2 zp ZP_WORD:6 22.0 diff --git a/src/test/ref/struct-ptr-33.asm b/src/test/ref/struct-ptr-33.asm index 58471dccf..2be75ffaf 100644 --- a/src/test/ref/struct-ptr-33.asm +++ b/src/test/ref/struct-ptr-33.asm @@ -13,10 +13,6 @@ main: { sta SCREEN+1 rts } - _0: .text "jesper" - .byte 0 - _1: .text "henry" - .byte 0 persons: .byte 7 .text "jesper" .byte 0 diff --git a/src/test/ref/struct-ptr-33.log b/src/test/ref/struct-ptr-33.log index 8812efa9f..3e75827e7 100644 --- a/src/test/ref/struct-ptr-33.log +++ b/src/test/ref/struct-ptr-33.log @@ -106,6 +106,9 @@ Constant inlined main::$1 = (byte[$d])(const struct Person*) main::person#1+(con Constant inlined main::person#0 = (const struct Person[2]) persons#0 Constant inlined main::$0 = (byte[$d])(const struct Person[2]) persons#0+(const byte) OFFSET_STRUCT_PERSON_NAME Successful SSA optimization Pass2ConstantInlining +Constant array inlined into struct $0 = (string) "jesper" +Constant array inlined into struct $1 = (string) "henry" +Successful SSA optimization Pass2ArrayInStructInlining Consolidated array index constant in *((byte[$d])persons#0+OFFSET_STRUCT_PERSON_NAME+2) Consolidated array index constant in *((byte[$d])main::person#1+OFFSET_STRUCT_PERSON_NAME+2) Consolidated array index constant in *(main::SCREEN#0+1) @@ -197,10 +200,6 @@ main: { rts } // File Data - _0: .text "jesper" - .byte 0 - _1: .text "henry" - .byte 0 persons: .byte 7 .text "jesper" .byte 0 @@ -266,10 +265,6 @@ main: { rts } // File Data - _0: .text "jesper" - .byte 0 - _1: .text "henry" - .byte 0 persons: .byte 7 .text "jesper" .byte 0 @@ -300,8 +295,6 @@ Removing instruction bbegin: Succesful ASM optimization Pass5UnusedLabelElimination FINAL SYMBOL TABLE -(const string) $0 $0 = (string) "jesper" -(const string) $1 $1 = (string) "henry" (label) @1 (label) @begin (label) @end @@ -317,7 +310,7 @@ FINAL SYMBOL TABLE (struct Person*) main::person (const struct Person*) main::person#1 person = (const struct Person[2]) persons#0+(const byte) SIZEOF_STRUCT_PERSON (struct Person[2]) persons -(const struct Person[2]) persons#0 persons = { { id: (byte) 7, name: (const string) $0, age: (word) $141 }, { id: (byte) 9, name: (const string) $1, age: (word) $7b } } +(const struct Person[2]) persons#0 persons = { { id: (byte) 7, name: (string) "jesper", age: (word) $141 }, { id: (byte) 9, name: (string) "henry", age: (word) $7b } } @@ -357,10 +350,6 @@ main: { rts } // File Data - _0: .text "jesper" - .byte 0 - _1: .text "henry" - .byte 0 persons: .byte 7 .text "jesper" .byte 0 diff --git a/src/test/ref/struct-ptr-33.sym b/src/test/ref/struct-ptr-33.sym index 58ef8317e..4a708a8bd 100644 --- a/src/test/ref/struct-ptr-33.sym +++ b/src/test/ref/struct-ptr-33.sym @@ -1,5 +1,3 @@ -(const string) $0 $0 = (string) "jesper" -(const string) $1 $1 = (string) "henry" (label) @1 (label) @begin (label) @end @@ -15,5 +13,5 @@ (struct Person*) main::person (const struct Person*) main::person#1 person = (const struct Person[2]) persons#0+(const byte) SIZEOF_STRUCT_PERSON (struct Person[2]) persons -(const struct Person[2]) persons#0 persons = { { id: (byte) 7, name: (const string) $0, age: (word) $141 }, { id: (byte) 9, name: (const string) $1, age: (word) $7b } } +(const struct Person[2]) persons#0 persons = { { id: (byte) 7, name: (string) "jesper", age: (word) $141 }, { id: (byte) 9, name: (string) "henry", age: (word) $7b } } diff --git a/src/test/ref/struct-ptr-34.asm b/src/test/ref/struct-ptr-34.asm new file mode 100644 index 000000000..682586e71 --- /dev/null +++ b/src/test/ref/struct-ptr-34.asm @@ -0,0 +1,62 @@ +// Example of a struct containing an array +// It works on the surface - but illustrates the problem with structs containing arrays treating them like pointers. +// https://gitlab.com/camelot/kickc/issues/312 +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + .const jesper_id = 4 + .const henriette_id = 7 + lda #jesper_name + sta.z print_person.person_name+1 + ldy #0 + ldx #jesper_id + jsr print_person + lda #henriette_name + sta.z print_person.person_name+1 + ldx #henriette_id + jsr print_person + rts + jesper_name: .text "jesper" + .byte 0 + .fill 9, 0 + henriette_name: .text "henriette" + .byte 0 + .fill 6, 0 +} +// print_person(byte register(X) person_id, byte[$10] zeropage(2) person_name) +print_person: { + .label person_name = 2 + lda DIGIT,x + sta SCREEN,y + tya + tax + inx + lda #' ' + sta SCREEN,x + inx + ldy #0 + b1: + lda (person_name),y + cmp #0 + bne b2 + lda #' ' + sta SCREEN,x + txa + tay + iny + rts + b2: + lda (person_name),y + sta SCREEN,x + inx + iny + jmp b1 +} + DIGIT: .text "0123456789" + .byte 0 diff --git a/src/test/ref/struct-ptr-34.cfg b/src/test/ref/struct-ptr-34.cfg new file mode 100644 index 000000000..2a1b77108 --- /dev/null +++ b/src/test/ref/struct-ptr-34.cfg @@ -0,0 +1,46 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + [5] call print_person + to:main::@1 +main::@1: scope:[main] from main + [6] phi() + [7] call print_person + to:main::@return +main::@return: scope:[main] from main::@1 + [8] return + to:@return +print_person: scope:[print_person] from main main::@1 + [9] (byte[$10]) print_person::person_name#4 ← phi( main/(const byte[$10]) main::jesper_name#0 main::@1/(const byte[$10]) main::henriette_name#0 ) + [9] (byte) idx#13 ← phi( main/(byte) 0 main::@1/(byte) idx#16 ) + [9] (byte) print_person::person_id#2 ← phi( main/(const byte) main::jesper_id#0 main::@1/(const byte) main::henriette_id#0 ) + [10] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + (byte) print_person::person_id#2) + [11] (byte) idx#4 ← ++ (byte) idx#13 + [12] *((const byte*) SCREEN#0 + (byte) idx#4) ← (byte) ' ' + [13] (byte) idx#5 ← ++ (byte) idx#4 + to:print_person::@1 +print_person::@1: scope:[print_person] from print_person print_person::@2 + [14] (byte) idx#14 ← phi( print_person/(byte) idx#5 print_person::@2/(byte) idx#6 ) + [14] (byte) print_person::i#2 ← phi( print_person/(byte) 0 print_person::@2/(byte) print_person::i#1 ) + [15] if((byte) 0!=*((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2)) goto print_person::@2 + to:print_person::@3 +print_person::@3: scope:[print_person] from print_person::@1 + [16] *((const byte*) SCREEN#0 + (byte) idx#14) ← (byte) ' ' + [17] (byte) idx#16 ← ++ (byte) idx#14 + to:print_person::@return +print_person::@return: scope:[print_person] from print_person::@3 + [18] return + to:@return +print_person::@2: scope:[print_person] from print_person::@1 + [19] *((const byte*) SCREEN#0 + (byte) idx#14) ← *((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2) + [20] (byte) idx#6 ← ++ (byte) idx#14 + [21] (byte) print_person::i#1 ← ++ (byte) print_person::i#2 + to:print_person::@1 diff --git a/src/test/ref/struct-ptr-34.log b/src/test/ref/struct-ptr-34.log new file mode 100644 index 000000000..b1c56b460 --- /dev/null +++ b/src/test/ref/struct-ptr-34.log @@ -0,0 +1,913 @@ +Fixing struct type size struct Person to 17 +Fixing struct type size struct Person to 17 +Fixing struct type size struct Person to 17 +Created struct value member variable (byte) main::jesper_id +Created struct value member variable (byte[$10]) main::jesper_name +Converted struct value to member variables (struct Person) main::jesper +Created struct value member variable (byte) main::henriette_id +Created struct value member variable (byte[$10]) main::henriette_name +Converted struct value to member variables (struct Person) main::henriette +Created struct value member variable (byte) print_person::person_id +Created struct value member variable (byte[$10]) print_person::person_name +Converted struct value to member variables (struct Person) print_person::person +Converted procedure struct value parameter to member unwinding (void()) print_person((byte) print_person::person_id , (byte[$10]) print_person::person_name) +Adding struct value list initializer (byte) main::jesper_id ← (number) 4 +Adding struct value list initializer (byte[$10]) main::jesper_name ← (string) "jesper" +Converted procedure struct value parameter to member unwinding in call (void~) main::$0 ← call print_person (byte) main::jesper_id (byte[$10]) main::jesper_name +Adding struct value list initializer (byte) main::henriette_id ← (number) 7 +Adding struct value list initializer (byte[$10]) main::henriette_name ← (string) "henriette" +Converted procedure struct value parameter to member unwinding in call (void~) main::$1 ← call print_person (byte) main::henriette_id (byte[$10]) main::henriette_name +Replacing struct member reference (struct Person) print_person::person.id with member unwinding reference (byte) print_person::person_id +Replacing struct member reference (struct Person) print_person::person.name with member unwinding reference (byte[$10]) print_person::person_name +Replacing struct member reference (struct Person) print_person::person.name with member unwinding reference (byte[$10]) print_person::person_name +Warning! Adding boolean cast to non-boolean condition *((byte[$10]) print_person::person_name + (byte) print_person::i) +Identified constant variable (byte) main::jesper_id +Identified constant variable (byte[$10]) main::jesper_name +Identified constant variable (byte) main::henriette_id +Identified constant variable (byte[$10]) main::henriette_name +Culled Empty Block (label) print_person::@4 +Culled Empty Block (label) print_person::@5 +Culled Empty Block (label) print_person::@6 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 +main: scope:[main] from @2 + (byte) idx#18 ← phi( @2/(byte) idx#20 ) + (byte) main::jesper_id#0 ← (number) 4 + (byte[$10]) main::jesper_name#0 ← (const string) main::$2 + (byte) print_person::person_id#0 ← (byte) main::jesper_id#0 + (byte[$10]) print_person::person_name#0 ← (byte[$10]) main::jesper_name#0 + call print_person + to:main::@1 +main::@1: scope:[main] from main + (byte) idx#10 ← phi( main/(byte) idx#8 ) + (byte) idx#0 ← (byte) idx#10 + (byte) main::henriette_id#0 ← (number) 7 + (byte[$10]) main::henriette_name#0 ← (const string) main::$3 + (byte) print_person::person_id#1 ← (byte) main::henriette_id#0 + (byte[$10]) print_person::person_name#1 ← (byte[$10]) main::henriette_name#0 + call print_person + to:main::@2 +main::@2: scope:[main] from main::@1 + (byte) idx#11 ← phi( main::@1/(byte) idx#8 ) + (byte) idx#1 ← (byte) idx#11 + to:main::@return +main::@return: scope:[main] from main::@2 + (byte) idx#12 ← phi( main::@2/(byte) idx#1 ) + (byte) idx#2 ← (byte) idx#12 + return + to:@return +@1: scope:[] from @begin + (byte*) SCREEN#0 ← ((byte*)) (number) $400 + (byte) idx#3 ← (number) 0 + (byte[]) DIGIT#0 ← (const string) $0 + to:@2 +print_person: scope:[print_person] from main main::@1 + (byte[$10]) print_person::person_name#4 ← phi( main/(byte[$10]) print_person::person_name#0 main::@1/(byte[$10]) print_person::person_name#1 ) + (byte) idx#13 ← phi( main/(byte) idx#18 main::@1/(byte) idx#0 ) + (byte) print_person::person_id#2 ← phi( main/(byte) print_person::person_id#0 main::@1/(byte) print_person::person_id#1 ) + *((byte*) SCREEN#0 + (byte) idx#13) ← *((byte[]) DIGIT#0 + (byte) print_person::person_id#2) + (byte) idx#4 ← ++ (byte) idx#13 + *((byte*) SCREEN#0 + (byte) idx#4) ← (byte) ' ' + (byte) idx#5 ← ++ (byte) idx#4 + (byte) print_person::i#0 ← (number) 0 + to:print_person::@1 +print_person::@1: scope:[print_person] from print_person print_person::@2 + (byte) idx#19 ← phi( print_person/(byte) idx#5 print_person::@2/(byte) idx#6 ) + (byte) print_person::i#2 ← phi( print_person/(byte) print_person::i#0 print_person::@2/(byte) print_person::i#1 ) + (byte[$10]) print_person::person_name#2 ← phi( print_person/(byte[$10]) print_person::person_name#4 print_person::@2/(byte[$10]) print_person::person_name#3 ) + (bool~) print_person::$0 ← (number) 0 != *((byte[$10]) print_person::person_name#2 + (byte) print_person::i#2) + if((bool~) print_person::$0) goto print_person::@2 + to:print_person::@3 +print_person::@2: scope:[print_person] from print_person::@1 + (byte) idx#14 ← phi( print_person::@1/(byte) idx#19 ) + (byte) print_person::i#3 ← phi( print_person::@1/(byte) print_person::i#2 ) + (byte[$10]) print_person::person_name#3 ← phi( print_person::@1/(byte[$10]) print_person::person_name#2 ) + *((byte*) SCREEN#0 + (byte) idx#14) ← *((byte[$10]) print_person::person_name#3 + (byte) print_person::i#3) + (byte) idx#6 ← ++ (byte) idx#14 + (byte) print_person::i#1 ← ++ (byte) print_person::i#3 + to:print_person::@1 +print_person::@3: scope:[print_person] from print_person::@1 + (byte) idx#15 ← phi( print_person::@1/(byte) idx#19 ) + *((byte*) SCREEN#0 + (byte) idx#15) ← (byte) ' ' + (byte) idx#7 ← ++ (byte) idx#15 + to:print_person::@return +print_person::@return: scope:[print_person] from print_person::@3 + (byte) idx#16 ← phi( print_person::@3/(byte) idx#7 ) + (byte) idx#8 ← (byte) idx#16 + return + to:@return +@2: scope:[] from @1 + (byte) idx#20 ← phi( @1/(byte) idx#3 ) + call main + to:@3 +@3: scope:[] from @2 + (byte) idx#17 ← phi( @2/(byte) idx#2 ) + (byte) idx#9 ← (byte) idx#17 + to:@end +@end: scope:[] from @3 + +SYMBOL TABLE SSA +(const string) $0 = (string) "0123456789" +(label) @1 +(label) @2 +(label) @3 +(label) @begin +(label) @end +(byte[]) DIGIT +(byte[]) DIGIT#0 +(byte) Person::id +(byte[$10]) Person::name +(byte*) SCREEN +(byte*) SCREEN#0 +(byte) idx +(byte) idx#0 +(byte) idx#1 +(byte) idx#10 +(byte) idx#11 +(byte) idx#12 +(byte) idx#13 +(byte) idx#14 +(byte) idx#15 +(byte) idx#16 +(byte) idx#17 +(byte) idx#18 +(byte) idx#19 +(byte) idx#2 +(byte) idx#20 +(byte) idx#3 +(byte) idx#4 +(byte) idx#5 +(byte) idx#6 +(byte) idx#7 +(byte) idx#8 +(byte) idx#9 +(void()) main() +(const string) main::$2 = (string) "jesper" +(const string) main::$3 = (string) "henriette" +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::henriette_id +(byte) main::henriette_id#0 +(byte[$10]) main::henriette_name +(byte[$10]) main::henriette_name#0 +(byte) main::jesper_id +(byte) main::jesper_id#0 +(byte[$10]) main::jesper_name +(byte[$10]) main::jesper_name#0 +(void()) print_person((byte) print_person::person_id , (byte[$10]) print_person::person_name) +(bool~) print_person::$0 +(label) print_person::@1 +(label) print_person::@2 +(label) print_person::@3 +(label) print_person::@return +(byte) print_person::i +(byte) print_person::i#0 +(byte) print_person::i#1 +(byte) print_person::i#2 +(byte) print_person::i#3 +(struct Person) print_person::person +(byte) print_person::person_id +(byte) print_person::person_id#0 +(byte) print_person::person_id#1 +(byte) print_person::person_id#2 +(byte[$10]) print_person::person_name +(byte[$10]) print_person::person_name#0 +(byte[$10]) print_person::person_name#1 +(byte[$10]) print_person::person_name#2 +(byte[$10]) print_person::person_name#3 +(byte[$10]) print_person::person_name#4 + +Adding number conversion cast (unumber) 4 in (byte) main::jesper_id#0 ← (number) 4 +Adding number conversion cast (unumber) 7 in (byte) main::henriette_id#0 ← (number) 7 +Adding number conversion cast (unumber) 0 in (byte) idx#3 ← (number) 0 +Adding number conversion cast (unumber) 0 in (byte) print_person::i#0 ← (number) 0 +Adding number conversion cast (unumber) 0 in (bool~) print_person::$0 ← (number) 0 != *((byte[$10]) print_person::person_name#2 + (byte) print_person::i#2) +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte) main::jesper_id#0 ← (unumber)(number) 4 +Inlining cast (byte) main::henriette_id#0 ← (unumber)(number) 7 +Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400 +Inlining cast (byte) idx#3 ← (unumber)(number) 0 +Inlining cast (byte) print_person::i#0 ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant integer cast 4 +Simplifying constant integer cast 7 +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 4 +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) idx#0 = (byte) idx#10 +Alias (byte) idx#1 = (byte) idx#11 (byte) idx#12 (byte) idx#2 +Alias (byte[$10]) print_person::person_name#2 = (byte[$10]) print_person::person_name#3 +Alias (byte) print_person::i#2 = (byte) print_person::i#3 +Alias (byte) idx#14 = (byte) idx#19 (byte) idx#15 +Alias (byte) idx#16 = (byte) idx#7 (byte) idx#8 +Alias (byte) idx#20 = (byte) idx#3 +Alias (byte) idx#17 = (byte) idx#9 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (byte) idx#18 (byte) idx#20 +Identical Phi Values (byte) idx#0 (byte) idx#16 +Identical Phi Values (byte) idx#1 (byte) idx#16 +Identical Phi Values (byte[$10]) print_person::person_name#2 (byte[$10]) print_person::person_name#4 +Identical Phi Values (byte) idx#17 (byte) idx#1 +Successful SSA optimization Pass2IdenticalPhiElimination +Simple Condition (bool~) print_person::$0 [29] if((byte) 0!=*((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2)) goto print_person::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte) main::jesper_id#0 = 4 +Constant (const byte[$10]) main::jesper_name#0 = main::$2 +Constant (const byte) main::henriette_id#0 = 7 +Constant (const byte[$10]) main::henriette_name#0 = main::$3 +Constant (const byte*) SCREEN#0 = (byte*) 1024 +Constant (const byte) idx#20 = 0 +Constant (const byte[]) DIGIT#0 = $0 +Constant (const byte) print_person::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Constant (const byte) print_person::person_id#0 = main::jesper_id#0 +Constant (const byte[$10]) print_person::person_name#0 = main::jesper_name#0 +Constant (const byte) print_person::person_id#1 = main::henriette_id#0 +Constant (const byte[$10]) print_person::person_name#1 = main::henriette_name#0 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings (const byte) print_person::i#0 +Inlining constant with var siblings (const byte) print_person::person_id#0 +Inlining constant with var siblings (const byte[$10]) print_person::person_name#0 +Inlining constant with var siblings (const byte) print_person::person_id#1 +Inlining constant with var siblings (const byte[$10]) print_person::person_name#1 +Inlining constant with var siblings (const byte) idx#20 +Constant inlined idx#20 = (byte) 0 +Constant inlined print_person::person_id#1 = (const byte) main::henriette_id#0 +Constant inlined print_person::person_id#0 = (const byte) main::jesper_id#0 +Constant inlined main::$2 = (const byte[$10]) main::jesper_name#0 +Constant inlined print_person::i#0 = (byte) 0 +Constant inlined print_person::person_name#1 = (const byte[$10]) main::henriette_name#0 +Constant inlined main::$3 = (const byte[$10]) main::henriette_name#0 +Constant inlined $0 = (const byte[]) DIGIT#0 +Constant inlined print_person::person_name#0 = (const byte[$10]) main::jesper_name#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 @3 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@2 +CALL GRAPH +Calls in [] to main:3 +Calls in [main] to print_person:7 print_person:9 + +Created 5 initial phi equivalence classes +Coalesced [8] idx#21 ← idx#16 +Coalesced [17] idx#22 ← idx#5 +Coalesced [26] print_person::i#4 ← print_person::i#1 +Coalesced [27] idx#23 ← idx#6 +Coalesced down to 5 phi equivalence classes +Culled Empty Block (label) @1 +Culled Empty Block (label) @3 +Culled Empty Block (label) main::@2 +Renumbering block @2 to @1 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@1 + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + [5] call print_person + to:main::@1 +main::@1: scope:[main] from main + [6] phi() + [7] call print_person + to:main::@return +main::@return: scope:[main] from main::@1 + [8] return + to:@return +print_person: scope:[print_person] from main main::@1 + [9] (byte[$10]) print_person::person_name#4 ← phi( main/(const byte[$10]) main::jesper_name#0 main::@1/(const byte[$10]) main::henriette_name#0 ) + [9] (byte) idx#13 ← phi( main/(byte) 0 main::@1/(byte) idx#16 ) + [9] (byte) print_person::person_id#2 ← phi( main/(const byte) main::jesper_id#0 main::@1/(const byte) main::henriette_id#0 ) + [10] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + (byte) print_person::person_id#2) + [11] (byte) idx#4 ← ++ (byte) idx#13 + [12] *((const byte*) SCREEN#0 + (byte) idx#4) ← (byte) ' ' + [13] (byte) idx#5 ← ++ (byte) idx#4 + to:print_person::@1 +print_person::@1: scope:[print_person] from print_person print_person::@2 + [14] (byte) idx#14 ← phi( print_person/(byte) idx#5 print_person::@2/(byte) idx#6 ) + [14] (byte) print_person::i#2 ← phi( print_person/(byte) 0 print_person::@2/(byte) print_person::i#1 ) + [15] if((byte) 0!=*((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2)) goto print_person::@2 + to:print_person::@3 +print_person::@3: scope:[print_person] from print_person::@1 + [16] *((const byte*) SCREEN#0 + (byte) idx#14) ← (byte) ' ' + [17] (byte) idx#16 ← ++ (byte) idx#14 + to:print_person::@return +print_person::@return: scope:[print_person] from print_person::@3 + [18] return + to:@return +print_person::@2: scope:[print_person] from print_person::@1 + [19] *((const byte*) SCREEN#0 + (byte) idx#14) ← *((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2) + [20] (byte) idx#6 ← ++ (byte) idx#14 + [21] (byte) print_person::i#1 ← ++ (byte) print_person::i#2 + to:print_person::@1 + + +VARIABLE REGISTER WEIGHTS +(byte[]) DIGIT +(byte) Person::id +(byte[$10]) Person::name +(byte*) SCREEN +(byte) idx +(byte) idx#13 3.0 +(byte) idx#14 9.75 +(byte) idx#16 1.0 +(byte) idx#4 3.0 +(byte) idx#5 4.0 +(byte) idx#6 11.0 +(void()) main() +(byte) main::henriette_id +(byte[$10]) main::henriette_name +(byte) main::jesper_id +(byte[$10]) main::jesper_name +(void()) print_person((byte) print_person::person_id , (byte[$10]) print_person::person_name) +(byte) print_person::i +(byte) print_person::i#1 22.0 +(byte) print_person::i#2 11.0 +(struct Person) print_person::person +(byte) print_person::person_id +(byte) print_person::person_id#2 2.0 +(byte[$10]) print_person::person_name +(byte[$10]) print_person::person_name#4 2.2 + +Initial phi equivalence classes +[ print_person::person_id#2 ] +[ idx#13 idx#16 ] +[ print_person::person_name#4 ] +[ print_person::i#2 print_person::i#1 ] +[ idx#14 idx#5 idx#6 ] +Added variable idx#4 to zero page equivalence class [ idx#4 ] +Complete equivalence classes +[ print_person::person_id#2 ] +[ idx#13 idx#16 ] +[ print_person::person_name#4 ] +[ print_person::i#2 print_person::i#1 ] +[ idx#14 idx#5 idx#6 ] +[ idx#4 ] +Allocated zp ZP_BYTE:2 [ print_person::person_id#2 ] +Allocated zp ZP_BYTE:3 [ idx#13 idx#16 ] +Allocated zp ZP_WORD:4 [ print_person::person_name#4 ] +Allocated zp ZP_BYTE:6 [ print_person::i#2 print_person::i#1 ] +Allocated zp ZP_BYTE:7 [ idx#14 idx#5 idx#6 ] +Allocated zp ZP_BYTE:8 [ idx#4 ] + +INITIAL ASM +Target platform is c64basic / MOS6502X + // File Comments +// Example of a struct containing an array +// It works on the surface - but illustrates the problem with structs containing arrays treating them like pointers. +// https://gitlab.com/camelot/kickc/issues/312 + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + .label idx = 8 + .label idx_5 = 7 + .label idx_6 = 7 + .label idx_13 = 3 + .label idx_14 = 7 + .label idx_16 = 3 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .const jesper_id = 4 + .const henriette_id = 7 + // [5] call print_person + // [9] phi from main to print_person [phi:main->print_person] + print_person_from_main: + // [9] phi (byte[$10]) print_person::person_name#4 = (const byte[$10]) main::jesper_name#0 [phi:main->print_person#0] -- pbuz1=pbuc1 + lda #jesper_name + sta.z print_person.person_name+1 + // [9] phi (byte) idx#13 = (byte) 0 [phi:main->print_person#1] -- vbuz1=vbuc1 + lda #0 + sta.z idx_13 + // [9] phi (byte) print_person::person_id#2 = (const byte) main::jesper_id#0 [phi:main->print_person#2] -- vbuz1=vbuc1 + lda #jesper_id + sta.z print_person.person_id + jsr print_person + // [6] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + jmp b1 + // main::@1 + b1: + // [7] call print_person + // [9] phi from main::@1 to print_person [phi:main::@1->print_person] + print_person_from_b1: + // [9] phi (byte[$10]) print_person::person_name#4 = (const byte[$10]) main::henriette_name#0 [phi:main::@1->print_person#0] -- pbuz1=pbuc1 + lda #henriette_name + sta.z print_person.person_name+1 + // [9] phi (byte) idx#13 = (byte) idx#16 [phi:main::@1->print_person#1] -- register_copy + // [9] phi (byte) print_person::person_id#2 = (const byte) main::henriette_id#0 [phi:main::@1->print_person#2] -- vbuz1=vbuc1 + lda #henriette_id + sta.z print_person.person_id + jsr print_person + jmp breturn + // main::@return + breturn: + // [8] return + rts + jesper_name: .text "jesper" + .byte 0 + .fill 9, 0 + henriette_name: .text "henriette" + .byte 0 + .fill 6, 0 +} + // print_person +// print_person(byte zeropage(2) person_id, byte[$10] zeropage(4) person_name) +print_person: { + .label i = 6 + .label person_id = 2 + .label person_name = 4 + // [10] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + (byte) print_person::person_id#2) -- pbuc1_derefidx_vbuz1=pbuc2_derefidx_vbuz2 + ldy.z person_id + lda DIGIT,y + ldy.z idx_13 + sta SCREEN,y + // [11] (byte) idx#4 ← ++ (byte) idx#13 -- vbuz1=_inc_vbuz2 + ldy.z idx_13 + iny + sty.z idx + // [12] *((const byte*) SCREEN#0 + (byte) idx#4) ← (byte) ' ' -- pbuc1_derefidx_vbuz1=vbuc2 + lda #' ' + ldy.z idx + sta SCREEN,y + // [13] (byte) idx#5 ← ++ (byte) idx#4 -- vbuz1=_inc_vbuz2 + ldy.z idx + iny + sty.z idx_5 + // [14] phi from print_person to print_person::@1 [phi:print_person->print_person::@1] + b1_from_print_person: + // [14] phi (byte) idx#14 = (byte) idx#5 [phi:print_person->print_person::@1#0] -- register_copy + // [14] phi (byte) print_person::i#2 = (byte) 0 [phi:print_person->print_person::@1#1] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp b1 + // print_person::@1 + b1: + // [15] if((byte) 0!=*((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2)) goto print_person::@2 -- vbuc1_neq_pbuz1_derefidx_vbuz2_then_la1 + ldy.z i + lda (person_name),y + cmp #0 + bne b2 + jmp b3 + // print_person::@3 + b3: + // [16] *((const byte*) SCREEN#0 + (byte) idx#14) ← (byte) ' ' -- pbuc1_derefidx_vbuz1=vbuc2 + lda #' ' + ldy.z idx_14 + sta SCREEN,y + // [17] (byte) idx#16 ← ++ (byte) idx#14 -- vbuz1=_inc_vbuz2 + ldy.z idx_14 + iny + sty.z idx_16 + jmp breturn + // print_person::@return + breturn: + // [18] return + rts + // print_person::@2 + b2: + // [19] *((const byte*) SCREEN#0 + (byte) idx#14) ← *((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2) -- pbuc1_derefidx_vbuz1=pbuz2_derefidx_vbuz3 + ldx.z idx_14 + ldy.z i + lda (person_name),y + sta SCREEN,x + // [20] (byte) idx#6 ← ++ (byte) idx#14 -- vbuz1=_inc_vbuz1 + inc.z idx_6 + // [21] (byte) print_person::i#1 ← ++ (byte) print_person::i#2 -- vbuz1=_inc_vbuz1 + inc.z i + // [14] phi from print_person::@2 to print_person::@1 [phi:print_person::@2->print_person::@1] + b1_from_b2: + // [14] phi (byte) idx#14 = (byte) idx#6 [phi:print_person::@2->print_person::@1#0] -- register_copy + // [14] phi (byte) print_person::i#2 = (byte) print_person::i#1 [phi:print_person::@2->print_person::@1#1] -- register_copy + jmp b1 +} + // File Data + DIGIT: .text "0123456789" + .byte 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [10] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + (byte) print_person::person_id#2) [ idx#13 print_person::person_name#4 ] ( main:2::print_person:5 [ idx#13 print_person::person_name#4 ] main:2::print_person:7 [ idx#13 print_person::person_name#4 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:3 [ idx#13 idx#16 ] +Statement [12] *((const byte*) SCREEN#0 + (byte) idx#4) ← (byte) ' ' [ print_person::person_name#4 idx#4 ] ( main:2::print_person:5 [ print_person::person_name#4 idx#4 ] main:2::print_person:7 [ print_person::person_name#4 idx#4 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:8 [ idx#4 ] +Statement [15] if((byte) 0!=*((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2)) goto print_person::@2 [ print_person::person_name#4 print_person::i#2 idx#14 ] ( main:2::print_person:5 [ print_person::person_name#4 print_person::i#2 idx#14 ] main:2::print_person:7 [ print_person::person_name#4 print_person::i#2 idx#14 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:6 [ print_person::i#2 print_person::i#1 ] +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:7 [ idx#14 idx#5 idx#6 ] +Statement [16] *((const byte*) SCREEN#0 + (byte) idx#14) ← (byte) ' ' [ idx#14 ] ( main:2::print_person:5 [ idx#14 ] main:2::print_person:7 [ idx#14 ] ) always clobbers reg byte a +Statement [19] *((const byte*) SCREEN#0 + (byte) idx#14) ← *((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2) [ print_person::person_name#4 print_person::i#2 idx#14 ] ( main:2::print_person:5 [ print_person::person_name#4 print_person::i#2 idx#14 ] main:2::print_person:7 [ print_person::person_name#4 print_person::i#2 idx#14 ] ) always clobbers reg byte a +Statement [10] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + (byte) print_person::person_id#2) [ idx#13 print_person::person_name#4 ] ( main:2::print_person:5 [ idx#13 print_person::person_name#4 ] main:2::print_person:7 [ idx#13 print_person::person_name#4 ] ) always clobbers reg byte a +Statement [12] *((const byte*) SCREEN#0 + (byte) idx#4) ← (byte) ' ' [ print_person::person_name#4 idx#4 ] ( main:2::print_person:5 [ print_person::person_name#4 idx#4 ] main:2::print_person:7 [ print_person::person_name#4 idx#4 ] ) always clobbers reg byte a +Statement [15] if((byte) 0!=*((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2)) goto print_person::@2 [ print_person::person_name#4 print_person::i#2 idx#14 ] ( main:2::print_person:5 [ print_person::person_name#4 print_person::i#2 idx#14 ] main:2::print_person:7 [ print_person::person_name#4 print_person::i#2 idx#14 ] ) always clobbers reg byte a +Statement [16] *((const byte*) SCREEN#0 + (byte) idx#14) ← (byte) ' ' [ idx#14 ] ( main:2::print_person:5 [ idx#14 ] main:2::print_person:7 [ idx#14 ] ) always clobbers reg byte a +Statement [19] *((const byte*) SCREEN#0 + (byte) idx#14) ← *((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2) [ print_person::person_name#4 print_person::i#2 idx#14 ] ( main:2::print_person:5 [ print_person::person_name#4 print_person::i#2 idx#14 ] main:2::print_person:7 [ print_person::person_name#4 print_person::i#2 idx#14 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ print_person::person_id#2 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:3 [ idx#13 idx#16 ] : zp ZP_BYTE:3 , reg byte x , reg byte y , +Potential registers zp ZP_WORD:4 [ print_person::person_name#4 ] : zp ZP_WORD:4 , +Potential registers zp ZP_BYTE:6 [ print_person::i#2 print_person::i#1 ] : zp ZP_BYTE:6 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:7 [ idx#14 idx#5 idx#6 ] : zp ZP_BYTE:7 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:8 [ idx#4 ] : zp ZP_BYTE:8 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [print_person] 33: zp ZP_BYTE:6 [ print_person::i#2 print_person::i#1 ] 2.2: zp ZP_WORD:4 [ print_person::person_name#4 ] 2: zp ZP_BYTE:2 [ print_person::person_id#2 ] +Uplift Scope [] 24.75: zp ZP_BYTE:7 [ idx#14 idx#5 idx#6 ] 4: zp ZP_BYTE:3 [ idx#13 idx#16 ] 3: zp ZP_BYTE:8 [ idx#4 ] +Uplift Scope [Person] +Uplift Scope [main] + +Uplifting [print_person] best 545 combination reg byte y [ print_person::i#2 print_person::i#1 ] zp ZP_WORD:4 [ print_person::person_name#4 ] reg byte x [ print_person::person_id#2 ] +Uplifting [] best 463 combination reg byte x [ idx#14 idx#5 idx#6 ] reg byte y [ idx#13 idx#16 ] reg byte x [ idx#4 ] +Uplifting [Person] best 463 combination +Uplifting [main] best 463 combination +Allocated (was zp ZP_WORD:4) zp ZP_WORD:2 [ print_person::person_name#4 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Example of a struct containing an array +// It works on the surface - but illustrates the problem with structs containing arrays treating them like pointers. +// https://gitlab.com/camelot/kickc/issues/312 + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .const jesper_id = 4 + .const henriette_id = 7 + // [5] call print_person + // [9] phi from main to print_person [phi:main->print_person] + print_person_from_main: + // [9] phi (byte[$10]) print_person::person_name#4 = (const byte[$10]) main::jesper_name#0 [phi:main->print_person#0] -- pbuz1=pbuc1 + lda #jesper_name + sta.z print_person.person_name+1 + // [9] phi (byte) idx#13 = (byte) 0 [phi:main->print_person#1] -- vbuyy=vbuc1 + ldy #0 + // [9] phi (byte) print_person::person_id#2 = (const byte) main::jesper_id#0 [phi:main->print_person#2] -- vbuxx=vbuc1 + ldx #jesper_id + jsr print_person + // [6] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + jmp b1 + // main::@1 + b1: + // [7] call print_person + // [9] phi from main::@1 to print_person [phi:main::@1->print_person] + print_person_from_b1: + // [9] phi (byte[$10]) print_person::person_name#4 = (const byte[$10]) main::henriette_name#0 [phi:main::@1->print_person#0] -- pbuz1=pbuc1 + lda #henriette_name + sta.z print_person.person_name+1 + // [9] phi (byte) idx#13 = (byte) idx#16 [phi:main::@1->print_person#1] -- register_copy + // [9] phi (byte) print_person::person_id#2 = (const byte) main::henriette_id#0 [phi:main::@1->print_person#2] -- vbuxx=vbuc1 + ldx #henriette_id + jsr print_person + jmp breturn + // main::@return + breturn: + // [8] return + rts + jesper_name: .text "jesper" + .byte 0 + .fill 9, 0 + henriette_name: .text "henriette" + .byte 0 + .fill 6, 0 +} + // print_person +// print_person(byte register(X) person_id, byte[$10] zeropage(2) person_name) +print_person: { + .label person_name = 2 + // [10] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + (byte) print_person::person_id#2) -- pbuc1_derefidx_vbuyy=pbuc2_derefidx_vbuxx + lda DIGIT,x + sta SCREEN,y + // [11] (byte) idx#4 ← ++ (byte) idx#13 -- vbuxx=_inc_vbuyy + tya + tax + inx + // [12] *((const byte*) SCREEN#0 + (byte) idx#4) ← (byte) ' ' -- pbuc1_derefidx_vbuxx=vbuc2 + lda #' ' + sta SCREEN,x + // [13] (byte) idx#5 ← ++ (byte) idx#4 -- vbuxx=_inc_vbuxx + inx + // [14] phi from print_person to print_person::@1 [phi:print_person->print_person::@1] + b1_from_print_person: + // [14] phi (byte) idx#14 = (byte) idx#5 [phi:print_person->print_person::@1#0] -- register_copy + // [14] phi (byte) print_person::i#2 = (byte) 0 [phi:print_person->print_person::@1#1] -- vbuyy=vbuc1 + ldy #0 + jmp b1 + // print_person::@1 + b1: + // [15] if((byte) 0!=*((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2)) goto print_person::@2 -- vbuc1_neq_pbuz1_derefidx_vbuyy_then_la1 + lda (person_name),y + cmp #0 + bne b2 + jmp b3 + // print_person::@3 + b3: + // [16] *((const byte*) SCREEN#0 + (byte) idx#14) ← (byte) ' ' -- pbuc1_derefidx_vbuxx=vbuc2 + lda #' ' + sta SCREEN,x + // [17] (byte) idx#16 ← ++ (byte) idx#14 -- vbuyy=_inc_vbuxx + txa + tay + iny + jmp breturn + // print_person::@return + breturn: + // [18] return + rts + // print_person::@2 + b2: + // [19] *((const byte*) SCREEN#0 + (byte) idx#14) ← *((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2) -- pbuc1_derefidx_vbuxx=pbuz1_derefidx_vbuyy + lda (person_name),y + sta SCREEN,x + // [20] (byte) idx#6 ← ++ (byte) idx#14 -- vbuxx=_inc_vbuxx + inx + // [21] (byte) print_person::i#1 ← ++ (byte) print_person::i#2 -- vbuyy=_inc_vbuyy + iny + // [14] phi from print_person::@2 to print_person::@1 [phi:print_person::@2->print_person::@1] + b1_from_b2: + // [14] phi (byte) idx#14 = (byte) idx#6 [phi:print_person::@2->print_person::@1#0] -- register_copy + // [14] phi (byte) print_person::i#2 = (byte) print_person::i#1 [phi:print_person::@2->print_person::@1#1] -- register_copy + jmp b1 +} + // File Data + DIGIT: .text "0123456789" + .byte 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp breturn +Removing instruction jmp b1 +Removing instruction jmp b3 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_main: +Removing instruction print_person_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction print_person_from_main: +Removing instruction b1: +Removing instruction breturn: +Removing instruction b1_from_print_person: +Removing instruction b3: +Removing instruction breturn: +Removing instruction b1_from_b2: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(byte[]) DIGIT +(const byte[]) DIGIT#0 DIGIT = (string) "0123456789" +(byte) Person::id +(byte[$10]) Person::name +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(byte) idx +(byte) idx#13 reg byte y 3.0 +(byte) idx#14 reg byte x 9.75 +(byte) idx#16 reg byte y 1.0 +(byte) idx#4 reg byte x 3.0 +(byte) idx#5 reg byte x 4.0 +(byte) idx#6 reg byte x 11.0 +(void()) main() +(label) main::@1 +(label) main::@return +(byte) main::henriette_id +(const byte) main::henriette_id#0 henriette_id = (byte) 7 +(byte[$10]) main::henriette_name +(const byte[$10]) main::henriette_name#0 henriette_name = (string) "henriette" +(byte) main::jesper_id +(const byte) main::jesper_id#0 jesper_id = (byte) 4 +(byte[$10]) main::jesper_name +(const byte[$10]) main::jesper_name#0 jesper_name = (string) "jesper" +(void()) print_person((byte) print_person::person_id , (byte[$10]) print_person::person_name) +(label) print_person::@1 +(label) print_person::@2 +(label) print_person::@3 +(label) print_person::@return +(byte) print_person::i +(byte) print_person::i#1 reg byte y 22.0 +(byte) print_person::i#2 reg byte y 11.0 +(struct Person) print_person::person +(byte) print_person::person_id +(byte) print_person::person_id#2 reg byte x 2.0 +(byte[$10]) print_person::person_name +(byte[$10]) print_person::person_name#4 person_name zp ZP_WORD:2 2.2 + +reg byte x [ print_person::person_id#2 ] +reg byte y [ idx#13 idx#16 ] +zp ZP_WORD:2 [ print_person::person_name#4 ] +reg byte y [ print_person::i#2 print_person::i#1 ] +reg byte x [ idx#14 idx#5 idx#6 ] +reg byte x [ idx#4 ] + + +FINAL ASSEMBLER +Score: 382 + + // File Comments +// Example of a struct containing an array +// It works on the surface - but illustrates the problem with structs containing arrays treating them like pointers. +// https://gitlab.com/camelot/kickc/issues/312 + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + .const jesper_id = 4 + .const henriette_id = 7 + // print_person(jesper) + // [5] call print_person + // [9] phi from main to print_person [phi:main->print_person] + // [9] phi (byte[$10]) print_person::person_name#4 = (const byte[$10]) main::jesper_name#0 [phi:main->print_person#0] -- pbuz1=pbuc1 + lda #jesper_name + sta.z print_person.person_name+1 + // [9] phi (byte) idx#13 = (byte) 0 [phi:main->print_person#1] -- vbuyy=vbuc1 + ldy #0 + // [9] phi (byte) print_person::person_id#2 = (const byte) main::jesper_id#0 [phi:main->print_person#2] -- vbuxx=vbuc1 + ldx #jesper_id + jsr print_person + // [6] phi from main to main::@1 [phi:main->main::@1] + // main::@1 + // print_person(henriette) + // [7] call print_person + // [9] phi from main::@1 to print_person [phi:main::@1->print_person] + // [9] phi (byte[$10]) print_person::person_name#4 = (const byte[$10]) main::henriette_name#0 [phi:main::@1->print_person#0] -- pbuz1=pbuc1 + lda #henriette_name + sta.z print_person.person_name+1 + // [9] phi (byte) idx#13 = (byte) idx#16 [phi:main::@1->print_person#1] -- register_copy + // [9] phi (byte) print_person::person_id#2 = (const byte) main::henriette_id#0 [phi:main::@1->print_person#2] -- vbuxx=vbuc1 + ldx #henriette_id + jsr print_person + // main::@return + // } + // [8] return + rts + jesper_name: .text "jesper" + .byte 0 + .fill 9, 0 + henriette_name: .text "henriette" + .byte 0 + .fill 6, 0 +} + // print_person +// print_person(byte register(X) person_id, byte[$10] zeropage(2) person_name) +print_person: { + .label person_name = 2 + // SCREEN[idx++] = DIGIT[person.id] + // [10] *((const byte*) SCREEN#0 + (byte) idx#13) ← *((const byte[]) DIGIT#0 + (byte) print_person::person_id#2) -- pbuc1_derefidx_vbuyy=pbuc2_derefidx_vbuxx + lda DIGIT,x + sta SCREEN,y + // SCREEN[idx++] = DIGIT[person.id]; + // [11] (byte) idx#4 ← ++ (byte) idx#13 -- vbuxx=_inc_vbuyy + tya + tax + inx + // SCREEN[idx++] = ' ' + // [12] *((const byte*) SCREEN#0 + (byte) idx#4) ← (byte) ' ' -- pbuc1_derefidx_vbuxx=vbuc2 + lda #' ' + sta SCREEN,x + // SCREEN[idx++] = ' '; + // [13] (byte) idx#5 ← ++ (byte) idx#4 -- vbuxx=_inc_vbuxx + inx + // [14] phi from print_person to print_person::@1 [phi:print_person->print_person::@1] + // [14] phi (byte) idx#14 = (byte) idx#5 [phi:print_person->print_person::@1#0] -- register_copy + // [14] phi (byte) print_person::i#2 = (byte) 0 [phi:print_person->print_person::@1#1] -- vbuyy=vbuc1 + ldy #0 + // print_person::@1 + b1: + // for(byte i=0; person.name[i]; i++) + // [15] if((byte) 0!=*((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2)) goto print_person::@2 -- vbuc1_neq_pbuz1_derefidx_vbuyy_then_la1 + lda (person_name),y + cmp #0 + bne b2 + // print_person::@3 + // SCREEN[idx++] = ' ' + // [16] *((const byte*) SCREEN#0 + (byte) idx#14) ← (byte) ' ' -- pbuc1_derefidx_vbuxx=vbuc2 + lda #' ' + sta SCREEN,x + // SCREEN[idx++] = ' '; + // [17] (byte) idx#16 ← ++ (byte) idx#14 -- vbuyy=_inc_vbuxx + txa + tay + iny + // print_person::@return + // } + // [18] return + rts + // print_person::@2 + b2: + // SCREEN[idx++] = person.name[i] + // [19] *((const byte*) SCREEN#0 + (byte) idx#14) ← *((byte[$10]) print_person::person_name#4 + (byte) print_person::i#2) -- pbuc1_derefidx_vbuxx=pbuz1_derefidx_vbuyy + lda (person_name),y + sta SCREEN,x + // SCREEN[idx++] = person.name[i]; + // [20] (byte) idx#6 ← ++ (byte) idx#14 -- vbuxx=_inc_vbuxx + inx + // for(byte i=0; person.name[i]; i++) + // [21] (byte) print_person::i#1 ← ++ (byte) print_person::i#2 -- vbuyy=_inc_vbuyy + iny + // [14] phi from print_person::@2 to print_person::@1 [phi:print_person::@2->print_person::@1] + // [14] phi (byte) idx#14 = (byte) idx#6 [phi:print_person::@2->print_person::@1#0] -- register_copy + // [14] phi (byte) print_person::i#2 = (byte) print_person::i#1 [phi:print_person::@2->print_person::@1#1] -- register_copy + jmp b1 +} + // File Data + DIGIT: .text "0123456789" + .byte 0 + diff --git a/src/test/ref/struct-ptr-34.sym b/src/test/ref/struct-ptr-34.sym new file mode 100644 index 000000000..ce93b6fe9 --- /dev/null +++ b/src/test/ref/struct-ptr-34.sym @@ -0,0 +1,47 @@ +(label) @1 +(label) @begin +(label) @end +(byte[]) DIGIT +(const byte[]) DIGIT#0 DIGIT = (string) "0123456789" +(byte) Person::id +(byte[$10]) Person::name +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(byte) idx +(byte) idx#13 reg byte y 3.0 +(byte) idx#14 reg byte x 9.75 +(byte) idx#16 reg byte y 1.0 +(byte) idx#4 reg byte x 3.0 +(byte) idx#5 reg byte x 4.0 +(byte) idx#6 reg byte x 11.0 +(void()) main() +(label) main::@1 +(label) main::@return +(byte) main::henriette_id +(const byte) main::henriette_id#0 henriette_id = (byte) 7 +(byte[$10]) main::henriette_name +(const byte[$10]) main::henriette_name#0 henriette_name = (string) "henriette" +(byte) main::jesper_id +(const byte) main::jesper_id#0 jesper_id = (byte) 4 +(byte[$10]) main::jesper_name +(const byte[$10]) main::jesper_name#0 jesper_name = (string) "jesper" +(void()) print_person((byte) print_person::person_id , (byte[$10]) print_person::person_name) +(label) print_person::@1 +(label) print_person::@2 +(label) print_person::@3 +(label) print_person::@return +(byte) print_person::i +(byte) print_person::i#1 reg byte y 22.0 +(byte) print_person::i#2 reg byte y 11.0 +(struct Person) print_person::person +(byte) print_person::person_id +(byte) print_person::person_id#2 reg byte x 2.0 +(byte[$10]) print_person::person_name +(byte[$10]) print_person::person_name#4 person_name zp ZP_WORD:2 2.2 + +reg byte x [ print_person::person_id#2 ] +reg byte y [ idx#13 idx#16 ] +zp ZP_WORD:2 [ print_person::person_name#4 ] +reg byte y [ print_person::i#2 print_person::i#1 ] +reg byte x [ idx#14 idx#5 idx#6 ] +reg byte x [ idx#4 ]