diff --git a/src/main/fragment/pssz1=vwuc1.asm b/src/main/fragment/pssz1=vwuc1.asm new file mode 100644 index 000000000..8716dc864 --- /dev/null +++ b/src/main/fragment/pssz1=vwuc1.asm @@ -0,0 +1,4 @@ +lda #<{c1} +sta {z1} +lda #>{c1} +sta {z1}+1 diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 58df39f05..44220c492 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -168,7 +168,7 @@ public class Compiler { new Pass1AssertReturn(program).execute(); new Pass1AssertUsedVars(program).execute(); - new PassNSizeOfSimplification(program).execute(); + new PassNSizeOfSimplification(program).execute(); // Needed to eliminate sizeof() referencing pointer value variables new Pass1UnwindStructValues(program).execute(); if(getLog().isVerbosePass1CreateSsa()) { @@ -178,7 +178,9 @@ public class Compiler { new Pass1FixLValuesLoHi(program).execute(); new Pass1AssertNoLValueIntermediate(program).execute(); - new Pass1PointerSizeofFix(program).execute(); + new Pass1PointerSizeofFix(program).execute(); // After this point in the code all pointer math is byte-based + new PassNStructPointerRewriting(program).execute(); + new PassNAddBooleanCasts(program).execute(); new PassNAddTypeConversionAssignment(program).execute(); new Pass1EarlyConstantIdentification(program).execute(); @@ -281,7 +283,6 @@ public class Compiler { optimizations.add(new Pass2ConstantIfs(program)); optimizations.add(new Pass2ConstantStringConsolidation(program)); optimizations.add(new Pass2RangeResolving(program)); - //optimizations.add(new Pass2BooleanizeConditions(program)); optimizations.add(new Pass2ComparisonOptimization(program)); optimizations.add(new Pass2InlineDerefIdx(program)); optimizations.add(new Pass2DeInlineWordDerefIdx(program)); diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstance.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstance.java index 92ff9d606..99a7fc713 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstance.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstance.java @@ -99,7 +99,7 @@ public class AsmFragmentInstance { StructMemberRef structMemberRef = (StructMemberRef) boundValue; StructDefinition structDefinition = program.getScope().getStructDefinition(structMemberRef); Variable structMember = structDefinition.getMember(structMemberRef.getMemberName()); - int memberByteOffset = structDefinition.getMemberByteOffset(structMember); + long memberByteOffset = structDefinition.getMemberByteOffset(structMember); VariableRef struct = (VariableRef) structMemberRef.getStruct(); Variable structVar = program.getScope().getVariable( struct); Registers.RegisterZpStruct structRegister = (Registers.RegisterZpStruct) structVar.getAllocation(); diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java index c50725d1b..e3ba7c90c 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFragmentInstanceSpecFactory.java @@ -309,7 +309,7 @@ public class AsmFragmentInstanceSpecFactory { StructMemberRef structMemberRef = (StructMemberRef) value; StructDefinition structDefinition = program.getScope().getStructDefinition(structMemberRef); Variable structMember = structDefinition.getMember(structMemberRef.getMemberName()); - int memberByteOffset = structDefinition.getMemberByteOffset(structMember); + long memberByteOffset = structDefinition.getMemberByteOffset(structMember); RValue struct = structMemberRef.getStruct(); if(struct instanceof VariableRef) { diff --git a/src/main/java/dk/camelot64/kickc/model/Registers.java b/src/main/java/dk/camelot64/kickc/model/Registers.java index 0b36bb7d9..caf716ad3 100644 --- a/src/main/java/dk/camelot64/kickc/model/Registers.java +++ b/src/main/java/dk/camelot64/kickc/model/Registers.java @@ -159,8 +159,8 @@ public class Registers { super(zp); } - public RegisterZpStructMember getMemberRegister(int memberByteOffset) { - return new RegisterZpStructMember(getZp()+memberByteOffset); + public RegisterZpStructMember getMemberRegister(long memberByteOffset) { + return new RegisterZpStructMember((int) (getZp()+memberByteOffset)); } @Override diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/StructDefinition.java b/src/main/java/dk/camelot64/kickc/model/symbols/StructDefinition.java index 32d7d3adc..96063b497 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/StructDefinition.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/StructDefinition.java @@ -37,8 +37,8 @@ public class StructDefinition extends Scope { * @param member The member to find offset for * @return The byte offset of the start of the member data */ - public int getMemberByteOffset(Variable member) { - int byteOffset=0; + public long getMemberByteOffset(Variable member) { + long byteOffset=0; for(Variable structMember : getAllVariables(false)) { if(structMember.equals(member)) { break; diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNStructPointerRewriting.java b/src/main/java/dk/camelot64/kickc/passes/PassNStructPointerRewriting.java new file mode 100644 index 000000000..14b9af119 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/PassNStructPointerRewriting.java @@ -0,0 +1,96 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.CompileError; +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.iterator.ProgramValueIterator; +import dk.camelot64.kickc.model.operators.Operators; +import dk.camelot64.kickc.model.statements.StatementAssignment; +import dk.camelot64.kickc.model.symbols.*; +import dk.camelot64.kickc.model.types.SymbolType; +import dk.camelot64.kickc.model.types.SymbolTypeInference; +import dk.camelot64.kickc.model.types.SymbolTypePointer; +import dk.camelot64.kickc.model.types.SymbolTypeStruct; +import dk.camelot64.kickc.model.values.*; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Rewrite access to member of dereferenced struct (*ptr_struct).x to reference the member directly *((*typeof_x)(ptr_struct+OFFSET_STRUCT_AAA_X)) + */ +public class PassNStructPointerRewriting extends Pass2SsaOptimization { + + public PassNStructPointerRewriting(Program program) { + super(program); + } + + @Override + public boolean step() { + AtomicBoolean modified = new AtomicBoolean(false); + ProgramValueIterator.execute(getProgram(), (programValue, currentStmt, stmtIt, currentBlock) -> { + if(programValue.get() instanceof StructMemberRef) { + StructMemberRef structMemberRef = (StructMemberRef) programValue.get(); + RValue struct = structMemberRef.getStruct(); + if(struct instanceof PointerDereferenceSimple) { + RValue structPointer = ((PointerDereferenceSimple) struct).getPointer(); + // We have a match for (*ptr_struct).x + SymbolType structType = SymbolTypeInference.inferType(getScope(), struct); + if(!(structType instanceof SymbolTypeStruct)) { + throw new CompileError("Accessing member of a non-struct ", currentStmt.getSource()); + } + StructDefinition structDefinition = ((SymbolTypeStruct) structType).getStructDefinition(getScope()); + ConstantRef memberOffsetConstant = getMemberOffsetConstant(getScope(), structDefinition, structMemberRef.getMemberName()); + SymbolType memberType = SymbolTypeInference.inferType(getScope(), structMemberRef); + getLog().append("Rewriting struct pointer member access " + programValue.get().toString(getProgram())); + // Cast struct pointer to the type of the member + CastValue structTypedPointer = new CastValue(new SymbolTypePointer(memberType), structPointer); + // Create temporary variable to hold pointer to member ($1) + Scope scope = getScope().getScope(currentBlock.getScope()); + VariableIntermediate memberAddress = scope.addVariableIntermediate(); + memberAddress.setType(new SymbolTypePointer(memberType)); + // Add statement $1 = ptr_struct + OFFSET_STRUCT_NAME_MEMBER + stmtIt.previous(); + stmtIt.add(new StatementAssignment(memberAddress.getRef(), structTypedPointer, Operators.PLUS, memberOffsetConstant, currentStmt.getSource(), currentStmt.getComments())); + stmtIt.next(); + // Replace (*ptr_struct).x with *($1) + programValue.set(new PointerDereferenceSimple(memberAddress.getRef())); + modified.set(true); + } + } + }); + return modified.get(); + } + + /** + * Get the constant variable containing the (byte) index of a specific member + * + * @param programScope The program scope (used for finding/adding the constant). + * @param structDefinition The struct + * @param memberName The name of the struct member + * @return The constant variable + */ + private static ConstantRef getMemberOffsetConstant(ProgramScope programScope, StructDefinition structDefinition, String memberName) { + String typeConstName = getMemberOffsetConstantName(structDefinition, memberName); + ConstantVar memberOffsetConstant = programScope.getConstant(typeConstName); + if(memberOffsetConstant == null) { + // Constant not found - create it + Variable memberDef = structDefinition.getMember(memberName); + long memberByteOffset = structDefinition.getMemberByteOffset(memberDef); + memberOffsetConstant = new ConstantVar(typeConstName, programScope, SymbolType.BYTE, new ConstantInteger(memberByteOffset & 0xff, SymbolType.BYTE)); + programScope.add(memberOffsetConstant); + } + return memberOffsetConstant.getRef(); + } + + /** + * Get the name of the constant variable containing the (byte) index of a specific member in a struct + * + * @param structDefinition The struct + * @param memberName The name of the struct member + * @return The name of the constant + */ + private static String getMemberOffsetConstantName(StructDefinition structDefinition, String memberName) { + return "OFFSET_" + structDefinition.getType().getTypeName().toUpperCase().replace(" ", "_") + "_" + memberName.toUpperCase(); + } + + +} diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 564402046..39b72df1c 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -35,6 +35,16 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testStructPtr4() throws IOException, URISyntaxException { + compileAndCompare("struct-ptr-4"); + } + + @Test + public void testStructPtr3() throws IOException, URISyntaxException { + compileAndCompare("struct-ptr-3"); + } + @Test public void testStructPtr2() throws IOException, URISyntaxException { compileAndCompare("struct-ptr-2", log()); diff --git a/src/test/kc/struct-ptr-3.kc b/src/test/kc/struct-ptr-3.kc new file mode 100644 index 000000000..e1f2125a4 --- /dev/null +++ b/src/test/kc/struct-ptr-3.kc @@ -0,0 +1,17 @@ +// Minimal struct - accessing pointer to struct in memory + +struct Point { + byte x; + byte y; +}; + +struct Point* points = 0x1000; + +void main() { + const byte* SCREEN = 0x0400; + SCREEN[0] = (*points).x; + SCREEN[1] = (*points).y; + points++; + SCREEN[2] = (*points).x; + SCREEN[3] = (*points).y; +} \ No newline at end of file diff --git a/src/test/kc/struct-ptr-4.kc b/src/test/kc/struct-ptr-4.kc new file mode 100644 index 000000000..dbb36c1fa --- /dev/null +++ b/src/test/kc/struct-ptr-4.kc @@ -0,0 +1,30 @@ +// Minimal struct - accessing pointer to struct in memory in a loop + +struct Point { + byte x; + byte y; +}; + +struct Point* POINTS = 0x1000; + +void main() { + // Fill points + struct Point* points = POINTS; + for( byte i: 0..3) { + (*points).x = i; + (*points).y = i+5; + points++; + } + + // Print points + const byte* SCREEN = 0x0400; + byte idx = 0; + points = POINTS; + for( byte i: 0..3) { + SCREEN[idx++] = (*points).x; + SCREEN[idx++] = (*points).y; + SCREEN[idx++] = ' '; + points++; + } + +} \ No newline at end of file diff --git a/src/test/ref/struct-ptr-3.asm b/src/test/ref/struct-ptr-3.asm new file mode 100644 index 000000000..08470c4b6 --- /dev/null +++ b/src/test/ref/struct-ptr-3.asm @@ -0,0 +1,18 @@ +// Minimal struct - accessing pointer to struct in memory +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 +main: { + .label SCREEN = $400 + lda $1000 + sta SCREEN + lda $1000+OFFSET_STRUCT_POINT_Y + sta SCREEN+1 + lda $1000+SIZEOF_STRUCT_POINT + sta SCREEN+2 + lda $1000+SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y + sta SCREEN+3 + rts +} diff --git a/src/test/ref/struct-ptr-3.cfg b/src/test/ref/struct-ptr-3.cfg new file mode 100644 index 000000000..aaffdf5ba --- /dev/null +++ b/src/test/ref/struct-ptr-3.cfg @@ -0,0 +1,18 @@ +@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] *((const byte*) main::SCREEN#0) ← *((byte*)(struct Point*) 4096) + [5] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(struct Point*) 4096+(const byte) OFFSET_STRUCT_POINT_Y) + [6] *((const byte*) main::SCREEN#0+(byte) 2) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT) + [7] *((const byte*) main::SCREEN#0+(byte) 3) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT+(const byte) OFFSET_STRUCT_POINT_Y) + to:main::@return +main::@return: scope:[main] from main + [8] return + to:@return diff --git a/src/test/ref/struct-ptr-3.log b/src/test/ref/struct-ptr-3.log new file mode 100644 index 000000000..697022a2d --- /dev/null +++ b/src/test/ref/struct-ptr-3.log @@ -0,0 +1,360 @@ +Fixing pointer increment (struct Point*) points ← ++ (struct Point*) points +Rewriting struct pointer member access *((struct Point*) points).x +Rewriting struct pointer member access *((struct Point*) points).y +Rewriting struct pointer member access *((struct Point*) points).x +Rewriting struct pointer member access *((struct Point*) points).y +Adding pointer type conversion cast (struct Point*) points in (struct Point*) points ← (number) $1000 +Adding pointer type conversion cast (byte*) main::SCREEN in (byte*) main::SCREEN ← (number) $400 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (struct Point*) points#0 ← ((struct Point*)) (number) $1000 + to:@1 +main: scope:[main] from @1 + (struct Point*) points#4 ← phi( @1/(struct Point*) points#7 ) + (byte*) main::SCREEN#0 ← ((byte*)) (number) $400 + (byte*) main::$0 ← (byte*)(struct Point*) points#4 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::SCREEN#0 + (number) 0) ← *((byte*) main::$0) + (byte*) main::$1 ← (byte*)(struct Point*) points#4 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::SCREEN#0 + (number) 1) ← *((byte*) main::$1) + (struct Point*) points#1 ← (struct Point*) points#4 + (const byte) SIZEOF_STRUCT_POINT + (byte*) main::$2 ← (byte*)(struct Point*) points#1 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::SCREEN#0 + (number) 2) ← *((byte*) main::$2) + (byte*) main::$3 ← (byte*)(struct Point*) points#1 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::SCREEN#0 + (number) 3) ← *((byte*) main::$3) + to:main::@return +main::@return: scope:[main] from main + (struct Point*) points#5 ← phi( main/(struct Point*) points#1 ) + (struct Point*) points#2 ← (struct Point*) points#5 + return + to:@return +@1: scope:[] from @begin + (struct Point*) points#7 ← phi( @begin/(struct Point*) points#0 ) + call main + to:@2 +@2: scope:[] from @1 + (struct Point*) points#6 ← phi( @1/(struct Point*) points#2 ) + (struct Point*) points#3 ← (struct Point*) points#6 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_X = (byte) 0 +(const byte) OFFSET_STRUCT_POINT_Y = (byte) 1 +(byte) Point::x +(byte) Point::y +(const byte) SIZEOF_STRUCT_POINT = (byte) 2 +(void()) main() +(byte*) main::$0 +(byte*) main::$1 +(byte*) main::$2 +(byte*) main::$3 +(label) main::@return +(byte*) main::SCREEN +(byte*) main::SCREEN#0 +(struct Point*) points +(struct Point*) points#0 +(struct Point*) points#1 +(struct Point*) points#2 +(struct Point*) points#3 +(struct Point*) points#4 +(struct Point*) points#5 +(struct Point*) points#6 +(struct Point*) points#7 + +Adding number conversion cast (unumber) 0 in *((byte*) main::SCREEN#0 + (number) 0) ← *((byte*) main::$0) +Adding number conversion cast (unumber) 1 in *((byte*) main::SCREEN#0 + (number) 1) ← *((byte*) main::$1) +Adding number conversion cast (unumber) 2 in *((byte*) main::SCREEN#0 + (number) 2) ← *((byte*) main::$2) +Adding number conversion cast (unumber) 3 in *((byte*) main::SCREEN#0 + (number) 3) ← *((byte*) main::$3) +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (struct Point*) points#0 ← (struct Point*)(number) $1000 +Inlining cast (byte*) main::SCREEN#0 ← (byte*)(number) $400 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (struct Point*) 4096 +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 1 +Simplifying constant integer cast 2 +Simplifying constant integer cast 3 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 2 +Finalized unsigned number type (byte) 3 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (struct Point*) points#1 = (struct Point*) points#5 (struct Point*) points#2 +Alias (struct Point*) points#0 = (struct Point*) points#7 +Alias (struct Point*) points#3 = (struct Point*) points#6 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (struct Point*) points#4 (struct Point*) points#0 +Identical Phi Values (struct Point*) points#3 (struct Point*) points#1 +Successful SSA optimization Pass2IdenticalPhiElimination +Constant (const struct Point*) points#0 = (struct Point*) 4096 +Constant (const byte*) main::SCREEN#0 = (byte*) 1024 +Successful SSA optimization Pass2ConstantIdentification +Constant value identified (byte*)points#0 in [3] (byte*) main::$0 ← (byte*)(const struct Point*) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)points#0 in [5] (byte*) main::$1 ← (byte*)(const struct Point*) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantValues +Converting *(pointer+n) to pointer[n] [4] *((const byte*) main::SCREEN#0 + (byte) 0) ← *((byte*) main::$0) -- *((byte*)points#0 + OFFSET_STRUCT_POINT_X) +Converting *(pointer+n) to pointer[n] [6] *((const byte*) main::SCREEN#0 + (byte) 1) ← *((byte*) main::$1) -- *((byte*)points#0 + OFFSET_STRUCT_POINT_Y) +Converting *(pointer+n) to pointer[n] [9] *((const byte*) main::SCREEN#0 + (byte) 2) ← *((byte*) main::$2) -- *((byte*)points#1 + OFFSET_STRUCT_POINT_X) +Converting *(pointer+n) to pointer[n] [11] *((const byte*) main::SCREEN#0 + (byte) 3) ← *((byte*) main::$3) -- *((byte*)points#1 + OFFSET_STRUCT_POINT_Y) +Successful SSA optimization Pass2InlineDerefIdx +Simplifying expression containing zero (byte*)points#0 in [3] (byte*) main::$0 ← (byte*)(const struct Point*) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)points#0 in [4] *((const byte*) main::SCREEN#0 + (byte) 0) ← *((byte*)(const struct Point*) points#0 + (const byte) OFFSET_STRUCT_POINT_X) +Simplifying expression containing zero main::SCREEN#0 in [4] *((const byte*) main::SCREEN#0 + (byte) 0) ← *((byte*)(const struct Point*) points#0) +Simplifying expression containing zero (byte*)points#1 in [8] (byte*) main::$2 ← (byte*)(struct Point*) points#1 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)points#1 in [9] *((const byte*) main::SCREEN#0 + (byte) 2) ← *((byte*)(struct Point*) points#1 + (const byte) OFFSET_STRUCT_POINT_X) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable (byte*) main::$0 and assignment [0] (byte*) main::$0 ← (byte*)(const struct Point*) points#0 +Eliminating unused variable (byte*) main::$1 and assignment [2] (byte*) main::$1 ← (byte*)(const struct Point*) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Eliminating unused variable (byte*) main::$2 and assignment [5] (byte*) main::$2 ← (byte*)(struct Point*) points#1 +Eliminating unused variable (byte*) main::$3 and assignment [7] (byte*) main::$3 ← (byte*)(struct Point*) points#1 + (const byte) OFFSET_STRUCT_POINT_Y +Eliminating unused constant (const byte) OFFSET_STRUCT_POINT_X +Successful SSA optimization PassNEliminateUnusedVars +Constant right-side identified [2] (struct Point*) points#1 ← (const struct Point*) points#0 + (const byte) SIZEOF_STRUCT_POINT +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const struct Point*) points#1 = points#0+SIZEOF_STRUCT_POINT +Successful SSA optimization Pass2ConstantIdentification +Constant value identified (byte*)points#1 in [3] *((const byte*) main::SCREEN#0 + (byte) 2) ← *((byte*)(const struct Point*) points#1) +Constant value identified (byte*)points#1 in [4] *((const byte*) main::SCREEN#0 + (byte) 3) ← *((byte*)(const struct Point*) points#1 + (const byte) OFFSET_STRUCT_POINT_Y) +Successful SSA optimization Pass2ConstantValues +Inlining constant with different constant siblings (const struct Point*) points#0 +Inlining constant with different constant siblings (const struct Point*) points#1 +Constant inlined points#0 = (struct Point*) 4096 +Constant inlined points#1 = (struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT +Successful SSA optimization Pass2ConstantInlining +Consolidated array index constant in *((byte*)(struct Point*) 4096+OFFSET_STRUCT_POINT_Y) +Consolidated array index constant in *(main::SCREEN#0+1) +Consolidated array index constant in *(main::SCREEN#0+2) +Consolidated array index constant in *((byte*)(struct Point*) 4096+SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y) +Consolidated array index constant in *(main::SCREEN#0+3) +Successful SSA optimization Pass2ConstantAdditionElimination +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:2 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Culled Empty Block (label) @2 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] *((const byte*) main::SCREEN#0) ← *((byte*)(struct Point*) 4096) + [5] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(struct Point*) 4096+(const byte) OFFSET_STRUCT_POINT_Y) + [6] *((const byte*) main::SCREEN#0+(byte) 2) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT) + [7] *((const byte*) main::SCREEN#0+(byte) 3) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT+(const byte) OFFSET_STRUCT_POINT_Y) + to:main::@return +main::@return: scope:[main] from main + [8] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte) Point::x +(byte) Point::y +(void()) main() +(byte*) main::SCREEN +(struct Point*) points + +Initial phi equivalence classes +Complete equivalence classes + +INITIAL ASM +//SEG0 File Comments +// Minimal struct - accessing pointer to struct in memory +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .label SCREEN = $400 + //SEG10 [4] *((const byte*) main::SCREEN#0) ← *((byte*)(struct Point*) 4096) -- _deref_pbuc1=_deref_pbuc2 + lda $1000 + sta SCREEN + //SEG11 [5] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(struct Point*) 4096+(const byte) OFFSET_STRUCT_POINT_Y) -- _deref_pbuc1=_deref_pbuc2 + lda $1000+OFFSET_STRUCT_POINT_Y + sta SCREEN+1 + //SEG12 [6] *((const byte*) main::SCREEN#0+(byte) 2) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT) -- _deref_pbuc1=_deref_pbuc2 + lda $1000+SIZEOF_STRUCT_POINT + sta SCREEN+2 + //SEG13 [7] *((const byte*) main::SCREEN#0+(byte) 3) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT+(const byte) OFFSET_STRUCT_POINT_Y) -- _deref_pbuc1=_deref_pbuc2 + lda $1000+SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y + sta SCREEN+3 + jmp breturn + //SEG14 main::@return + breturn: + //SEG15 [8] return + rts +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((const byte*) main::SCREEN#0) ← *((byte*)(struct Point*) 4096) [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [5] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(struct Point*) 4096+(const byte) OFFSET_STRUCT_POINT_Y) [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [6] *((const byte*) main::SCREEN#0+(byte) 2) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT) [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [7] *((const byte*) main::SCREEN#0+(byte) 3) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT+(const byte) OFFSET_STRUCT_POINT_Y) [ ] ( main:2 [ ] ) always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [Point] +Uplift Scope [main] +Uplift Scope [] + +Uplifting [Point] best 53 combination +Uplifting [main] best 53 combination +Uplifting [] best 53 combination + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Minimal struct - accessing pointer to struct in memory +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .label SCREEN = $400 + //SEG10 [4] *((const byte*) main::SCREEN#0) ← *((byte*)(struct Point*) 4096) -- _deref_pbuc1=_deref_pbuc2 + lda $1000 + sta SCREEN + //SEG11 [5] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(struct Point*) 4096+(const byte) OFFSET_STRUCT_POINT_Y) -- _deref_pbuc1=_deref_pbuc2 + lda $1000+OFFSET_STRUCT_POINT_Y + sta SCREEN+1 + //SEG12 [6] *((const byte*) main::SCREEN#0+(byte) 2) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT) -- _deref_pbuc1=_deref_pbuc2 + lda $1000+SIZEOF_STRUCT_POINT + sta SCREEN+2 + //SEG13 [7] *((const byte*) main::SCREEN#0+(byte) 3) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT+(const byte) OFFSET_STRUCT_POINT_Y) -- _deref_pbuc1=_deref_pbuc2 + lda $1000+SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y + sta SCREEN+3 + jmp breturn + //SEG14 main::@return + breturn: + //SEG15 [8] return + rts +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction bend_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction breturn: +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 +(const byte) OFFSET_STRUCT_POINT_Y OFFSET_STRUCT_POINT_Y = (byte) 1 +(byte) Point::x +(byte) Point::y +(const byte) SIZEOF_STRUCT_POINT SIZEOF_STRUCT_POINT = (byte) 2 +(void()) main() +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = (byte*) 1024 +(struct Point*) points + + + +FINAL ASSEMBLER +Score: 38 + +//SEG0 File Comments +// Minimal struct - accessing pointer to struct in memory +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 +//SEG3 @begin +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +//SEG5 @1 +//SEG6 [2] call main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +//SEG8 @end +//SEG9 main +main: { + .label SCREEN = $400 + //SEG10 [4] *((const byte*) main::SCREEN#0) ← *((byte*)(struct Point*) 4096) -- _deref_pbuc1=_deref_pbuc2 + lda $1000 + sta SCREEN + //SEG11 [5] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(struct Point*) 4096+(const byte) OFFSET_STRUCT_POINT_Y) -- _deref_pbuc1=_deref_pbuc2 + lda $1000+OFFSET_STRUCT_POINT_Y + sta SCREEN+1 + //SEG12 [6] *((const byte*) main::SCREEN#0+(byte) 2) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT) -- _deref_pbuc1=_deref_pbuc2 + lda $1000+SIZEOF_STRUCT_POINT + sta SCREEN+2 + //SEG13 [7] *((const byte*) main::SCREEN#0+(byte) 3) ← *((byte*)(struct Point*) 4096+(const byte) SIZEOF_STRUCT_POINT+(const byte) OFFSET_STRUCT_POINT_Y) -- _deref_pbuc1=_deref_pbuc2 + lda $1000+SIZEOF_STRUCT_POINT+OFFSET_STRUCT_POINT_Y + sta SCREEN+3 + //SEG14 main::@return + //SEG15 [8] return + rts +} + diff --git a/src/test/ref/struct-ptr-3.sym b/src/test/ref/struct-ptr-3.sym new file mode 100644 index 000000000..1daabbadc --- /dev/null +++ b/src/test/ref/struct-ptr-3.sym @@ -0,0 +1 @@ +program \ No newline at end of file diff --git a/src/test/ref/struct-ptr-4.asm b/src/test/ref/struct-ptr-4.asm new file mode 100644 index 000000000..cb341bca5 --- /dev/null +++ b/src/test/ref/struct-ptr-4.asm @@ -0,0 +1,70 @@ +// Minimal struct - accessing pointer to struct in memory in a loop +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label POINTS = $1000 +main: { + // Print points + .label SCREEN = $400 + .label points = 2 + .label points_3 = 4 + .label i1 = 6 + .label points_5 = 4 + ldx #0 + lda #POINTS + sta points+1 + b1: + txa + ldy #0 + sta (points),y + txa + clc + adc #5 + ldy #OFFSET_STRUCT_POINT_Y + sta (points),y + lda #SIZEOF_STRUCT_POINT + clc + adc points + sta points + bcc !+ + inc points+1 + !: + inx + cpx #4 + bne b1 + lda #0 + sta i1 + tax + lda #POINTS + sta points_5+1 + b2: + ldy #0 + lda (points_5),y + sta SCREEN,x + inx + ldy #OFFSET_STRUCT_POINT_Y + lda (points_5),y + sta SCREEN,x + inx + lda #' ' + sta SCREEN,x + inx + lda #SIZEOF_STRUCT_POINT + clc + adc points_3 + sta points_3 + bcc !+ + inc points_3+1 + !: + inc i1 + lda #4 + cmp i1 + bne b2 + rts +} diff --git a/src/test/ref/struct-ptr-4.cfg b/src/test/ref/struct-ptr-4.cfg new file mode 100644 index 000000000..eefba79a3 --- /dev/null +++ b/src/test/ref/struct-ptr-4.cfg @@ -0,0 +1,39 @@ +@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() + to:main::@1 +main::@1: scope:[main] from main main::@1 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@1/(byte) main::i#1 ) + [5] (struct Point*) main::points#4 ← phi( main/(const struct Point*) POINTS#0 main::@1/(struct Point*) main::points#1 ) + [6] *((byte*)(struct Point*) main::points#4) ← (byte) main::i#2 + [7] (byte~) main::$0 ← (byte) main::i#2 + (byte) 5 + [8] *((byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_Y) ← (byte~) main::$0 + [9] (struct Point*) main::points#1 ← (struct Point*) main::points#4 + (const byte) SIZEOF_STRUCT_POINT + [10] (byte) main::i#1 ← ++ (byte) main::i#2 + [11] if((byte) main::i#1!=(byte) 4) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 main::@2 + [12] (byte) main::i1#2 ← phi( main::@1/(byte) 0 main::@2/(byte) main::i1#1 ) + [12] (byte) main::idx#4 ← phi( main::@1/(byte) 0 main::@2/(byte) main::idx#3 ) + [12] (struct Point*) main::points#5 ← phi( main::@1/(const struct Point*) POINTS#0 main::@2/(struct Point*) main::points#3 ) + [13] *((const byte*) main::SCREEN#0 + (byte) main::idx#4) ← *((byte*)(struct Point*) main::points#5) + [14] (byte) main::idx#1 ← ++ (byte) main::idx#4 + [15] *((const byte*) main::SCREEN#0 + (byte) main::idx#1) ← *((byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_Y) + [16] (byte) main::idx#2 ← ++ (byte) main::idx#1 + [17] *((const byte*) main::SCREEN#0 + (byte) main::idx#2) ← (byte) ' ' + [18] (byte) main::idx#3 ← ++ (byte) main::idx#2 + [19] (struct Point*) main::points#3 ← (struct Point*) main::points#5 + (const byte) SIZEOF_STRUCT_POINT + [20] (byte) main::i1#1 ← ++ (byte) main::i1#2 + [21] if((byte) main::i1#1!=(byte) 4) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@2 + [22] return + to:@return diff --git a/src/test/ref/struct-ptr-4.log b/src/test/ref/struct-ptr-4.log new file mode 100644 index 000000000..94fad62e3 --- /dev/null +++ b/src/test/ref/struct-ptr-4.log @@ -0,0 +1,835 @@ +Fixing pointer increment (struct Point*) main::points ← ++ (struct Point*) main::points +Fixing pointer increment (struct Point*) main::points ← ++ (struct Point*) main::points +Rewriting struct pointer member access *((struct Point*) main::points).x +Rewriting struct pointer member access *((struct Point*) main::points).y +Rewriting struct pointer member access *((struct Point*) main::points).x +Rewriting struct pointer member access *((struct Point*) main::points).y +Adding pointer type conversion cast (struct Point*) POINTS in (struct Point*) POINTS ← (number) $1000 +Adding pointer type conversion cast (byte*) main::SCREEN in (byte*) main::SCREEN ← (number) $400 +Identified constant variable (struct Point*) POINTS +Culled Empty Block (label) main::@4 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (struct Point*) POINTS#0 ← ((struct Point*)) (number) $1000 + to:@1 +main: scope:[main] from @1 + (struct Point*) main::points#0 ← (struct Point*) POINTS#0 + (byte) main::i#0 ← (byte) 0 + to:main::@1 +main::@1: scope:[main] from main main::@1 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@1/(byte) main::i#1 ) + (struct Point*) main::points#4 ← phi( main/(struct Point*) main::points#0 main::@1/(struct Point*) main::points#1 ) + (byte*) main::$3 ← (byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::$3) ← (byte) main::i#2 + (number~) main::$0 ← (byte) main::i#2 + (number) 5 + (byte*) main::$4 ← (byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::$4) ← (number~) main::$0 + (struct Point*) main::points#1 ← (struct Point*) main::points#4 + (const byte) SIZEOF_STRUCT_POINT + (byte) main::i#1 ← (byte) main::i#2 + rangenext(0,3) + (bool~) main::$1 ← (byte) main::i#1 != rangelast(0,3) + if((bool~) main::$1) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 + (byte*) main::SCREEN#0 ← ((byte*)) (number) $400 + (byte) main::idx#0 ← (number) 0 + (struct Point*) main::points#2 ← (struct Point*) POINTS#0 + (byte) main::i1#0 ← (byte) 0 + to:main::@3 +main::@3: scope:[main] from main::@2 main::@3 + (byte) main::i1#2 ← phi( main::@2/(byte) main::i1#0 main::@3/(byte) main::i1#1 ) + (byte) main::idx#4 ← phi( main::@2/(byte) main::idx#0 main::@3/(byte) main::idx#3 ) + (struct Point*) main::points#5 ← phi( main::@2/(struct Point*) main::points#2 main::@3/(struct Point*) main::points#3 ) + (byte*) main::$5 ← (byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::SCREEN#0 + (byte) main::idx#4) ← *((byte*) main::$5) + (byte) main::idx#1 ← ++ (byte) main::idx#4 + (byte*) main::$6 ← (byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::SCREEN#0 + (byte) main::idx#1) ← *((byte*) main::$6) + (byte) main::idx#2 ← ++ (byte) main::idx#1 + *((byte*) main::SCREEN#0 + (byte) main::idx#2) ← (byte) ' ' + (byte) main::idx#3 ← ++ (byte) main::idx#2 + (struct Point*) main::points#3 ← (struct Point*) main::points#5 + (const byte) SIZEOF_STRUCT_POINT + (byte) main::i1#1 ← (byte) main::i1#2 + rangenext(0,3) + (bool~) main::$2 ← (byte) main::i1#1 != rangelast(0,3) + if((bool~) main::$2) goto main::@3 + to:main::@return +main::@return: scope:[main] from main::@3 + return + to:@return +@1: scope:[] from @begin + call main + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_X = (byte) 0 +(const byte) OFFSET_STRUCT_POINT_Y = (byte) 1 +(struct Point*) POINTS +(struct Point*) POINTS#0 +(byte) Point::x +(byte) Point::y +(const byte) SIZEOF_STRUCT_POINT = (byte) 2 +(void()) main() +(number~) main::$0 +(bool~) main::$1 +(bool~) main::$2 +(byte*) main::$3 +(byte*) main::$4 +(byte*) main::$5 +(byte*) main::$6 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte*) main::SCREEN +(byte*) main::SCREEN#0 +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i1 +(byte) main::i1#0 +(byte) main::i1#1 +(byte) main::i1#2 +(byte) main::idx +(byte) main::idx#0 +(byte) main::idx#1 +(byte) main::idx#2 +(byte) main::idx#3 +(byte) main::idx#4 +(struct Point*) main::points +(struct Point*) main::points#0 +(struct Point*) main::points#1 +(struct Point*) main::points#2 +(struct Point*) main::points#3 +(struct Point*) main::points#4 +(struct Point*) main::points#5 + +Adding number conversion cast (unumber) 5 in (number~) main::$0 ← (byte) main::i#2 + (number) 5 +Adding number conversion cast (unumber) main::$0 in (number~) main::$0 ← (byte) main::i#2 + (unumber)(number) 5 +Adding number conversion cast (unumber) 0 in (byte) main::idx#0 ← (number) 0 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (struct Point*) POINTS#0 ← (struct Point*)(number) $1000 +Inlining cast (byte*) main::SCREEN#0 ← (byte*)(number) $400 +Inlining cast (byte) main::idx#0 ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (struct Point*) 4096 +Simplifying constant integer cast 5 +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 5 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inferred type updated to byte in (unumber~) main::$0 ← (byte) main::i#2 + (byte) 5 +Simple Condition (bool~) main::$1 [12] if((byte) main::i#1!=rangelast(0,3)) goto main::@1 +Simple Condition (bool~) main::$2 [29] if((byte) main::i1#1!=rangelast(0,3)) goto main::@3 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const struct Point*) POINTS#0 = (struct Point*) 4096 +Constant (const byte) main::i#0 = 0 +Constant (const byte*) main::SCREEN#0 = (byte*) 1024 +Constant (const byte) main::idx#0 = 0 +Constant (const byte) main::i1#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Constant (const struct Point*) main::points#0 = POINTS#0 +Constant (const struct Point*) main::points#2 = POINTS#0 +Successful SSA optimization Pass2ConstantIdentification +Resolved ranged next value [10] main::i#1 ← ++ main::i#2 to ++ +Resolved ranged comparison value [12] if(main::i#1!=rangelast(0,3)) goto main::@1 to (number) 4 +Resolved ranged next value [27] main::i1#1 ← ++ main::i1#2 to ++ +Resolved ranged comparison value [29] if(main::i1#1!=rangelast(0,3)) goto main::@3 to (number) 4 +Converting *(pointer+n) to pointer[n] [5] *((byte*) main::$3) ← (byte) main::i#2 -- *((byte*)main::points#4 + OFFSET_STRUCT_POINT_X) +Converting *(pointer+n) to pointer[n] [8] *((byte*) main::$4) ← (byte~) main::$0 -- *((byte*)main::points#4 + OFFSET_STRUCT_POINT_Y) +Converting *(pointer+n) to pointer[n] [19] *((const byte*) main::SCREEN#0 + (byte) main::idx#4) ← *((byte*) main::$5) -- *((byte*)main::points#5 + OFFSET_STRUCT_POINT_X) +Converting *(pointer+n) to pointer[n] [22] *((const byte*) main::SCREEN#0 + (byte) main::idx#1) ← *((byte*) main::$6) -- *((byte*)main::points#5 + OFFSET_STRUCT_POINT_Y) +Successful SSA optimization Pass2InlineDerefIdx +Simplifying expression containing zero (byte*)main::points#4 in [4] (byte*) main::$3 ← (byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)main::points#4 in [5] *((byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_X) ← (byte) main::i#2 +Simplifying expression containing zero (byte*)main::points#5 in [18] (byte*) main::$5 ← (byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)main::points#5 in [19] *((const byte*) main::SCREEN#0 + (byte) main::idx#4) ← *((byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_X) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable (byte*) main::$3 and assignment [1] (byte*) main::$3 ← (byte*)(struct Point*) main::points#4 +Eliminating unused variable (byte*) main::$4 and assignment [4] (byte*) main::$4 ← (byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_Y +Eliminating unused variable (byte*) main::$5 and assignment [10] (byte*) main::$5 ← (byte*)(struct Point*) main::points#5 +Eliminating unused variable (byte*) main::$6 and assignment [13] (byte*) main::$6 ← (byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_Y +Eliminating unused constant (const byte) OFFSET_STRUCT_POINT_X +Successful SSA optimization PassNEliminateUnusedVars +Adding number conversion cast (unumber) 4 in if((byte) main::i#1!=(number) 4) goto main::@1 +Adding number conversion cast (unumber) 4 in if((byte) main::i1#1!=(number) 4) goto main::@3 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 4 +Simplifying constant integer cast 4 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 4 +Finalized unsigned number type (byte) 4 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inlining constant with var siblings (const byte) main::i#0 +Inlining constant with var siblings (const byte) main::idx#0 +Inlining constant with var siblings (const byte) main::i1#0 +Inlining constant with var siblings (const struct Point*) main::points#0 +Inlining constant with var siblings (const struct Point*) main::points#2 +Constant inlined main::points#2 = (const struct Point*) POINTS#0 +Constant inlined main::i#0 = (byte) 0 +Constant inlined main::i1#0 = (byte) 0 +Constant inlined main::idx#0 = (byte) 0 +Constant inlined main::points#0 = (const struct Point*) POINTS#0 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@5(between main::@1 and main::@1) +Added new block during phi lifting main::@6(between main::@3 and main::@3) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@2 +CALL GRAPH +Calls in [] to main:2 + +Created 5 initial phi equivalence classes +Coalesced [25] main::points#7 ← main::points#3 +Coalesced [26] main::idx#5 ← main::idx#3 +Coalesced [27] main::i1#3 ← main::i1#1 +Coalesced [28] main::points#6 ← main::points#1 +Coalesced [29] main::i#3 ← main::i#1 +Coalesced down to 5 phi equivalence classes +Culled Empty Block (label) @2 +Culled Empty Block (label) main::@2 +Culled Empty Block (label) main::@6 +Culled Empty Block (label) main::@5 +Renumbering block main::@3 to main::@2 +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 + +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() + to:main::@1 +main::@1: scope:[main] from main main::@1 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@1/(byte) main::i#1 ) + [5] (struct Point*) main::points#4 ← phi( main/(const struct Point*) POINTS#0 main::@1/(struct Point*) main::points#1 ) + [6] *((byte*)(struct Point*) main::points#4) ← (byte) main::i#2 + [7] (byte~) main::$0 ← (byte) main::i#2 + (byte) 5 + [8] *((byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_Y) ← (byte~) main::$0 + [9] (struct Point*) main::points#1 ← (struct Point*) main::points#4 + (const byte) SIZEOF_STRUCT_POINT + [10] (byte) main::i#1 ← ++ (byte) main::i#2 + [11] if((byte) main::i#1!=(byte) 4) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 main::@2 + [12] (byte) main::i1#2 ← phi( main::@1/(byte) 0 main::@2/(byte) main::i1#1 ) + [12] (byte) main::idx#4 ← phi( main::@1/(byte) 0 main::@2/(byte) main::idx#3 ) + [12] (struct Point*) main::points#5 ← phi( main::@1/(const struct Point*) POINTS#0 main::@2/(struct Point*) main::points#3 ) + [13] *((const byte*) main::SCREEN#0 + (byte) main::idx#4) ← *((byte*)(struct Point*) main::points#5) + [14] (byte) main::idx#1 ← ++ (byte) main::idx#4 + [15] *((const byte*) main::SCREEN#0 + (byte) main::idx#1) ← *((byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_Y) + [16] (byte) main::idx#2 ← ++ (byte) main::idx#1 + [17] *((const byte*) main::SCREEN#0 + (byte) main::idx#2) ← (byte) ' ' + [18] (byte) main::idx#3 ← ++ (byte) main::idx#2 + [19] (struct Point*) main::points#3 ← (struct Point*) main::points#5 + (const byte) SIZEOF_STRUCT_POINT + [20] (byte) main::i1#1 ← ++ (byte) main::i1#2 + [21] if((byte) main::i1#1!=(byte) 4) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@2 + [22] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(struct Point*) POINTS +(byte) Point::x +(byte) Point::y +(void()) main() +(byte~) main::$0 22.0 +(byte*) main::SCREEN +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#2 8.8 +(byte) main::i1 +(byte) main::i1#1 16.5 +(byte) main::i1#2 2.75 +(byte) main::idx +(byte) main::idx#1 16.5 +(byte) main::idx#2 16.5 +(byte) main::idx#3 5.5 +(byte) main::idx#4 16.5 +(struct Point*) main::points +(struct Point*) main::points#1 7.333333333333333 +(struct Point*) main::points#3 7.333333333333333 +(struct Point*) main::points#4 5.5 +(struct Point*) main::points#5 3.142857142857143 + +Initial phi equivalence classes +[ main::points#4 main::points#1 ] +[ main::i#2 main::i#1 ] +[ main::points#5 main::points#3 ] +[ main::idx#4 main::idx#3 ] +[ main::i1#2 main::i1#1 ] +Added variable main::$0 to zero page equivalence class [ main::$0 ] +Added variable main::idx#1 to zero page equivalence class [ main::idx#1 ] +Added variable main::idx#2 to zero page equivalence class [ main::idx#2 ] +Complete equivalence classes +[ main::points#4 main::points#1 ] +[ main::i#2 main::i#1 ] +[ main::points#5 main::points#3 ] +[ main::idx#4 main::idx#3 ] +[ main::i1#2 main::i1#1 ] +[ main::$0 ] +[ main::idx#1 ] +[ main::idx#2 ] +Allocated zp ZP_WORD:2 [ main::points#4 main::points#1 ] +Allocated zp ZP_BYTE:4 [ main::i#2 main::i#1 ] +Allocated zp ZP_WORD:5 [ main::points#5 main::points#3 ] +Allocated zp ZP_BYTE:7 [ main::idx#4 main::idx#3 ] +Allocated zp ZP_BYTE:8 [ main::i1#2 main::i1#1 ] +Allocated zp ZP_BYTE:9 [ main::$0 ] +Allocated zp ZP_BYTE:10 [ main::idx#1 ] +Allocated zp ZP_BYTE:11 [ main::idx#2 ] + +INITIAL ASM +//SEG0 File Comments +// Minimal struct - accessing pointer to struct in memory in a loop +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label POINTS = $1000 +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG9 @end +bend: +//SEG10 main +main: { + // Print points + .label SCREEN = $400 + .label _0 = 9 + .label points = 2 + .label i = 4 + .label idx = $a + .label idx_2 = $b + .label idx_3 = 7 + .label points_3 = 5 + .label i1 = 8 + .label points_5 = 5 + .label idx_4 = 7 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta i + //SEG13 [5] phi (struct Point*) main::points#4 = (const struct Point*) POINTS#0 [phi:main->main::@1#1] -- pssz1=pssc1 + lda #POINTS + sta points+1 + jmp b1 + //SEG14 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + //SEG15 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + //SEG16 [5] phi (struct Point*) main::points#4 = (struct Point*) main::points#1 [phi:main::@1->main::@1#1] -- register_copy + jmp b1 + //SEG17 main::@1 + b1: + //SEG18 [6] *((byte*)(struct Point*) main::points#4) ← (byte) main::i#2 -- _deref_pbuz1=vbuz2 + lda i + ldy #0 + sta (points),y + //SEG19 [7] (byte~) main::$0 ← (byte) main::i#2 + (byte) 5 -- vbuz1=vbuz2_plus_vbuc1 + lax i + axs #-[5] + stx _0 + //SEG20 [8] *((byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_Y) ← (byte~) main::$0 -- pbuz1_derefidx_vbuc1=vbuz2 + lda _0 + ldy #OFFSET_STRUCT_POINT_Y + sta (points),y + //SEG21 [9] (struct Point*) main::points#1 ← (struct Point*) main::points#4 + (const byte) SIZEOF_STRUCT_POINT -- pssz1=pssz1_plus_vbuc1 + lda #SIZEOF_STRUCT_POINT + clc + adc points + sta points + bcc !+ + inc points+1 + !: + //SEG22 [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + //SEG23 [11] if((byte) main::i#1!=(byte) 4) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #4 + cmp i + bne b1_from_b1 + //SEG24 [12] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + //SEG25 [12] phi (byte) main::i1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuz1=vbuc1 + lda #0 + sta i1 + //SEG26 [12] phi (byte) main::idx#4 = (byte) 0 [phi:main::@1->main::@2#1] -- vbuz1=vbuc1 + lda #0 + sta idx_4 + //SEG27 [12] phi (struct Point*) main::points#5 = (const struct Point*) POINTS#0 [phi:main::@1->main::@2#2] -- pssz1=pssc1 + lda #POINTS + sta points_5+1 + jmp b2 + //SEG28 [12] phi from main::@2 to main::@2 [phi:main::@2->main::@2] + b2_from_b2: + //SEG29 [12] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@2->main::@2#0] -- register_copy + //SEG30 [12] phi (byte) main::idx#4 = (byte) main::idx#3 [phi:main::@2->main::@2#1] -- register_copy + //SEG31 [12] phi (struct Point*) main::points#5 = (struct Point*) main::points#3 [phi:main::@2->main::@2#2] -- register_copy + jmp b2 + //SEG32 main::@2 + b2: + //SEG33 [13] *((const byte*) main::SCREEN#0 + (byte) main::idx#4) ← *((byte*)(struct Point*) main::points#5) -- pbuc1_derefidx_vbuz1=_deref_pbuz2 + ldx idx_4 + ldy #0 + lda (points_5),y + sta SCREEN,x + //SEG34 [14] (byte) main::idx#1 ← ++ (byte) main::idx#4 -- vbuz1=_inc_vbuz2 + ldy idx_4 + iny + sty idx + //SEG35 [15] *((const byte*) main::SCREEN#0 + (byte) main::idx#1) ← *((byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_Y) -- pbuc1_derefidx_vbuz1=pbuz2_derefidx_vbuc2 + ldx idx + ldy #OFFSET_STRUCT_POINT_Y + lda (points_5),y + sta SCREEN,x + //SEG36 [16] (byte) main::idx#2 ← ++ (byte) main::idx#1 -- vbuz1=_inc_vbuz2 + ldy idx + iny + sty idx_2 + //SEG37 [17] *((const byte*) main::SCREEN#0 + (byte) main::idx#2) ← (byte) ' ' -- pbuc1_derefidx_vbuz1=vbuc2 + lda #' ' + ldy idx_2 + sta SCREEN,y + //SEG38 [18] (byte) main::idx#3 ← ++ (byte) main::idx#2 -- vbuz1=_inc_vbuz2 + ldy idx_2 + iny + sty idx_3 + //SEG39 [19] (struct Point*) main::points#3 ← (struct Point*) main::points#5 + (const byte) SIZEOF_STRUCT_POINT -- pssz1=pssz1_plus_vbuc1 + lda #SIZEOF_STRUCT_POINT + clc + adc points_3 + sta points_3 + bcc !+ + inc points_3+1 + !: + //SEG40 [20] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuz1=_inc_vbuz1 + inc i1 + //SEG41 [21] if((byte) main::i1#1!=(byte) 4) goto main::@2 -- vbuz1_neq_vbuc1_then_la1 + lda #4 + cmp i1 + bne b2_from_b2 + jmp breturn + //SEG42 main::@return + breturn: + //SEG43 [22] return + rts +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] *((byte*)(struct Point*) main::points#4) ← (byte) main::i#2 [ main::points#4 main::i#2 ] ( main:2 [ main::points#4 main::i#2 ] ) always clobbers reg byte y +Removing always clobbered register reg byte y as potential for zp ZP_BYTE:4 [ main::i#2 main::i#1 ] +Statement [8] *((byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_Y) ← (byte~) main::$0 [ main::points#4 main::i#2 ] ( main:2 [ main::points#4 main::i#2 ] ) always clobbers reg byte y +Statement [9] (struct Point*) main::points#1 ← (struct Point*) main::points#4 + (const byte) SIZEOF_STRUCT_POINT [ main::i#2 main::points#1 ] ( main:2 [ main::i#2 main::points#1 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:4 [ main::i#2 main::i#1 ] +Statement [13] *((const byte*) main::SCREEN#0 + (byte) main::idx#4) ← *((byte*)(struct Point*) main::points#5) [ main::points#5 main::idx#4 main::i1#2 ] ( main:2 [ main::points#5 main::idx#4 main::i1#2 ] ) always clobbers reg byte a reg byte y +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:7 [ main::idx#4 main::idx#3 ] +Removing always clobbered register reg byte y as potential for zp ZP_BYTE:7 [ main::idx#4 main::idx#3 ] +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:8 [ main::i1#2 main::i1#1 ] +Removing always clobbered register reg byte y as potential for zp ZP_BYTE:8 [ main::i1#2 main::i1#1 ] +Statement [15] *((const byte*) main::SCREEN#0 + (byte) main::idx#1) ← *((byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_Y) [ main::points#5 main::i1#2 main::idx#1 ] ( main:2 [ main::points#5 main::i1#2 main::idx#1 ] ) always clobbers reg byte a reg byte y +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:10 [ main::idx#1 ] +Removing always clobbered register reg byte y as potential for zp ZP_BYTE:10 [ main::idx#1 ] +Statement [17] *((const byte*) main::SCREEN#0 + (byte) main::idx#2) ← (byte) ' ' [ main::points#5 main::i1#2 main::idx#2 ] ( main:2 [ main::points#5 main::i1#2 main::idx#2 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:11 [ main::idx#2 ] +Statement [19] (struct Point*) main::points#3 ← (struct Point*) main::points#5 + (const byte) SIZEOF_STRUCT_POINT [ main::i1#2 main::points#3 main::idx#3 ] ( main:2 [ main::i1#2 main::points#3 main::idx#3 ] ) always clobbers reg byte a +Statement [6] *((byte*)(struct Point*) main::points#4) ← (byte) main::i#2 [ main::points#4 main::i#2 ] ( main:2 [ main::points#4 main::i#2 ] ) always clobbers reg byte a reg byte y +Statement [7] (byte~) main::$0 ← (byte) main::i#2 + (byte) 5 [ main::points#4 main::i#2 main::$0 ] ( main:2 [ main::points#4 main::i#2 main::$0 ] ) always clobbers reg byte a +Statement [8] *((byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_Y) ← (byte~) main::$0 [ main::points#4 main::i#2 ] ( main:2 [ main::points#4 main::i#2 ] ) always clobbers reg byte y +Statement [9] (struct Point*) main::points#1 ← (struct Point*) main::points#4 + (const byte) SIZEOF_STRUCT_POINT [ main::i#2 main::points#1 ] ( main:2 [ main::i#2 main::points#1 ] ) always clobbers reg byte a +Statement [13] *((const byte*) main::SCREEN#0 + (byte) main::idx#4) ← *((byte*)(struct Point*) main::points#5) [ main::points#5 main::idx#4 main::i1#2 ] ( main:2 [ main::points#5 main::idx#4 main::i1#2 ] ) always clobbers reg byte a reg byte y +Statement [15] *((const byte*) main::SCREEN#0 + (byte) main::idx#1) ← *((byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_Y) [ main::points#5 main::i1#2 main::idx#1 ] ( main:2 [ main::points#5 main::i1#2 main::idx#1 ] ) always clobbers reg byte a reg byte y +Statement [17] *((const byte*) main::SCREEN#0 + (byte) main::idx#2) ← (byte) ' ' [ main::points#5 main::i1#2 main::idx#2 ] ( main:2 [ main::points#5 main::i1#2 main::idx#2 ] ) always clobbers reg byte a +Statement [19] (struct Point*) main::points#3 ← (struct Point*) main::points#5 + (const byte) SIZEOF_STRUCT_POINT [ main::i1#2 main::points#3 main::idx#3 ] ( main:2 [ main::i1#2 main::points#3 main::idx#3 ] ) always clobbers reg byte a +Potential registers zp ZP_WORD:2 [ main::points#4 main::points#1 ] : zp ZP_WORD:2 , +Potential registers zp ZP_BYTE:4 [ main::i#2 main::i#1 ] : zp ZP_BYTE:4 , reg byte x , +Potential registers zp ZP_WORD:5 [ main::points#5 main::points#3 ] : zp ZP_WORD:5 , +Potential registers zp ZP_BYTE:7 [ main::idx#4 main::idx#3 ] : zp ZP_BYTE:7 , reg byte x , +Potential registers zp ZP_BYTE:8 [ main::i1#2 main::i1#1 ] : zp ZP_BYTE:8 , reg byte x , +Potential registers zp ZP_BYTE:9 [ main::$0 ] : zp ZP_BYTE:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:10 [ main::idx#1 ] : zp ZP_BYTE:10 , reg byte x , +Potential registers zp ZP_BYTE:11 [ main::idx#2 ] : zp ZP_BYTE:11 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 25.3: zp ZP_BYTE:4 [ main::i#2 main::i#1 ] 22: zp ZP_BYTE:7 [ main::idx#4 main::idx#3 ] 22: zp ZP_BYTE:9 [ main::$0 ] 19.25: zp ZP_BYTE:8 [ main::i1#2 main::i1#1 ] 16.5: zp ZP_BYTE:10 [ main::idx#1 ] 16.5: zp ZP_BYTE:11 [ main::idx#2 ] 12.83: zp ZP_WORD:2 [ main::points#4 main::points#1 ] 10.48: zp ZP_WORD:5 [ main::points#5 main::points#3 ] +Uplift Scope [Point] +Uplift Scope [] + +Uplifting [main] best 1708 combination reg byte x [ main::i#2 main::i#1 ] reg byte x [ main::idx#4 main::idx#3 ] reg byte a [ main::$0 ] zp ZP_BYTE:8 [ main::i1#2 main::i1#1 ] reg byte x [ main::idx#1 ] zp ZP_BYTE:11 [ main::idx#2 ] zp ZP_WORD:2 [ main::points#4 main::points#1 ] zp ZP_WORD:5 [ main::points#5 main::points#3 ] +Limited combination testing to 100 combinations of 192 possible. +Uplifting [Point] best 1708 combination +Uplifting [] best 1708 combination +Attempting to uplift remaining variables inzp ZP_BYTE:8 [ main::i1#2 main::i1#1 ] +Uplifting [main] best 1708 combination zp ZP_BYTE:8 [ main::i1#2 main::i1#1 ] +Attempting to uplift remaining variables inzp ZP_BYTE:11 [ main::idx#2 ] +Uplifting [main] best 1618 combination reg byte x [ main::idx#2 ] +Allocated (was zp ZP_WORD:5) zp ZP_WORD:4 [ main::points#5 main::points#3 ] +Allocated (was zp ZP_BYTE:8) zp ZP_BYTE:6 [ main::i1#2 main::i1#1 ] + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Minimal struct - accessing pointer to struct in memory in a loop +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label POINTS = $1000 +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG9 @end +bend: +//SEG10 main +main: { + // Print points + .label SCREEN = $400 + .label points = 2 + .label points_3 = 4 + .label i1 = 6 + .label points_5 = 4 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + //SEG13 [5] phi (struct Point*) main::points#4 = (const struct Point*) POINTS#0 [phi:main->main::@1#1] -- pssz1=pssc1 + lda #POINTS + sta points+1 + jmp b1 + //SEG14 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + //SEG15 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + //SEG16 [5] phi (struct Point*) main::points#4 = (struct Point*) main::points#1 [phi:main::@1->main::@1#1] -- register_copy + jmp b1 + //SEG17 main::@1 + b1: + //SEG18 [6] *((byte*)(struct Point*) main::points#4) ← (byte) main::i#2 -- _deref_pbuz1=vbuxx + txa + ldy #0 + sta (points),y + //SEG19 [7] (byte~) main::$0 ← (byte) main::i#2 + (byte) 5 -- vbuaa=vbuxx_plus_vbuc1 + txa + clc + adc #5 + //SEG20 [8] *((byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_Y) ← (byte~) main::$0 -- pbuz1_derefidx_vbuc1=vbuaa + ldy #OFFSET_STRUCT_POINT_Y + sta (points),y + //SEG21 [9] (struct Point*) main::points#1 ← (struct Point*) main::points#4 + (const byte) SIZEOF_STRUCT_POINT -- pssz1=pssz1_plus_vbuc1 + lda #SIZEOF_STRUCT_POINT + clc + adc points + sta points + bcc !+ + inc points+1 + !: + //SEG22 [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG23 [11] if((byte) main::i#1!=(byte) 4) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #4 + bne b1_from_b1 + //SEG24 [12] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + //SEG25 [12] phi (byte) main::i1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuz1=vbuc1 + lda #0 + sta i1 + //SEG26 [12] phi (byte) main::idx#4 = (byte) 0 [phi:main::@1->main::@2#1] -- vbuxx=vbuc1 + ldx #0 + //SEG27 [12] phi (struct Point*) main::points#5 = (const struct Point*) POINTS#0 [phi:main::@1->main::@2#2] -- pssz1=pssc1 + lda #POINTS + sta points_5+1 + jmp b2 + //SEG28 [12] phi from main::@2 to main::@2 [phi:main::@2->main::@2] + b2_from_b2: + //SEG29 [12] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@2->main::@2#0] -- register_copy + //SEG30 [12] phi (byte) main::idx#4 = (byte) main::idx#3 [phi:main::@2->main::@2#1] -- register_copy + //SEG31 [12] phi (struct Point*) main::points#5 = (struct Point*) main::points#3 [phi:main::@2->main::@2#2] -- register_copy + jmp b2 + //SEG32 main::@2 + b2: + //SEG33 [13] *((const byte*) main::SCREEN#0 + (byte) main::idx#4) ← *((byte*)(struct Point*) main::points#5) -- pbuc1_derefidx_vbuxx=_deref_pbuz1 + ldy #0 + lda (points_5),y + sta SCREEN,x + //SEG34 [14] (byte) main::idx#1 ← ++ (byte) main::idx#4 -- vbuxx=_inc_vbuxx + inx + //SEG35 [15] *((const byte*) main::SCREEN#0 + (byte) main::idx#1) ← *((byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_Y) -- pbuc1_derefidx_vbuxx=pbuz1_derefidx_vbuc2 + ldy #OFFSET_STRUCT_POINT_Y + lda (points_5),y + sta SCREEN,x + //SEG36 [16] (byte) main::idx#2 ← ++ (byte) main::idx#1 -- vbuxx=_inc_vbuxx + inx + //SEG37 [17] *((const byte*) main::SCREEN#0 + (byte) main::idx#2) ← (byte) ' ' -- pbuc1_derefidx_vbuxx=vbuc2 + lda #' ' + sta SCREEN,x + //SEG38 [18] (byte) main::idx#3 ← ++ (byte) main::idx#2 -- vbuxx=_inc_vbuxx + inx + //SEG39 [19] (struct Point*) main::points#3 ← (struct Point*) main::points#5 + (const byte) SIZEOF_STRUCT_POINT -- pssz1=pssz1_plus_vbuc1 + lda #SIZEOF_STRUCT_POINT + clc + adc points_3 + sta points_3 + bcc !+ + inc points_3+1 + !: + //SEG40 [20] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuz1=_inc_vbuz1 + inc i1 + //SEG41 [21] if((byte) main::i1#1!=(byte) 4) goto main::@2 -- vbuz1_neq_vbuc1_then_la1 + lda #4 + cmp i1 + bne b2_from_b2 + jmp breturn + //SEG42 main::@return + breturn: + //SEG43 [22] return + rts +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp b2 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing instruction ldx #0 with TAX +Replacing label b1_from_b1 with b1 +Replacing label b2_from_b2 with b2 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b1: +Removing instruction b2_from_b2: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction b2_from_b1: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction jmp b1 +Removing instruction jmp b2 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_Y OFFSET_STRUCT_POINT_Y = (byte) 1 +(struct Point*) POINTS +(const struct Point*) POINTS#0 POINTS = (struct Point*) 4096 +(byte) Point::x +(byte) Point::y +(const byte) SIZEOF_STRUCT_POINT SIZEOF_STRUCT_POINT = (byte) 2 +(void()) main() +(byte~) main::$0 reg byte a 22.0 +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = (byte*) 1024 +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 8.8 +(byte) main::i1 +(byte) main::i1#1 i1 zp ZP_BYTE:6 16.5 +(byte) main::i1#2 i1 zp ZP_BYTE:6 2.75 +(byte) main::idx +(byte) main::idx#1 reg byte x 16.5 +(byte) main::idx#2 reg byte x 16.5 +(byte) main::idx#3 reg byte x 5.5 +(byte) main::idx#4 reg byte x 16.5 +(struct Point*) main::points +(struct Point*) main::points#1 points zp ZP_WORD:2 7.333333333333333 +(struct Point*) main::points#3 points#3 zp ZP_WORD:4 7.333333333333333 +(struct Point*) main::points#4 points zp ZP_WORD:2 5.5 +(struct Point*) main::points#5 points#5 zp ZP_WORD:4 3.142857142857143 + +zp ZP_WORD:2 [ main::points#4 main::points#1 ] +reg byte x [ main::i#2 main::i#1 ] +zp ZP_WORD:4 [ main::points#5 main::points#3 ] +reg byte x [ main::idx#4 main::idx#3 ] +zp ZP_BYTE:6 [ main::i1#2 main::i1#1 ] +reg byte a [ main::$0 ] +reg byte x [ main::idx#1 ] +reg byte x [ main::idx#2 ] + + +FINAL ASSEMBLER +Score: 1456 + +//SEG0 File Comments +// Minimal struct - accessing pointer to struct in memory in a loop +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .const SIZEOF_STRUCT_POINT = 2 + .const OFFSET_STRUCT_POINT_Y = 1 + .label POINTS = $1000 +//SEG3 @begin +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +//SEG5 @1 +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +//SEG9 @end +//SEG10 main +main: { + // Print points + .label SCREEN = $400 + .label points = 2 + .label points_3 = 4 + .label i1 = 6 + .label points_5 = 4 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + //SEG12 [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + //SEG13 [5] phi (struct Point*) main::points#4 = (const struct Point*) POINTS#0 [phi:main->main::@1#1] -- pssz1=pssc1 + lda #POINTS + sta points+1 + //SEG14 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + //SEG15 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + //SEG16 [5] phi (struct Point*) main::points#4 = (struct Point*) main::points#1 [phi:main::@1->main::@1#1] -- register_copy + //SEG17 main::@1 + b1: + //SEG18 [6] *((byte*)(struct Point*) main::points#4) ← (byte) main::i#2 -- _deref_pbuz1=vbuxx + txa + ldy #0 + sta (points),y + //SEG19 [7] (byte~) main::$0 ← (byte) main::i#2 + (byte) 5 -- vbuaa=vbuxx_plus_vbuc1 + txa + clc + adc #5 + //SEG20 [8] *((byte*)(struct Point*) main::points#4 + (const byte) OFFSET_STRUCT_POINT_Y) ← (byte~) main::$0 -- pbuz1_derefidx_vbuc1=vbuaa + ldy #OFFSET_STRUCT_POINT_Y + sta (points),y + //SEG21 [9] (struct Point*) main::points#1 ← (struct Point*) main::points#4 + (const byte) SIZEOF_STRUCT_POINT -- pssz1=pssz1_plus_vbuc1 + lda #SIZEOF_STRUCT_POINT + clc + adc points + sta points + bcc !+ + inc points+1 + !: + //SEG22 [10] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG23 [11] if((byte) main::i#1!=(byte) 4) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #4 + bne b1 + //SEG24 [12] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + //SEG25 [12] phi (byte) main::i1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuz1=vbuc1 + lda #0 + sta i1 + //SEG26 [12] phi (byte) main::idx#4 = (byte) 0 [phi:main::@1->main::@2#1] -- vbuxx=vbuc1 + tax + //SEG27 [12] phi (struct Point*) main::points#5 = (const struct Point*) POINTS#0 [phi:main::@1->main::@2#2] -- pssz1=pssc1 + lda #POINTS + sta points_5+1 + //SEG28 [12] phi from main::@2 to main::@2 [phi:main::@2->main::@2] + //SEG29 [12] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@2->main::@2#0] -- register_copy + //SEG30 [12] phi (byte) main::idx#4 = (byte) main::idx#3 [phi:main::@2->main::@2#1] -- register_copy + //SEG31 [12] phi (struct Point*) main::points#5 = (struct Point*) main::points#3 [phi:main::@2->main::@2#2] -- register_copy + //SEG32 main::@2 + b2: + //SEG33 [13] *((const byte*) main::SCREEN#0 + (byte) main::idx#4) ← *((byte*)(struct Point*) main::points#5) -- pbuc1_derefidx_vbuxx=_deref_pbuz1 + ldy #0 + lda (points_5),y + sta SCREEN,x + //SEG34 [14] (byte) main::idx#1 ← ++ (byte) main::idx#4 -- vbuxx=_inc_vbuxx + inx + //SEG35 [15] *((const byte*) main::SCREEN#0 + (byte) main::idx#1) ← *((byte*)(struct Point*) main::points#5 + (const byte) OFFSET_STRUCT_POINT_Y) -- pbuc1_derefidx_vbuxx=pbuz1_derefidx_vbuc2 + ldy #OFFSET_STRUCT_POINT_Y + lda (points_5),y + sta SCREEN,x + //SEG36 [16] (byte) main::idx#2 ← ++ (byte) main::idx#1 -- vbuxx=_inc_vbuxx + inx + //SEG37 [17] *((const byte*) main::SCREEN#0 + (byte) main::idx#2) ← (byte) ' ' -- pbuc1_derefidx_vbuxx=vbuc2 + lda #' ' + sta SCREEN,x + //SEG38 [18] (byte) main::idx#3 ← ++ (byte) main::idx#2 -- vbuxx=_inc_vbuxx + inx + //SEG39 [19] (struct Point*) main::points#3 ← (struct Point*) main::points#5 + (const byte) SIZEOF_STRUCT_POINT -- pssz1=pssz1_plus_vbuc1 + lda #SIZEOF_STRUCT_POINT + clc + adc points_3 + sta points_3 + bcc !+ + inc points_3+1 + !: + //SEG40 [20] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuz1=_inc_vbuz1 + inc i1 + //SEG41 [21] if((byte) main::i1#1!=(byte) 4) goto main::@2 -- vbuz1_neq_vbuc1_then_la1 + lda #4 + cmp i1 + bne b2 + //SEG42 main::@return + //SEG43 [22] return + rts +} + diff --git a/src/test/ref/struct-ptr-4.sym b/src/test/ref/struct-ptr-4.sym new file mode 100644 index 000000000..1daabbadc --- /dev/null +++ b/src/test/ref/struct-ptr-4.sym @@ -0,0 +1 @@ +program \ No newline at end of file