From 56c33cdac26a76c18d15be8fcbc286ca14c044b6 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Mon, 10 Jun 2019 22:49:21 +0200 Subject: [PATCH] Implemented struct member unwinding when copying structs referenced through pointers/array indexing. --- .../java/dk/camelot64/kickc/Compiler.java | 4 +- .../kickc/passes/Pass1UnwindStructValues.java | 212 ++++-- .../passes/PassNStructPointerRewriting.java | 2 +- .../dk/camelot64/kickc/test/TestPrograms.java | 5 + src/test/kc/struct-ptr-9.kc | 19 + src/test/ref/struct-ptr-9.asm | 34 + src/test/ref/struct-ptr-9.cfg | 31 + src/test/ref/struct-ptr-9.log | 605 ++++++++++++++++++ src/test/ref/struct-ptr-9.sym | 27 + 9 files changed, 888 insertions(+), 51 deletions(-) create mode 100644 src/test/kc/struct-ptr-9.kc create mode 100644 src/test/ref/struct-ptr-9.asm create mode 100644 src/test/ref/struct-ptr-9.cfg create mode 100644 src/test/ref/struct-ptr-9.log create mode 100644 src/test/ref/struct-ptr-9.sym diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 6fd32791a..ad00f831f 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -169,8 +169,6 @@ public class Compiler { new Pass1AssertReturn(program).execute(); new Pass1AssertUsedVars(program).execute(); - new PassNSizeOfSimplification(program).execute(); // Needed to eliminate sizeof() referencing pointer value variables - new Pass1UnwindStructValues(program).execute(); if(getLog().isVerbosePass1CreateSsa()) { getLog().append("SYMBOLS"); @@ -180,6 +178,8 @@ public class Compiler { new Pass1FixLValuesLoHi(program).execute(); new Pass1AssertNoLValueIntermediate(program).execute(); new Pass1PointerSizeofFix(program).execute(); // After this point in the code all pointer math is byte-based + new PassNSizeOfSimplification(program).execute(); // Needed to eliminate sizeof() referencing pointer value variables + new Pass1UnwindStructValues(program).execute(); new PassNStructPointerRewriting(program).execute(); new PassNAddBooleanCasts(program).execute(); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java index a33bf7c4b..9c063e0c6 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java @@ -3,11 +3,15 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.model.*; import dk.camelot64.kickc.model.InternalError; import dk.camelot64.kickc.model.iterator.ProgramValueIterator; +import dk.camelot64.kickc.model.operators.Operators; import dk.camelot64.kickc.model.statements.Statement; import dk.camelot64.kickc.model.statements.StatementAssignment; import dk.camelot64.kickc.model.statements.StatementCall; import dk.camelot64.kickc.model.statements.StatementReturn; 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.*; @@ -54,11 +58,9 @@ public class Pass1UnwindStructValues extends Pass1Base { Statement statement = stmtIt.next(); if(statement instanceof StatementAssignment) { StatementAssignment assignment = (StatementAssignment) statement; - if(assignment.getlValue() instanceof VariableRef) { - Variable assignedVar = getScope().getVariable((VariableRef) assignment.getlValue()); - if(assignedVar.getType() instanceof SymbolTypeStruct) { - modified |= unwindAssignment(assignment, assignedVar, stmtIt, structUnwinding); - } + SymbolType lValueType = SymbolTypeInference.inferType(getScope(), assignment.getlValue()); + if(lValueType instanceof SymbolTypeStruct) { + modified |= unwindAssignment(assignment, (SymbolTypeStruct)lValueType, stmtIt, block, structUnwinding); } } else if(statement instanceof StatementCall) { modified |= unwindCall((StatementCall) statement, structUnwinding); @@ -86,7 +88,7 @@ public class Pass1UnwindStructValues extends Pass1Base { Variable structVariable = getScope().getVariable((VariableRef) structMemberRef.getStruct()); StructUnwinding.VariableUnwinding memberVariables = structUnwinding.getVariableUnwinding(structVariable.getRef()); if(memberVariables != null) { - VariableRef structMemberVariable = memberVariables.getMemberUnwinding(structMemberRef.getMemberName()); + VariableRef structMemberVariable = (VariableRef) memberVariables.getMemberUnwinding(structMemberRef.getMemberName()); getLog().append("Replacing struct member reference " + structMemberRef.toString(getProgram()) + " with member variable reference " + structMemberVariable.toString(getProgram())); programValue.set(structMemberVariable); modified.set(true); @@ -200,7 +202,8 @@ public class Pass1UnwindStructValues extends Pass1Base { if(parameter.getType() instanceof SymbolTypeStruct) { StructUnwinding.VariableUnwinding parameterUnwinding = structUnwinding.getVariableUnwinding(parameter.getRef()); for(String memberName : parameterUnwinding.getMemberNames()) { - unwoundParameterNames.add(parameterUnwinding.getMemberUnwinding(memberName).getLocalName()); + VariableRef memberUnwinding = (VariableRef) parameterUnwinding.getMemberUnwinding(memberName); + unwoundParameterNames.add(memberUnwinding.getLocalName()); procedureUnwound = true; } } else { @@ -258,20 +261,24 @@ public class Pass1UnwindStructValues extends Pass1Base { * Unwind an assignment to a struct value variable into assignment of each member * * @param assignment The assignment statement - * @param assignedVar The struct value variable being assigned to (the LValue) + * @param structType The struct type being unwound * @param stmtIt The statement iterator used for adding/removing statements + * @param currentBlock The current code block * @param structUnwinding Information about unwound struct value variables */ - private boolean unwindAssignment(StatementAssignment assignment, Variable assignedVar, ListIterator stmtIt, StructUnwinding structUnwinding) { + private boolean unwindAssignment(StatementAssignment assignment, SymbolTypeStruct structType, ListIterator stmtIt, ControlFlowBlock currentBlock, StructUnwinding structUnwinding) { boolean modified = false; - // Assigning a struct! - if(assignment.getOperator() == null && assignment.getrValue2() instanceof StructZero) { - // Initializing a struct - unwind to assigning zero to each member! - StructUnwinding.VariableUnwinding variableUnwinding = structUnwinding.getVariableUnwinding(assignedVar.getRef()); - if(variableUnwinding != null) { + + StructMemberUnwinding memberUnwinding = getStructMemberUnwinding(assignment.getlValue(), structType, structUnwinding, assignment, stmtIt, currentBlock); + if(memberUnwinding==null) { + throw new CompileError("Cannot unwind struct assignment " + assignment.toString(getProgram(), false), assignment); + } + + if(assignment.getOperator() == null && assignment.getrValue2() instanceof StructZero && assignment.getlValue() instanceof VariableRef) { + // Zero-initializing a struct - unwind to assigning zero to each member! stmtIt.previous(); - for(String memberName : variableUnwinding.getMemberNames()) { - VariableRef memberVarRef = variableUnwinding.getMemberUnwinding(memberName); + for(String memberName : memberUnwinding.getMemberNames()) { + VariableRef memberVarRef = (VariableRef) memberUnwinding.getMemberUnwinding(memberName); Variable memberVar = getScope().getVariable(memberVarRef); Statement initStmt = Pass0GenerateStatementSequence.createDefaultInitializationStatement(memberVarRef, memberVar.getType(), assignment.getSource(), Comment.NO_COMMENTS); stmtIt.add(initStmt); @@ -280,18 +287,33 @@ public class Pass1UnwindStructValues extends Pass1Base { stmtIt.next(); stmtIt.remove(); modified = true; + } else if(assignment.getOperator() == null && assignment.getrValue2() instanceof ValueList) { + // Initializing struct with a value list - unwind to assigning each member with a value from the list + ValueList valueList = (ValueList) assignment.getrValue2(); + if(memberUnwinding.getMemberNames().size() != valueList.getList().size()) { + throw new CompileError("Struct initialization list has wrong size. Need " + memberUnwinding.getMemberNames().size() + " got " + valueList.getList().size(), assignment); } - } else if(assignment.getOperator() == null && assignment.getrValue2() instanceof VariableRef) { - Variable sourceVar = getScope().getVariable((VariableRef) assignment.getrValue2()); - if(sourceVar.getType().equals(assignedVar.getType())) { + stmtIt.previous(); + int idx = 0; + for(String memberName : memberUnwinding.getMemberNames()) { + LValue memberLvalue = memberUnwinding.getMemberUnwinding(memberName); + Statement initStmt = new StatementAssignment(memberLvalue, valueList.getList().get(idx++), assignment.getSource(), Comment.NO_COMMENTS); + stmtIt.add(initStmt); + getLog().append("Adding struct value list initializer " + initStmt.toString(getProgram(), false)); + } + stmtIt.next(); + stmtIt.remove(); + modified = true; + } else if(assignment.getOperator() == null) { + SymbolType sourceType = SymbolTypeInference.inferType(getScope(), assignment.getrValue2()); + if(sourceType.equals(structType)) { // Copying a struct - unwind to assigning each member! - StructUnwinding.VariableUnwinding assignedMemberVariables = structUnwinding.getVariableUnwinding(assignedVar.getRef()); - StructUnwinding.VariableUnwinding sourceMemberVariables = structUnwinding.getVariableUnwinding(sourceVar.getRef()); - if(assignedMemberVariables != null && sourceMemberVariables != null) { + StructMemberUnwinding sourceMemberUnwinding = getStructMemberUnwinding((LValue) assignment.getrValue2(), structType, structUnwinding, assignment, stmtIt, currentBlock); + if(sourceMemberUnwinding != null) { stmtIt.previous(); - for(String memberName : assignedMemberVariables.getMemberNames()) { - VariableRef assignedMemberVarRef = assignedMemberVariables.getMemberUnwinding(memberName); - VariableRef sourceMemberVarRef = sourceMemberVariables.getMemberUnwinding(memberName); + for(String memberName : memberUnwinding.getMemberNames()) { + LValue assignedMemberVarRef = memberUnwinding.getMemberUnwinding(memberName); + LValue sourceMemberVarRef = sourceMemberUnwinding.getMemberUnwinding(memberName); Statement copyStmt = new StatementAssignment(assignedMemberVarRef, sourceMemberVarRef, assignment.getSource(), Comment.NO_COMMENTS); stmtIt.add(copyStmt); getLog().append("Adding struct value member variable copy " + copyStmt.toString(getProgram(), false)); @@ -303,32 +325,45 @@ public class Pass1UnwindStructValues extends Pass1Base { } else { throw new CompileError("Incompatible struct assignment " + assignment.toString(getProgram(), false), assignment); } - } else if(assignment.getOperator() == null && assignment.getrValue2() instanceof ValueList) { - // Initializing struct with individual values - unwind to assigning each member with a value from the list - StructUnwinding.VariableUnwinding variableUnwinding = structUnwinding.getVariableUnwinding(assignedVar.getRef()); - if(variableUnwinding != null) { - ValueList valueList = (ValueList) assignment.getrValue2(); - if(variableUnwinding.getMemberNames().size() != valueList.getList().size()) { - throw new CompileError("Struct initialization list has wrong size. Need " + variableUnwinding.getMemberNames().size() + " got " + valueList.getList().size(), assignment); - } - stmtIt.previous(); - int idx = 0; - for(String memberName : variableUnwinding.getMemberNames()) { - VariableRef memberVarRef = variableUnwinding.getMemberUnwinding(memberName); - Statement initStmt = new StatementAssignment(memberVarRef, valueList.getList().get(idx++), assignment.getSource(), Comment.NO_COMMENTS); - stmtIt.add(initStmt); - getLog().append("Adding struct value list initializer " + initStmt.toString(getProgram(), false)); - } - stmtIt.next(); - stmtIt.remove(); - modified = true; - } } else { throw new CompileError("Incompatible struct assignment " + assignment.toString(getProgram(), false), assignment); } return modified; } + private StructMemberUnwinding getStructMemberUnwinding(LValue lValue, SymbolTypeStruct lValueType, StructUnwinding structUnwinding, Statement currentStmt, ListIterator stmtIt, ControlFlowBlock currentBlock) { + if(lValue instanceof VariableRef) { + return structUnwinding.getVariableUnwinding((VariableRef) lValue); + } else if(lValue instanceof PointerDereferenceSimple) { + return new StructMemberUnwindingPointerDerefSimple((PointerDereferenceSimple) lValue, lValueType.getStructDefinition(getScope()), stmtIt, currentBlock, currentStmt); + } else if(lValue instanceof PointerDereferenceIndexed) { + return new StructMemberUnwindingPointerDerefIndexed((PointerDereferenceIndexed) lValue, lValueType.getStructDefinition(getScope()), stmtIt, currentBlock, currentStmt); + } else { + throw new InternalError("Struct unwinding not implemented for "+lValue.toString(getProgram())); + } + } + + + /** Information about how members of an struct Lvalue is unwound. */ + interface StructMemberUnwinding { + + /** + * Get the names of the members of the struct + * + * @return the names + */ + List getMemberNames(); + + /** + * Get the LValue that a specific member was unwound to + * + * @param memberName The member name + * @return The unwinding of the member + */ + LValue getMemberUnwinding(String memberName); + } + + /** * Keeps track of all structs that have been unwound into member variables. */ @@ -363,7 +398,7 @@ public class Pass1UnwindStructValues extends Pass1Base { /** Information about how a single struct variable was unwound. */ - static class VariableUnwinding { + static class VariableUnwinding implements StructMemberUnwinding { /** Maps member names to the unwound variables. */ Map memberUnwinding = new LinkedHashMap<>(); @@ -378,7 +413,7 @@ public class Pass1UnwindStructValues extends Pass1Base { * * @return the names */ - List getMemberNames() { + public List getMemberNames() { return new ArrayList<>(memberUnwinding.keySet()); } @@ -388,12 +423,93 @@ public class Pass1UnwindStructValues extends Pass1Base { * @param memberName The member name * @return The new variable */ - VariableRef getMemberUnwinding(String memberName) { + public LValue getMemberUnwinding(String memberName) { return this.memberUnwinding.get(memberName); } } } + /** Unwinding for a simple pointer deref to a struct. */ + private class StructMemberUnwindingPointerDerefSimple implements StructMemberUnwinding { + private final StructDefinition structDefinition; + private final ControlFlowBlock currentBlock; + private final ListIterator stmtIt; + private final PointerDereferenceSimple pointerDeref; + private final Statement currentStmt; + + public StructMemberUnwindingPointerDerefSimple(PointerDereferenceSimple pointerDeref, StructDefinition structDefinition, ListIterator stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) { + this.structDefinition = structDefinition; + this.currentBlock = currentBlock; + this.stmtIt = stmtIt; + this.pointerDeref = pointerDeref; + this.currentStmt = currentStmt; + } + + @Override + public List getMemberNames() { + Collection structMemberVars = structDefinition.getAllVariables(false); + ArrayList memberNames = new ArrayList<>(); + for(Variable structMemberVar : structMemberVars) { + memberNames.add(structMemberVar.getLocalName()); + } + return memberNames; + } + + @Override + public LValue getMemberUnwinding(String memberName) { + ConstantRef memberOffsetConstant = PassNStructPointerRewriting.getMemberOffsetConstant(getScope(), structDefinition, memberName); + Variable member = structDefinition.getMember(memberName); + Scope scope = getScope().getScope(currentBlock.getScope()); + VariableIntermediate memberAddress = scope.addVariableIntermediate(); + memberAddress.setType(new SymbolTypePointer(member.getType())); + CastValue structTypedPointer = new CastValue(new SymbolTypePointer(member.getType()), pointerDeref.getPointer()); + // Add statement $1 = ptr_struct + OFFSET_STRUCT_NAME_MEMBER + stmtIt.add(new StatementAssignment(memberAddress.getRef(), structTypedPointer, Operators.PLUS, memberOffsetConstant, currentStmt.getSource(), currentStmt.getComments())); + // Unwind to *(ptr_struct+OFFSET_STRUCT_NAME_MEMBER) + return new PointerDereferenceSimple(memberAddress.getRef()); + } + } + + /** Unwinding for a indexed pointer deref to a struct. */ + private class StructMemberUnwindingPointerDerefIndexed implements StructMemberUnwinding { + private final StructDefinition structDefinition; + private final ControlFlowBlock currentBlock; + private final ListIterator stmtIt; + private final PointerDereferenceIndexed pointerDeref; + private final Statement currentStmt; + + public StructMemberUnwindingPointerDerefIndexed(PointerDereferenceIndexed pointerDeref, StructDefinition structDefinition, ListIterator stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) { + this.structDefinition = structDefinition; + this.currentBlock = currentBlock; + this.stmtIt = stmtIt; + this.pointerDeref = pointerDeref; + this.currentStmt = currentStmt; + } + + @Override + public List getMemberNames() { + Collection structMemberVars = structDefinition.getAllVariables(false); + ArrayList memberNames = new ArrayList<>(); + for(Variable structMemberVar : structMemberVars) { + memberNames.add(structMemberVar.getLocalName()); + } + return memberNames; + } + + @Override + public LValue getMemberUnwinding(String memberName) { + ConstantRef memberOffsetConstant = PassNStructPointerRewriting.getMemberOffsetConstant(getScope(), structDefinition, memberName); + Variable member = structDefinition.getMember(memberName); + Scope scope = getScope().getScope(currentBlock.getScope()); + VariableIntermediate memberAddress = scope.addVariableIntermediate(); + memberAddress.setType(new SymbolTypePointer(member.getType())); + CastValue structTypedPointer = new CastValue(new SymbolTypePointer(member.getType()), pointerDeref.getPointer()); + // Add statement $1 = ptr_struct + OFFSET_STRUCT_NAME_MEMBER + stmtIt.add(new StatementAssignment(memberAddress.getRef(), structTypedPointer, Operators.PLUS, memberOffsetConstant, currentStmt.getSource(), currentStmt.getComments())); + // Unwind to *(ptr_struct+OFFSET_STRUCT_NAME_MEMBER[idx] + return new PointerDereferenceIndexed(memberAddress.getRef(), pointerDeref.getIndex()); + } + } } diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNStructPointerRewriting.java b/src/main/java/dk/camelot64/kickc/passes/PassNStructPointerRewriting.java index fc8f629ee..6f18cb48e 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNStructPointerRewriting.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNStructPointerRewriting.java @@ -92,7 +92,7 @@ public class PassNStructPointerRewriting extends Pass2SsaOptimization { * @param memberName The name of the struct member * @return The constant variable */ - private static ConstantRef getMemberOffsetConstant(ProgramScope programScope, StructDefinition structDefinition, String memberName) { + public static ConstantRef getMemberOffsetConstant(ProgramScope programScope, StructDefinition structDefinition, String memberName) { String typeConstName = getMemberOffsetConstantName(structDefinition, memberName); ConstantVar memberOffsetConstant = programScope.getConstant(typeConstName); if(memberOffsetConstant == null) { diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 75c3c3adc..8c6914f78 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -35,6 +35,11 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testStructPtr9() throws IOException, URISyntaxException { + compileAndCompare("struct-ptr-9"); + } + @Test public void testStructPtr8() throws IOException, URISyntaxException { compileAndCompare("struct-ptr-8"); diff --git a/src/test/kc/struct-ptr-9.kc b/src/test/kc/struct-ptr-9.kc new file mode 100644 index 000000000..1dd36833a --- /dev/null +++ b/src/test/kc/struct-ptr-9.kc @@ -0,0 +1,19 @@ +// Minimal struct - array access with struct value copying (and initializing) + +struct Point { + byte x; + byte y; +}; + +struct Point[2] points; + +void main() { + for( byte i: 0..1) { + points[i] = { 2, i }; + } + + const struct Point* SCREEN = 0x0400; + for( byte i: 0..1) { + SCREEN[i] = points[i]; + } +} \ No newline at end of file diff --git a/src/test/ref/struct-ptr-9.asm b/src/test/ref/struct-ptr-9.asm new file mode 100644 index 000000000..d0bd9a59d --- /dev/null +++ b/src/test/ref/struct-ptr-9.asm @@ -0,0 +1,34 @@ +// Minimal struct - array access with struct value copying (and initializing) +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const OFFSET_STRUCT_POINT_Y = 1 +main: { + .label SCREEN = $400 + ldy #0 + b1: + tya + asl + tax + lda #2 + sta points,x + tya + sta points+OFFSET_STRUCT_POINT_Y,x + iny + cpy #2 + bne b1 + ldx #0 + b2: + txa + asl + tay + lda points,y + sta SCREEN,y + lda points+OFFSET_STRUCT_POINT_Y,y + sta SCREEN+OFFSET_STRUCT_POINT_Y,y + inx + cpx #2 + bne b2 + rts +} + points: .fill 2*2, 0 diff --git a/src/test/ref/struct-ptr-9.cfg b/src/test/ref/struct-ptr-9.cfg new file mode 100644 index 000000000..cf26c2a06 --- /dev/null +++ b/src/test/ref/struct-ptr-9.cfg @@ -0,0 +1,31 @@ +@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 ) + [6] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 + [7] *((byte*)(const struct Point[2]) points#0 + (byte~) main::$2) ← (byte) 2 + [8] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$2) ← (byte) main::i#2 + [9] (byte) main::i#1 ← ++ (byte) main::i#2 + [10] if((byte) main::i#1!=(byte) 2) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 main::@2 + [11] (byte) main::i1#2 ← phi( main::@1/(byte) 0 main::@2/(byte) main::i1#1 ) + [12] (byte~) main::$3 ← (byte) main::i1#2 << (byte) 1 + [13] *((byte*)(const struct Point*) main::SCREEN#0 + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$3) + [14] *((byte*)(const struct Point*) main::SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) + [15] (byte) main::i1#1 ← ++ (byte) main::i1#2 + [16] if((byte) main::i1#1!=(byte) 2) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@2 + [17] return + to:@return diff --git a/src/test/ref/struct-ptr-9.log b/src/test/ref/struct-ptr-9.log new file mode 100644 index 000000000..7cc335334 --- /dev/null +++ b/src/test/ref/struct-ptr-9.log @@ -0,0 +1,605 @@ +Fixing pointer array-indexing *((struct Point[2]) points + (byte) main::i) +Fixing pointer array-indexing *((struct Point[2]) points + (byte) main::i1) +Fixing pointer array-indexing *((struct Point*) main::SCREEN + (byte) main::i1) +Adding struct value list initializer *((byte*) main::$4 + (byte~) main::$2) ← (number) 2 +Adding struct value list initializer *((byte*) main::$5 + (byte~) main::$2) ← (byte) main::i +Adding struct value member variable copy *((byte*) main::$6 + (byte~) main::$3) ← *((byte*) main::$7 + (byte~) main::$3) +Adding struct value member variable copy *((byte*) main::$8 + (byte~) main::$3) ← *((byte*) main::$9 + (byte~) main::$3) +Adding pointer type conversion cast (struct Point*) main::SCREEN in (struct Point*) main::SCREEN ← (number) $400 +Culled Empty Block (label) main::@4 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (struct Point[2]) points#0 ← { fill( 2, 0) } + to:@1 +main: scope:[main] from @1 + (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 ) + (byte~) main::$2 ← (byte) main::i#2 * (const byte) SIZEOF_STRUCT_POINT + (byte*) main::$4 ← (byte*)(struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::$4 + (byte~) main::$2) ← (number) 2 + (byte*) main::$5 ← (byte*)(struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::$5 + (byte~) main::$2) ← (byte) main::i#2 + (byte) main::i#1 ← (byte) main::i#2 + rangenext(0,1) + (bool~) main::$0 ← (byte) main::i#1 != rangelast(0,1) + if((bool~) main::$0) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 + (struct Point*) main::SCREEN#0 ← ((struct Point*)) (number) $400 + (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::$3 ← (byte) main::i1#2 * (const byte) SIZEOF_STRUCT_POINT + (byte*) main::$6 ← (byte*)(struct Point*) main::SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X + (byte*) main::$7 ← (byte*)(struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X + *((byte*) main::$6 + (byte~) main::$3) ← *((byte*) main::$7 + (byte~) main::$3) + (byte*) main::$8 ← (byte*)(struct Point*) main::SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y + (byte*) main::$9 ← (byte*)(struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y + *((byte*) main::$8 + (byte~) main::$3) ← *((byte*) main::$9 + (byte~) main::$3) + (byte) main::i1#1 ← (byte) main::i1#2 + rangenext(0,1) + (bool~) main::$1 ← (byte) main::i1#1 != rangelast(0,1) + if((bool~) main::$1) 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 +(byte) Point::x +(byte) Point::y +(const byte) SIZEOF_STRUCT_POINT = (byte) 2 +(void()) main() +(bool~) main::$0 +(bool~) main::$1 +(byte~) main::$2 +(byte~) main::$3 +(byte*) main::$4 +(byte*) main::$5 +(byte*) main::$6 +(byte*) main::$7 +(byte*) main::$8 +(byte*) main::$9 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(struct Point*) main::SCREEN +(struct Point*) 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 +(struct Point[2]) points +(struct Point[2]) points#0 + +Adding number conversion cast (unumber) 2 in *((byte*) main::$4 + (byte~) main::$2) ← (number) 2 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast *((byte*) main::$4 + (byte~) main::$2) ← (unumber)(number) 2 +Inlining cast (struct Point*) main::SCREEN#0 ← (struct Point*)(number) $400 +Successful SSA optimization Pass2InlineCast +Simplifying constant integer cast 2 +Simplifying constant pointer cast (struct Point*) 1024 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 2 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simple Condition (bool~) main::$0 [10] if((byte) main::i#1!=rangelast(0,1)) goto main::@1 +Simple Condition (bool~) main::$1 [23] if((byte) main::i1#1!=rangelast(0,1)) goto main::@3 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant right-side identified [0] (struct Point[2]) points#0 ← { fill( 2, 0) } +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const struct Point[2]) points#0 = { fill( 2, 0) } +Constant (const byte) main::i#0 = 0 +Constant (const struct Point*) main::SCREEN#0 = (struct Point*) 1024 +Constant (const byte) main::i1#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Constant value identified (byte*)points#0 in [4] (byte*) main::$4 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)points#0 in [6] (byte*) main::$5 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant value identified (byte*)main::SCREEN#0 in [15] (byte*) main::$6 ← (byte*)(const struct Point*) main::SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)points#0 in [16] (byte*) main::$7 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Constant value identified (byte*)main::SCREEN#0 in [18] (byte*) main::$8 ← (byte*)(const struct Point*) main::SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant value identified (byte*)points#0 in [19] (byte*) main::$9 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantValues +Resolved ranged next value [8] main::i#1 ← ++ main::i#2 to ++ +Resolved ranged comparison value [10] if(main::i#1!=rangelast(0,1)) goto main::@1 to (number) 2 +Resolved ranged next value [21] main::i1#1 ← ++ main::i1#2 to ++ +Resolved ranged comparison value [23] if(main::i1#1!=rangelast(0,1)) goto main::@3 to (number) 2 +Simplifying expression containing zero (byte*)points#0 in [4] (byte*) main::$4 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)main::SCREEN#0 in [15] (byte*) main::$6 ← (byte*)(const struct Point*) main::SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_X +Simplifying expression containing zero (byte*)points#0 in [16] (byte*) main::$7 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_X +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant (const byte) OFFSET_STRUCT_POINT_X +Successful SSA optimization PassNEliminateUnusedVars +Adding number conversion cast (unumber) 2 in if((byte) main::i#1!=(number) 2) goto main::@1 +Adding number conversion cast (unumber) 2 in if((byte) main::i1#1!=(number) 2) goto main::@3 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 2 +Simplifying constant integer cast 2 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 2 +Finalized unsigned number type (byte) 2 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Constant right-side identified [4] (byte*) main::$5 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant right-side identified [13] (byte*) main::$8 ← (byte*)(const struct Point*) main::SCREEN#0 + (const byte) OFFSET_STRUCT_POINT_Y +Constant right-side identified [14] (byte*) main::$9 ← (byte*)(const struct Point[2]) points#0 + (const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant (const byte*) main::$4 = (byte*)points#0 +Constant (const byte*) main::$5 = (byte*)points#0+OFFSET_STRUCT_POINT_Y +Constant (const byte*) main::$6 = (byte*)main::SCREEN#0 +Constant (const byte*) main::$7 = (byte*)points#0 +Constant (const byte*) main::$8 = (byte*)main::SCREEN#0+OFFSET_STRUCT_POINT_Y +Constant (const byte*) main::$9 = (byte*)points#0+OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantIdentification +Rewriting multiplication to use shift [1] (byte~) main::$2 ← (byte) main::i#2 * (const byte) SIZEOF_STRUCT_POINT +Rewriting multiplication to use shift [7] (byte~) main::$3 ← (byte) main::i1#2 * (const byte) SIZEOF_STRUCT_POINT +Successful SSA optimization Pass2MultiplyToShiftRewriting +Inlining constant with var siblings (const byte) main::i#0 +Inlining constant with var siblings (const byte) main::i1#0 +Constant inlined main::$5 = (byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y +Constant inlined main::i#0 = (byte) 0 +Constant inlined main::i1#0 = (byte) 0 +Constant inlined main::$6 = (byte*)(const struct Point*) main::SCREEN#0 +Constant inlined main::$4 = (byte*)(const struct Point[2]) points#0 +Constant inlined main::$9 = (byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y +Constant inlined main::$7 = (byte*)(const struct Point[2]) points#0 +Constant inlined main::$8 = (byte*)(const struct Point*) main::SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y +Successful SSA optimization Pass2ConstantInlining +Eliminating unused constant (const byte) SIZEOF_STRUCT_POINT +Successful SSA optimization PassNEliminateUnusedVars +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 2 initial phi equivalence classes +Coalesced [20] main::i1#3 ← main::i1#1 +Coalesced [21] main::i#3 ← main::i#1 +Coalesced down to 2 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 ) + [6] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 + [7] *((byte*)(const struct Point[2]) points#0 + (byte~) main::$2) ← (byte) 2 + [8] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$2) ← (byte) main::i#2 + [9] (byte) main::i#1 ← ++ (byte) main::i#2 + [10] if((byte) main::i#1!=(byte) 2) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 main::@2 + [11] (byte) main::i1#2 ← phi( main::@1/(byte) 0 main::@2/(byte) main::i1#1 ) + [12] (byte~) main::$3 ← (byte) main::i1#2 << (byte) 1 + [13] *((byte*)(const struct Point*) main::SCREEN#0 + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$3) + [14] *((byte*)(const struct Point*) main::SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) + [15] (byte) main::i1#1 ← ++ (byte) main::i1#2 + [16] if((byte) main::i1#1!=(byte) 2) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@2 + [17] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte) Point::x +(byte) Point::y +(void()) main() +(byte~) main::$2 16.5 +(byte~) main::$3 27.5 +(struct Point*) main::SCREEN +(byte) main::i +(byte) main::i#1 16.5 +(byte) main::i#2 11.0 +(byte) main::i1 +(byte) main::i1#1 16.5 +(byte) main::i1#2 8.25 +(struct Point[2]) points + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ main::i1#2 main::i1#1 ] +Added variable main::$2 to zero page equivalence class [ main::$2 ] +Added variable main::$3 to zero page equivalence class [ main::$3 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ main::i1#2 main::i1#1 ] +[ main::$2 ] +[ main::$3 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Allocated zp ZP_BYTE:3 [ main::i1#2 main::i1#1 ] +Allocated zp ZP_BYTE:4 [ main::$2 ] +Allocated zp ZP_BYTE:5 [ main::$3 ] + +INITIAL ASM +//SEG0 File Comments +// Minimal struct - array access with struct value copying (and initializing) +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .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 +//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: { + .label SCREEN = $400 + .label _2 = 4 + .label _3 = 5 + .label i = 2 + .label i1 = 3 + //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 + jmp b1 + //SEG13 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + //SEG14 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + jmp b1 + //SEG15 main::@1 + b1: + //SEG16 [6] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 -- vbuz1=vbuz2_rol_1 + lda i + asl + sta _2 + //SEG17 [7] *((byte*)(const struct Point[2]) points#0 + (byte~) main::$2) ← (byte) 2 -- pbuc1_derefidx_vbuz1=vbuc2 + lda #2 + ldy _2 + sta points,y + //SEG18 [8] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuz1=vbuz2 + lda i + ldy _2 + sta points+OFFSET_STRUCT_POINT_Y,y + //SEG19 [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + //SEG20 [10] if((byte) main::i#1!=(byte) 2) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #2 + cmp i + bne b1_from_b1 + //SEG21 [11] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + //SEG22 [11] phi (byte) main::i1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuz1=vbuc1 + lda #0 + sta i1 + jmp b2 + //SEG23 [11] phi from main::@2 to main::@2 [phi:main::@2->main::@2] + b2_from_b2: + //SEG24 [11] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@2->main::@2#0] -- register_copy + jmp b2 + //SEG25 main::@2 + b2: + //SEG26 [12] (byte~) main::$3 ← (byte) main::i1#2 << (byte) 1 -- vbuz1=vbuz2_rol_1 + lda i1 + asl + sta _3 + //SEG27 [13] *((byte*)(const struct Point*) main::SCREEN#0 + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$3) -- pbuc1_derefidx_vbuz1=pbuc2_derefidx_vbuz1 + ldy _3 + lda points,y + sta SCREEN,y + //SEG28 [14] *((byte*)(const struct Point*) main::SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) -- pbuc1_derefidx_vbuz1=pbuc2_derefidx_vbuz1 + ldy _3 + lda points+OFFSET_STRUCT_POINT_Y,y + sta SCREEN+OFFSET_STRUCT_POINT_Y,y + //SEG29 [15] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuz1=_inc_vbuz1 + inc i1 + //SEG30 [16] if((byte) main::i1#1!=(byte) 2) goto main::@2 -- vbuz1_neq_vbuc1_then_la1 + lda #2 + cmp i1 + bne b2_from_b2 + jmp breturn + //SEG31 main::@return + breturn: + //SEG32 [17] return + rts +} + points: .fill 2*2, 0 + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 [ main::i#2 main::$2 ] ( main:2 [ main::i#2 main::$2 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Statement [7] *((byte*)(const struct Point[2]) points#0 + (byte~) main::$2) ← (byte) 2 [ main::i#2 main::$2 ] ( main:2 [ main::i#2 main::$2 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:4 [ main::$2 ] +Statement [8] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$2) ← (byte) main::i#2 [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Statement [12] (byte~) main::$3 ← (byte) main::i1#2 << (byte) 1 [ main::i1#2 main::$3 ] ( main:2 [ main::i1#2 main::$3 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:3 [ main::i1#2 main::i1#1 ] +Statement [13] *((byte*)(const struct Point*) main::SCREEN#0 + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$3) [ main::i1#2 main::$3 ] ( main:2 [ main::i1#2 main::$3 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:5 [ main::$3 ] +Statement [14] *((byte*)(const struct Point*) main::SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) [ main::i1#2 ] ( main:2 [ main::i1#2 ] ) always clobbers reg byte a +Statement [6] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 [ main::i#2 main::$2 ] ( main:2 [ main::i#2 main::$2 ] ) always clobbers reg byte a +Statement [7] *((byte*)(const struct Point[2]) points#0 + (byte~) main::$2) ← (byte) 2 [ main::i#2 main::$2 ] ( main:2 [ main::i#2 main::$2 ] ) always clobbers reg byte a +Statement [8] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$2) ← (byte) main::i#2 [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Statement [12] (byte~) main::$3 ← (byte) main::i1#2 << (byte) 1 [ main::i1#2 main::$3 ] ( main:2 [ main::i1#2 main::$3 ] ) always clobbers reg byte a +Statement [13] *((byte*)(const struct Point*) main::SCREEN#0 + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$3) [ main::i1#2 main::$3 ] ( main:2 [ main::i1#2 main::$3 ] ) always clobbers reg byte a +Statement [14] *((byte*)(const struct Point*) main::SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) [ main::i1#2 ] ( main:2 [ main::i1#2 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:3 [ main::i1#2 main::i1#1 ] : zp ZP_BYTE:3 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:4 [ main::$2 ] : zp ZP_BYTE:4 , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:5 [ main::$3 ] : zp ZP_BYTE:5 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 27.5: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 27.5: zp ZP_BYTE:5 [ main::$3 ] 24.75: zp ZP_BYTE:3 [ main::i1#2 main::i1#1 ] 16.5: zp ZP_BYTE:4 [ main::$2 ] +Uplift Scope [Point] +Uplift Scope [] + +Uplifting [main] best 788 combination reg byte y [ main::i#2 main::i#1 ] reg byte y [ main::$3 ] reg byte x [ main::i1#2 main::i1#1 ] reg byte x [ main::$2 ] +Uplifting [Point] best 788 combination +Uplifting [] best 788 combination + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Minimal struct - array access with struct value copying (and initializing) +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .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 +//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: { + .label SCREEN = $400 + //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] -- vbuyy=vbuc1 + ldy #0 + jmp b1 + //SEG13 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + b1_from_b1: + //SEG14 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + jmp b1 + //SEG15 main::@1 + b1: + //SEG16 [6] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 -- vbuxx=vbuyy_rol_1 + tya + asl + tax + //SEG17 [7] *((byte*)(const struct Point[2]) points#0 + (byte~) main::$2) ← (byte) 2 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #2 + sta points,x + //SEG18 [8] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuxx=vbuyy + tya + sta points+OFFSET_STRUCT_POINT_Y,x + //SEG19 [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuyy=_inc_vbuyy + iny + //SEG20 [10] if((byte) main::i#1!=(byte) 2) goto main::@1 -- vbuyy_neq_vbuc1_then_la1 + cpy #2 + bne b1_from_b1 + //SEG21 [11] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + b2_from_b1: + //SEG22 [11] phi (byte) main::i1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuxx=vbuc1 + ldx #0 + jmp b2 + //SEG23 [11] phi from main::@2 to main::@2 [phi:main::@2->main::@2] + b2_from_b2: + //SEG24 [11] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@2->main::@2#0] -- register_copy + jmp b2 + //SEG25 main::@2 + b2: + //SEG26 [12] (byte~) main::$3 ← (byte) main::i1#2 << (byte) 1 -- vbuyy=vbuxx_rol_1 + txa + asl + tay + //SEG27 [13] *((byte*)(const struct Point*) main::SCREEN#0 + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$3) -- pbuc1_derefidx_vbuyy=pbuc2_derefidx_vbuyy + lda points,y + sta SCREEN,y + //SEG28 [14] *((byte*)(const struct Point*) main::SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) -- pbuc1_derefidx_vbuyy=pbuc2_derefidx_vbuyy + lda points+OFFSET_STRUCT_POINT_Y,y + sta SCREEN+OFFSET_STRUCT_POINT_Y,y + //SEG29 [15] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuxx=_inc_vbuxx + inx + //SEG30 [16] if((byte) main::i1#1!=(byte) 2) goto main::@2 -- vbuxx_neq_vbuc1_then_la1 + cpx #2 + bne b2_from_b2 + jmp breturn + //SEG31 main::@return + breturn: + //SEG32 [17] return + rts +} + points: .fill 2*2, 0 + +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 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 +(byte) Point::x +(byte) Point::y +(void()) main() +(byte~) main::$2 reg byte x 16.5 +(byte~) main::$3 reg byte y 27.5 +(label) main::@1 +(label) main::@2 +(label) main::@return +(struct Point*) main::SCREEN +(const struct Point*) main::SCREEN#0 SCREEN = (struct Point*) 1024 +(byte) main::i +(byte) main::i#1 reg byte y 16.5 +(byte) main::i#2 reg byte y 11.0 +(byte) main::i1 +(byte) main::i1#1 reg byte x 16.5 +(byte) main::i1#2 reg byte x 8.25 +(struct Point[2]) points +(const struct Point[2]) points#0 points = { fill( 2, 0) } + +reg byte y [ main::i#2 main::i#1 ] +reg byte x [ main::i1#2 main::i1#1 ] +reg byte x [ main::$2 ] +reg byte y [ main::$3 ] + + +FINAL ASSEMBLER +Score: 626 + +//SEG0 File Comments +// Minimal struct - array access with struct value copying (and initializing) +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .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 [4] phi from @1 to main [phi:@1->main] +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +//SEG9 @end +//SEG10 main +main: { + .label SCREEN = $400 + //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] -- vbuyy=vbuc1 + ldy #0 + //SEG13 [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1] + //SEG14 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@1->main::@1#0] -- register_copy + //SEG15 main::@1 + b1: + //SEG16 [6] (byte~) main::$2 ← (byte) main::i#2 << (byte) 1 -- vbuxx=vbuyy_rol_1 + tya + asl + tax + //SEG17 [7] *((byte*)(const struct Point[2]) points#0 + (byte~) main::$2) ← (byte) 2 -- pbuc1_derefidx_vbuxx=vbuc2 + lda #2 + sta points,x + //SEG18 [8] *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuxx=vbuyy + tya + sta points+OFFSET_STRUCT_POINT_Y,x + //SEG19 [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuyy=_inc_vbuyy + iny + //SEG20 [10] if((byte) main::i#1!=(byte) 2) goto main::@1 -- vbuyy_neq_vbuc1_then_la1 + cpy #2 + bne b1 + //SEG21 [11] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + //SEG22 [11] phi (byte) main::i1#2 = (byte) 0 [phi:main::@1->main::@2#0] -- vbuxx=vbuc1 + ldx #0 + //SEG23 [11] phi from main::@2 to main::@2 [phi:main::@2->main::@2] + //SEG24 [11] phi (byte) main::i1#2 = (byte) main::i1#1 [phi:main::@2->main::@2#0] -- register_copy + //SEG25 main::@2 + b2: + //SEG26 [12] (byte~) main::$3 ← (byte) main::i1#2 << (byte) 1 -- vbuyy=vbuxx_rol_1 + txa + asl + tay + //SEG27 [13] *((byte*)(const struct Point*) main::SCREEN#0 + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0 + (byte~) main::$3) -- pbuc1_derefidx_vbuyy=pbuc2_derefidx_vbuyy + lda points,y + sta SCREEN,y + //SEG28 [14] *((byte*)(const struct Point*) main::SCREEN#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) ← *((byte*)(const struct Point[2]) points#0+(const byte) OFFSET_STRUCT_POINT_Y + (byte~) main::$3) -- pbuc1_derefidx_vbuyy=pbuc2_derefidx_vbuyy + lda points+OFFSET_STRUCT_POINT_Y,y + sta SCREEN+OFFSET_STRUCT_POINT_Y,y + //SEG29 [15] (byte) main::i1#1 ← ++ (byte) main::i1#2 -- vbuxx=_inc_vbuxx + inx + //SEG30 [16] if((byte) main::i1#1!=(byte) 2) goto main::@2 -- vbuxx_neq_vbuc1_then_la1 + cpx #2 + bne b2 + //SEG31 main::@return + //SEG32 [17] return + rts +} + points: .fill 2*2, 0 + diff --git a/src/test/ref/struct-ptr-9.sym b/src/test/ref/struct-ptr-9.sym new file mode 100644 index 000000000..38c8949d3 --- /dev/null +++ b/src/test/ref/struct-ptr-9.sym @@ -0,0 +1,27 @@ +(label) @1 +(label) @begin +(label) @end +(const byte) OFFSET_STRUCT_POINT_Y OFFSET_STRUCT_POINT_Y = (byte) 1 +(byte) Point::x +(byte) Point::y +(void()) main() +(byte~) main::$2 reg byte x 16.5 +(byte~) main::$3 reg byte y 27.5 +(label) main::@1 +(label) main::@2 +(label) main::@return +(struct Point*) main::SCREEN +(const struct Point*) main::SCREEN#0 SCREEN = (struct Point*) 1024 +(byte) main::i +(byte) main::i#1 reg byte y 16.5 +(byte) main::i#2 reg byte y 11.0 +(byte) main::i1 +(byte) main::i1#1 reg byte x 16.5 +(byte) main::i1#2 reg byte x 8.25 +(struct Point[2]) points +(const struct Point[2]) points#0 points = { fill( 2, 0) } + +reg byte y [ main::i#2 main::i#1 ] +reg byte x [ main::i1#2 main::i1#1 ] +reg byte x [ main::$2 ] +reg byte y [ main::$3 ]