mirror of
https://gitlab.com/camelot/kickc.git
synced 2025-08-09 04:25:12 +00:00
Implemented struct member unwinding when copying structs referenced through pointers/array indexing.
This commit is contained in:
@@ -169,8 +169,6 @@ public class Compiler {
|
|||||||
new Pass1AssertReturn(program).execute();
|
new Pass1AssertReturn(program).execute();
|
||||||
new Pass1AssertUsedVars(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()) {
|
if(getLog().isVerbosePass1CreateSsa()) {
|
||||||
getLog().append("SYMBOLS");
|
getLog().append("SYMBOLS");
|
||||||
@@ -180,6 +178,8 @@ public class Compiler {
|
|||||||
new Pass1FixLValuesLoHi(program).execute();
|
new Pass1FixLValuesLoHi(program).execute();
|
||||||
new Pass1AssertNoLValueIntermediate(program).execute();
|
new Pass1AssertNoLValueIntermediate(program).execute();
|
||||||
new Pass1PointerSizeofFix(program).execute(); // After this point in the code all pointer math is byte-based
|
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 PassNStructPointerRewriting(program).execute();
|
||||||
|
|
||||||
new PassNAddBooleanCasts(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.*;
|
||||||
import dk.camelot64.kickc.model.InternalError;
|
import dk.camelot64.kickc.model.InternalError;
|
||||||
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
|
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.Statement;
|
||||||
import dk.camelot64.kickc.model.statements.StatementAssignment;
|
import dk.camelot64.kickc.model.statements.StatementAssignment;
|
||||||
import dk.camelot64.kickc.model.statements.StatementCall;
|
import dk.camelot64.kickc.model.statements.StatementCall;
|
||||||
import dk.camelot64.kickc.model.statements.StatementReturn;
|
import dk.camelot64.kickc.model.statements.StatementReturn;
|
||||||
import dk.camelot64.kickc.model.symbols.*;
|
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.types.SymbolTypeStruct;
|
||||||
import dk.camelot64.kickc.model.values.*;
|
import dk.camelot64.kickc.model.values.*;
|
||||||
|
|
||||||
@@ -54,11 +58,9 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
Statement statement = stmtIt.next();
|
Statement statement = stmtIt.next();
|
||||||
if(statement instanceof StatementAssignment) {
|
if(statement instanceof StatementAssignment) {
|
||||||
StatementAssignment assignment = (StatementAssignment) statement;
|
StatementAssignment assignment = (StatementAssignment) statement;
|
||||||
if(assignment.getlValue() instanceof VariableRef) {
|
SymbolType lValueType = SymbolTypeInference.inferType(getScope(), assignment.getlValue());
|
||||||
Variable assignedVar = getScope().getVariable((VariableRef) assignment.getlValue());
|
if(lValueType instanceof SymbolTypeStruct) {
|
||||||
if(assignedVar.getType() instanceof SymbolTypeStruct) {
|
modified |= unwindAssignment(assignment, (SymbolTypeStruct)lValueType, stmtIt, block, structUnwinding);
|
||||||
modified |= unwindAssignment(assignment, assignedVar, stmtIt, structUnwinding);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if(statement instanceof StatementCall) {
|
} else if(statement instanceof StatementCall) {
|
||||||
modified |= unwindCall((StatementCall) statement, structUnwinding);
|
modified |= unwindCall((StatementCall) statement, structUnwinding);
|
||||||
@@ -86,7 +88,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
Variable structVariable = getScope().getVariable((VariableRef) structMemberRef.getStruct());
|
Variable structVariable = getScope().getVariable((VariableRef) structMemberRef.getStruct());
|
||||||
StructUnwinding.VariableUnwinding memberVariables = structUnwinding.getVariableUnwinding(structVariable.getRef());
|
StructUnwinding.VariableUnwinding memberVariables = structUnwinding.getVariableUnwinding(structVariable.getRef());
|
||||||
if(memberVariables != null) {
|
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()));
|
getLog().append("Replacing struct member reference " + structMemberRef.toString(getProgram()) + " with member variable reference " + structMemberVariable.toString(getProgram()));
|
||||||
programValue.set(structMemberVariable);
|
programValue.set(structMemberVariable);
|
||||||
modified.set(true);
|
modified.set(true);
|
||||||
@@ -200,7 +202,8 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
if(parameter.getType() instanceof SymbolTypeStruct) {
|
if(parameter.getType() instanceof SymbolTypeStruct) {
|
||||||
StructUnwinding.VariableUnwinding parameterUnwinding = structUnwinding.getVariableUnwinding(parameter.getRef());
|
StructUnwinding.VariableUnwinding parameterUnwinding = structUnwinding.getVariableUnwinding(parameter.getRef());
|
||||||
for(String memberName : parameterUnwinding.getMemberNames()) {
|
for(String memberName : parameterUnwinding.getMemberNames()) {
|
||||||
unwoundParameterNames.add(parameterUnwinding.getMemberUnwinding(memberName).getLocalName());
|
VariableRef memberUnwinding = (VariableRef) parameterUnwinding.getMemberUnwinding(memberName);
|
||||||
|
unwoundParameterNames.add(memberUnwinding.getLocalName());
|
||||||
procedureUnwound = true;
|
procedureUnwound = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -258,20 +261,24 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
* Unwind an assignment to a struct value variable into assignment of each member
|
* Unwind an assignment to a struct value variable into assignment of each member
|
||||||
*
|
*
|
||||||
* @param assignment The assignment statement
|
* @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 stmtIt The statement iterator used for adding/removing statements
|
||||||
|
* @param currentBlock The current code block
|
||||||
* @param structUnwinding Information about unwound struct value variables
|
* @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;
|
boolean modified = false;
|
||||||
// Assigning a struct!
|
|
||||||
if(assignment.getOperator() == null && assignment.getrValue2() instanceof StructZero) {
|
StructMemberUnwinding memberUnwinding = getStructMemberUnwinding(assignment.getlValue(), structType, structUnwinding, assignment, stmtIt, currentBlock);
|
||||||
// Initializing a struct - unwind to assigning zero to each member!
|
if(memberUnwinding==null) {
|
||||||
StructUnwinding.VariableUnwinding variableUnwinding = structUnwinding.getVariableUnwinding(assignedVar.getRef());
|
throw new CompileError("Cannot unwind struct assignment " + assignment.toString(getProgram(), false), assignment);
|
||||||
if(variableUnwinding != null) {
|
}
|
||||||
|
|
||||||
|
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();
|
stmtIt.previous();
|
||||||
for(String memberName : variableUnwinding.getMemberNames()) {
|
for(String memberName : memberUnwinding.getMemberNames()) {
|
||||||
VariableRef memberVarRef = variableUnwinding.getMemberUnwinding(memberName);
|
VariableRef memberVarRef = (VariableRef) memberUnwinding.getMemberUnwinding(memberName);
|
||||||
Variable memberVar = getScope().getVariable(memberVarRef);
|
Variable memberVar = getScope().getVariable(memberVarRef);
|
||||||
Statement initStmt = Pass0GenerateStatementSequence.createDefaultInitializationStatement(memberVarRef, memberVar.getType(), assignment.getSource(), Comment.NO_COMMENTS);
|
Statement initStmt = Pass0GenerateStatementSequence.createDefaultInitializationStatement(memberVarRef, memberVar.getType(), assignment.getSource(), Comment.NO_COMMENTS);
|
||||||
stmtIt.add(initStmt);
|
stmtIt.add(initStmt);
|
||||||
@@ -280,18 +287,33 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
stmtIt.next();
|
stmtIt.next();
|
||||||
stmtIt.remove();
|
stmtIt.remove();
|
||||||
modified = true;
|
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) {
|
stmtIt.previous();
|
||||||
Variable sourceVar = getScope().getVariable((VariableRef) assignment.getrValue2());
|
int idx = 0;
|
||||||
if(sourceVar.getType().equals(assignedVar.getType())) {
|
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!
|
// Copying a struct - unwind to assigning each member!
|
||||||
StructUnwinding.VariableUnwinding assignedMemberVariables = structUnwinding.getVariableUnwinding(assignedVar.getRef());
|
StructMemberUnwinding sourceMemberUnwinding = getStructMemberUnwinding((LValue) assignment.getrValue2(), structType, structUnwinding, assignment, stmtIt, currentBlock);
|
||||||
StructUnwinding.VariableUnwinding sourceMemberVariables = structUnwinding.getVariableUnwinding(sourceVar.getRef());
|
if(sourceMemberUnwinding != null) {
|
||||||
if(assignedMemberVariables != null && sourceMemberVariables != null) {
|
|
||||||
stmtIt.previous();
|
stmtIt.previous();
|
||||||
for(String memberName : assignedMemberVariables.getMemberNames()) {
|
for(String memberName : memberUnwinding.getMemberNames()) {
|
||||||
VariableRef assignedMemberVarRef = assignedMemberVariables.getMemberUnwinding(memberName);
|
LValue assignedMemberVarRef = memberUnwinding.getMemberUnwinding(memberName);
|
||||||
VariableRef sourceMemberVarRef = sourceMemberVariables.getMemberUnwinding(memberName);
|
LValue sourceMemberVarRef = sourceMemberUnwinding.getMemberUnwinding(memberName);
|
||||||
Statement copyStmt = new StatementAssignment(assignedMemberVarRef, sourceMemberVarRef, assignment.getSource(), Comment.NO_COMMENTS);
|
Statement copyStmt = new StatementAssignment(assignedMemberVarRef, sourceMemberVarRef, assignment.getSource(), Comment.NO_COMMENTS);
|
||||||
stmtIt.add(copyStmt);
|
stmtIt.add(copyStmt);
|
||||||
getLog().append("Adding struct value member variable copy " + copyStmt.toString(getProgram(), false));
|
getLog().append("Adding struct value member variable copy " + copyStmt.toString(getProgram(), false));
|
||||||
@@ -303,32 +325,45 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
} else {
|
} else {
|
||||||
throw new CompileError("Incompatible struct assignment " + assignment.toString(getProgram(), false), assignment);
|
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 {
|
} else {
|
||||||
throw new CompileError("Incompatible struct assignment " + assignment.toString(getProgram(), false), assignment);
|
throw new CompileError("Incompatible struct assignment " + assignment.toString(getProgram(), false), assignment);
|
||||||
}
|
}
|
||||||
return modified;
|
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.
|
* 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. */
|
/** Information about how a single struct variable was unwound. */
|
||||||
static class VariableUnwinding {
|
static class VariableUnwinding implements StructMemberUnwinding {
|
||||||
|
|
||||||
/** Maps member names to the unwound variables. */
|
/** Maps member names to the unwound variables. */
|
||||||
Map<String, VariableRef> memberUnwinding = new LinkedHashMap<>();
|
Map<String, VariableRef> memberUnwinding = new LinkedHashMap<>();
|
||||||
@@ -378,7 +413,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
*
|
*
|
||||||
* @return the names
|
* @return the names
|
||||||
*/
|
*/
|
||||||
List<String> getMemberNames() {
|
public List<String> getMemberNames() {
|
||||||
return new ArrayList<>(memberUnwinding.keySet());
|
return new ArrayList<>(memberUnwinding.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -388,12 +423,93 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
* @param memberName The member name
|
* @param memberName The member name
|
||||||
* @return The new variable
|
* @return The new variable
|
||||||
*/
|
*/
|
||||||
VariableRef getMemberUnwinding(String memberName) {
|
public LValue getMemberUnwinding(String memberName) {
|
||||||
return this.memberUnwinding.get(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
|
* @param memberName The name of the struct member
|
||||||
* @return The constant variable
|
* @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);
|
String typeConstName = getMemberOffsetConstantName(structDefinition, memberName);
|
||||||
ConstantVar memberOffsetConstant = programScope.getConstant(typeConstName);
|
ConstantVar memberOffsetConstant = programScope.getConstant(typeConstName);
|
||||||
if(memberOffsetConstant == null) {
|
if(memberOffsetConstant == null) {
|
||||||
|
@@ -35,6 +35,11 @@ public class TestPrograms {
|
|||||||
public TestPrograms() {
|
public TestPrograms() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStructPtr9() throws IOException, URISyntaxException {
|
||||||
|
compileAndCompare("struct-ptr-9");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStructPtr8() throws IOException, URISyntaxException {
|
public void testStructPtr8() throws IOException, URISyntaxException {
|
||||||
compileAndCompare("struct-ptr-8");
|
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 ]
|
Reference in New Issue
Block a user