mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-11-27 04:49:27 +00:00
Implemented struct member unwinding when copying structs referenced through pointers/array indexing.
This commit is contained in:
parent
c8178378b2
commit
56c33cdac2
@ -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();
|
||||
|
@ -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<Statement> stmtIt, StructUnwinding structUnwinding) {
|
||||
private boolean unwindAssignment(StatementAssignment assignment, SymbolTypeStruct structType, ListIterator<Statement> 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<Statement> 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<String> 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<String, VariableRef> memberUnwinding = new LinkedHashMap<>();
|
||||
@ -378,7 +413,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
||||
*
|
||||
* @return the names
|
||||
*/
|
||||
List<String> getMemberNames() {
|
||||
public List<String> 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<Statement> stmtIt;
|
||||
private final PointerDereferenceSimple pointerDeref;
|
||||
private final Statement currentStmt;
|
||||
|
||||
public StructMemberUnwindingPointerDerefSimple(PointerDereferenceSimple pointerDeref, StructDefinition structDefinition, ListIterator<Statement> stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) {
|
||||
this.structDefinition = structDefinition;
|
||||
this.currentBlock = currentBlock;
|
||||
this.stmtIt = stmtIt;
|
||||
this.pointerDeref = pointerDeref;
|
||||
this.currentStmt = currentStmt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMemberNames() {
|
||||
Collection<Variable> structMemberVars = structDefinition.getAllVariables(false);
|
||||
ArrayList<String> 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<Statement> stmtIt;
|
||||
private final PointerDereferenceIndexed pointerDeref;
|
||||
private final Statement currentStmt;
|
||||
|
||||
public StructMemberUnwindingPointerDerefIndexed(PointerDereferenceIndexed pointerDeref, StructDefinition structDefinition, ListIterator<Statement> stmtIt, ControlFlowBlock currentBlock, Statement currentStmt) {
|
||||
this.structDefinition = structDefinition;
|
||||
this.currentBlock = currentBlock;
|
||||
this.stmtIt = stmtIt;
|
||||
this.pointerDeref = pointerDeref;
|
||||
this.currentStmt = currentStmt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMemberNames() {
|
||||
Collection<Variable> structMemberVars = structDefinition.getAllVariables(false);
|
||||
ArrayList<String> 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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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");
|
||||
|
19
src/test/kc/struct-ptr-9.kc
Normal file
19
src/test/kc/struct-ptr-9.kc
Normal file
@ -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];
|
||||
}
|
||||
}
|
34
src/test/ref/struct-ptr-9.asm
Normal file
34
src/test/ref/struct-ptr-9.asm
Normal file
@ -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
|
31
src/test/ref/struct-ptr-9.cfg
Normal file
31
src/test/ref/struct-ptr-9.cfg
Normal file
@ -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
|
605
src/test/ref/struct-ptr-9.log
Normal file
605
src/test/ref/struct-ptr-9.log
Normal file
@ -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
|
||||
|
27
src/test/ref/struct-ptr-9.sym
Normal file
27
src/test/ref/struct-ptr-9.sym
Normal file
@ -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 ]
|
Loading…
Reference in New Issue
Block a user