mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-10-21 02:24:34 +00:00
Implemented unwound struct address-of rewriting to use first member.
This commit is contained in:
parent
00c8d5c857
commit
afc1b74ef8
@ -274,6 +274,7 @@ public class Compiler {
|
|||||||
optimizations.add(new Pass2ConditionalAndOrRewriting(program));
|
optimizations.add(new Pass2ConditionalAndOrRewriting(program));
|
||||||
optimizations.add(new PassNAddBooleanCasts(program));
|
optimizations.add(new PassNAddBooleanCasts(program));
|
||||||
optimizations.add(new PassNStructPointerRewriting(program));
|
optimizations.add(new PassNStructPointerRewriting(program));
|
||||||
|
optimizations.add(new PassNStructAddressOfRewriting(program));
|
||||||
optimizations.add(new Pass2ConditionalJumpSequenceImprovement(program));
|
optimizations.add(new Pass2ConditionalJumpSequenceImprovement(program));
|
||||||
optimizations.add(new Pass2ConstantRValueConsolidation(program));
|
optimizations.add(new Pass2ConstantRValueConsolidation(program));
|
||||||
optimizations.add(new Pass2ConstantIdentification(program));
|
optimizations.add(new Pass2ConstantIdentification(program));
|
||||||
@ -387,7 +388,6 @@ public class Compiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void pass3Analysis() {
|
private void pass3Analysis() {
|
||||||
new PassNEliminateStructUnwoundPlaceholder(program).step();
|
|
||||||
new Pass3AssertNoTypeId(program).check();
|
new Pass3AssertNoTypeId(program).check();
|
||||||
new Pass3AssertRValues(program).check();
|
new Pass3AssertRValues(program).check();
|
||||||
new Pass3AssertNoNumbers(program).check();
|
new Pass3AssertNoNumbers(program).check();
|
||||||
|
@ -49,10 +49,12 @@ public class LiveRangeEquivalenceClass {
|
|||||||
}
|
}
|
||||||
LiveRangeVariables liveRanges = set.getProgram().getLiveRangeVariables();
|
LiveRangeVariables liveRanges = set.getProgram().getLiveRangeVariables();
|
||||||
LiveRange varLiveRange = liveRanges.getLiveRange(variable);
|
LiveRange varLiveRange = liveRanges.getLiveRange(variable);
|
||||||
if(liveRange.overlaps(varLiveRange)) {
|
if(varLiveRange!=null) {
|
||||||
throw new RuntimeException("Compilation error! Variable live range overlaps live range equivalence class live range. " + variable);
|
if(liveRange.overlaps(varLiveRange)) {
|
||||||
|
throw new RuntimeException("Compilation error! Variable live range overlaps live range equivalence class live range. " + variable);
|
||||||
|
}
|
||||||
|
liveRange.add(varLiveRange);
|
||||||
}
|
}
|
||||||
liveRange.add(varLiveRange);
|
|
||||||
variables.add(variable);
|
variables.add(variable);
|
||||||
set.setVarClass(variable, this);
|
set.setVarClass(variable, this);
|
||||||
}
|
}
|
||||||
|
@ -770,6 +770,28 @@ public interface ProgramValue {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ProgramValueStructUnwoundPlaceholderMember implements ProgramValue {
|
||||||
|
private StructUnwoundPlaceholder placeholder;
|
||||||
|
private int idx;
|
||||||
|
|
||||||
|
public ProgramValueStructUnwoundPlaceholderMember(StructUnwoundPlaceholder placeholder, int idx) {
|
||||||
|
this.placeholder = placeholder;
|
||||||
|
this.idx = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Value get() {
|
||||||
|
return placeholder.getUnwoundMembers().get(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(Value value) {
|
||||||
|
placeholder.getUnwoundMembers().set(idx, (RValue) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pointer index inside a indexed pointer dererence value.
|
* Pointer index inside a indexed pointer dererence value.
|
||||||
*/
|
*/
|
||||||
|
@ -188,6 +188,11 @@ public class ProgramValueIterator {
|
|||||||
subValues.add(new ProgramValue.ProgramValuePointer((PointerDereference) value));
|
subValues.add(new ProgramValue.ProgramValuePointer((PointerDereference) value));
|
||||||
} else if(value instanceof StructMemberRef) {
|
} else if(value instanceof StructMemberRef) {
|
||||||
subValues.add(new ProgramValue.ProgramValueStruct((StructMemberRef) value));
|
subValues.add(new ProgramValue.ProgramValueStruct((StructMemberRef) value));
|
||||||
|
} else if(value instanceof StructUnwoundPlaceholder) {
|
||||||
|
int size = ((StructUnwoundPlaceholder) value).getUnwoundMembers().size();
|
||||||
|
for(int i = 0; i < size; i++) {
|
||||||
|
subValues.add(new ProgramValue.ProgramValueStructUnwoundPlaceholderMember((StructUnwoundPlaceholder) value, i));
|
||||||
|
}
|
||||||
} else if(value instanceof ValueList) {
|
} else if(value instanceof ValueList) {
|
||||||
ValueList valueList = (ValueList) value;
|
ValueList valueList = (ValueList) value;
|
||||||
int size = valueList.getList().size();
|
int size = valueList.getList().size();
|
||||||
@ -226,7 +231,6 @@ public class ProgramValueIterator {
|
|||||||
value instanceof ConstantLiteral ||
|
value instanceof ConstantLiteral ||
|
||||||
value instanceof ConstantRef ||
|
value instanceof ConstantRef ||
|
||||||
value instanceof StructZero ||
|
value instanceof StructZero ||
|
||||||
value instanceof StructUnwoundPlaceholder ||
|
|
||||||
value instanceof LabelRef
|
value instanceof LabelRef
|
||||||
) {
|
) {
|
||||||
// No sub values
|
// No sub values
|
||||||
|
@ -3,23 +3,45 @@ package dk.camelot64.kickc.model.values;
|
|||||||
import dk.camelot64.kickc.model.Program;
|
import dk.camelot64.kickc.model.Program;
|
||||||
import dk.camelot64.kickc.model.types.SymbolTypeStruct;
|
import dk.camelot64.kickc.model.types.SymbolTypeStruct;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/** Used as a placeholder value, where a struct has been unwound. */
|
/** Used as a placeholder value, where a struct has been unwound. */
|
||||||
public class StructUnwoundPlaceholder implements RValue {
|
public class StructUnwoundPlaceholder implements RValue {
|
||||||
|
|
||||||
public StructUnwoundPlaceholder(SymbolTypeStruct typeStruct) {
|
public StructUnwoundPlaceholder(SymbolTypeStruct typeStruct, List<RValue> unwoundMembers) {
|
||||||
this.typeStruct = typeStruct;
|
this.typeStruct = typeStruct;
|
||||||
|
this.unwoundMembers = unwoundMembers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The type of the struct. */
|
/** The type of the struct. */
|
||||||
private SymbolTypeStruct typeStruct;
|
private SymbolTypeStruct typeStruct;
|
||||||
|
|
||||||
|
/** The unwound struct members. */
|
||||||
|
private List<RValue> unwoundMembers;
|
||||||
|
|
||||||
public SymbolTypeStruct getTypeStruct() {
|
public SymbolTypeStruct getTypeStruct() {
|
||||||
return typeStruct;
|
return typeStruct;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<RValue> getUnwoundMembers() {
|
||||||
|
return unwoundMembers;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(Program program) {
|
public String toString(Program program) {
|
||||||
return "struct-unwound";
|
StringBuffer str = new StringBuffer();
|
||||||
|
str.append("struct-unwound {");
|
||||||
|
boolean first = true;
|
||||||
|
for(RValue unwoundMember : unwoundMembers) {
|
||||||
|
if(first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
str.append(", ");
|
||||||
|
}
|
||||||
|
str.append(unwoundMember.toString(program));
|
||||||
|
}
|
||||||
|
str.append("}");
|
||||||
|
return str.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -276,9 +276,11 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
|
|
||||||
if(assignment.getOperator() == null && assignment.getrValue2() instanceof StructZero && assignment.getlValue() instanceof VariableRef) {
|
if(assignment.getOperator() == null && assignment.getrValue2() instanceof StructZero && assignment.getlValue() instanceof VariableRef) {
|
||||||
// Zero-initializing a struct - unwind to assigning zero to each member!
|
// Zero-initializing a struct - unwind to assigning zero to each member!
|
||||||
|
List<RValue> membersUnwound = new ArrayList<>();
|
||||||
stmtIt.previous();
|
stmtIt.previous();
|
||||||
for(String memberName : memberUnwinding.getMemberNames()) {
|
for(String memberName : memberUnwinding.getMemberNames()) {
|
||||||
VariableRef memberVarRef = (VariableRef) memberUnwinding.getMemberUnwinding(memberName);
|
VariableRef memberVarRef = (VariableRef) memberUnwinding.getMemberUnwinding(memberName);
|
||||||
|
membersUnwound.add(memberVarRef);
|
||||||
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);
|
||||||
@ -286,7 +288,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
}
|
}
|
||||||
stmtIt.next();
|
stmtIt.next();
|
||||||
if(assignment.getlValue() instanceof VariableRef) {
|
if(assignment.getlValue() instanceof VariableRef) {
|
||||||
assignment.setrValue2(new StructUnwoundPlaceholder(structType));
|
assignment.setrValue2(new StructUnwoundPlaceholder(structType, membersUnwound));
|
||||||
} else {
|
} else {
|
||||||
stmtIt.remove();
|
stmtIt.remove();
|
||||||
}
|
}
|
||||||
@ -298,16 +300,18 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
throw new CompileError("Struct initialization list has wrong size. Need " + memberUnwinding.getMemberNames().size() + " got " + valueList.getList().size(), assignment);
|
throw new CompileError("Struct initialization list has wrong size. Need " + memberUnwinding.getMemberNames().size() + " got " + valueList.getList().size(), assignment);
|
||||||
}
|
}
|
||||||
stmtIt.previous();
|
stmtIt.previous();
|
||||||
|
List<RValue> membersUnwound = new ArrayList<>();
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
for(String memberName : memberUnwinding.getMemberNames()) {
|
for(String memberName : memberUnwinding.getMemberNames()) {
|
||||||
LValue memberLvalue = memberUnwinding.getMemberUnwinding(memberName);
|
LValue memberLvalue = memberUnwinding.getMemberUnwinding(memberName);
|
||||||
|
membersUnwound.add(memberLvalue);
|
||||||
Statement initStmt = new StatementAssignment(memberLvalue, valueList.getList().get(idx++), assignment.getSource(), Comment.NO_COMMENTS);
|
Statement initStmt = new StatementAssignment(memberLvalue, valueList.getList().get(idx++), assignment.getSource(), Comment.NO_COMMENTS);
|
||||||
stmtIt.add(initStmt);
|
stmtIt.add(initStmt);
|
||||||
getLog().append("Adding struct value list initializer " + initStmt.toString(getProgram(), false));
|
getLog().append("Adding struct value list initializer " + initStmt.toString(getProgram(), false));
|
||||||
}
|
}
|
||||||
stmtIt.next();
|
stmtIt.next();
|
||||||
if(assignment.getlValue() instanceof VariableRef) {
|
if(assignment.getlValue() instanceof VariableRef) {
|
||||||
assignment.setrValue2(new StructUnwoundPlaceholder(structType));
|
assignment.setrValue2(new StructUnwoundPlaceholder(structType, membersUnwound));
|
||||||
} else {
|
} else {
|
||||||
stmtIt.remove();
|
stmtIt.remove();
|
||||||
}
|
}
|
||||||
@ -320,17 +324,19 @@ public class Pass1UnwindStructValues extends Pass1Base {
|
|||||||
// Copying a struct - unwind to assigning each member!
|
// Copying a struct - unwind to assigning each member!
|
||||||
StructMemberUnwinding sourceMemberUnwinding = getStructMemberUnwinding((LValue) assignment.getrValue2(), structType, structUnwinding, assignment, stmtIt, currentBlock);
|
StructMemberUnwinding sourceMemberUnwinding = getStructMemberUnwinding((LValue) assignment.getrValue2(), structType, structUnwinding, assignment, stmtIt, currentBlock);
|
||||||
if(sourceMemberUnwinding != null) {
|
if(sourceMemberUnwinding != null) {
|
||||||
|
List<RValue> membersUnwound = new ArrayList<>();
|
||||||
stmtIt.previous();
|
stmtIt.previous();
|
||||||
for(String memberName : memberUnwinding.getMemberNames()) {
|
for(String memberName : memberUnwinding.getMemberNames()) {
|
||||||
LValue assignedMemberVarRef = memberUnwinding.getMemberUnwinding(memberName);
|
LValue assignedMemberVarRef = memberUnwinding.getMemberUnwinding(memberName);
|
||||||
LValue sourceMemberVarRef = sourceMemberUnwinding.getMemberUnwinding(memberName);
|
LValue sourceMemberVarRef = sourceMemberUnwinding.getMemberUnwinding(memberName);
|
||||||
|
membersUnwound.add(assignedMemberVarRef);
|
||||||
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));
|
||||||
}
|
}
|
||||||
stmtIt.next();
|
stmtIt.next();
|
||||||
if(assignment.getlValue() instanceof VariableRef) {
|
if(assignment.getlValue() instanceof VariableRef) {
|
||||||
assignment.setrValue2(new StructUnwoundPlaceholder(structType));
|
assignment.setrValue2(new StructUnwoundPlaceholder(structType, membersUnwound));
|
||||||
} else {
|
} else {
|
||||||
stmtIt.remove();
|
stmtIt.remove();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package dk.camelot64.kickc.passes;
|
package dk.camelot64.kickc.passes;
|
||||||
|
|
||||||
import dk.camelot64.kickc.model.CompileError;
|
import dk.camelot64.kickc.model.CompileError;
|
||||||
|
import dk.camelot64.kickc.model.ConstantNotLiteral;
|
||||||
import dk.camelot64.kickc.model.ControlFlowBlock;
|
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||||
import dk.camelot64.kickc.model.Program;
|
import dk.camelot64.kickc.model.Program;
|
||||||
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
|
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
|
||||||
@ -80,11 +81,18 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!SymbolTypeConversion.assignmentTypeMatch(variableType, valueType)) {
|
if(!SymbolTypeConversion.assignmentTypeMatch(variableType, valueType)) {
|
||||||
throw new CompileError(
|
ConstantLiteral constantLiteral = null;
|
||||||
"Constant variable has a non-matching type \n variable: " + variable.toString(getProgram()) +
|
try {
|
||||||
"\n value: (" + valueType.toString() + ") " + constVal.calculateLiteral(getScope()) +
|
constantLiteral = constVal.calculateLiteral(getScope());
|
||||||
"\n value definition: " + constVal.toString(getProgram())
|
} catch(ConstantNotLiteral e) {
|
||||||
);
|
// ignore
|
||||||
|
}
|
||||||
|
String literalStr = (constantLiteral == null) ? "null" : constantLiteral.toString(getProgram());
|
||||||
|
throw new CompileError(
|
||||||
|
"Constant variable has a non-matching type \n variable: " + variable.toString(getProgram()) +
|
||||||
|
"\n value: (" + valueType.toString() + ") " + literalStr +
|
||||||
|
"\n value definition: " + constVal.toString(getProgram())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConstantVar constantVar = new ConstantVar(
|
ConstantVar constantVar = new ConstantVar(
|
||||||
@ -197,7 +205,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
|
|||||||
// Volatile variables cannot be constant
|
// Volatile variables cannot be constant
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(assignment.getrValue1()==null && assignment.getOperator()==null && assignment.getrValue2() instanceof ConstantValue) {
|
if(assignment.getrValue1() == null && assignment.getOperator() == null && assignment.getrValue2() instanceof ConstantValue) {
|
||||||
constants.put(variable, new ConstantVariableValue(variable, (ConstantValue) assignment.getrValue2(), assignment));
|
constants.put(variable, new ConstantVariableValue(variable, (ConstantValue) assignment.getrValue2(), assignment));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -324,7 +332,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
|
|||||||
// If the symbol is part of an unwound struct - look at the struct itself
|
// If the symbol is part of an unwound struct - look at the struct itself
|
||||||
Pass1UnwindStructValues.StructUnwinding structUnwinding = program.getStructUnwinding();
|
Pass1UnwindStructValues.StructUnwinding structUnwinding = program.getStructUnwinding();
|
||||||
VariableRef structVarRef = structUnwinding.getContainingStructVariable(symbolRef);
|
VariableRef structVarRef = structUnwinding.getContainingStructVariable(symbolRef);
|
||||||
if(structVarRef!=null) {
|
if(structVarRef != null) {
|
||||||
return isAddressOfUsed(structVarRef, program);
|
return isAddressOfUsed(structVarRef, program);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
package dk.camelot64.kickc.passes;
|
|
||||||
|
|
||||||
import dk.camelot64.kickc.model.ControlFlowBlock;
|
|
||||||
import dk.camelot64.kickc.model.Program;
|
|
||||||
import dk.camelot64.kickc.model.statements.Statement;
|
|
||||||
import dk.camelot64.kickc.model.statements.StatementAssignment;
|
|
||||||
import dk.camelot64.kickc.model.values.StructUnwoundPlaceholder;
|
|
||||||
|
|
||||||
import java.util.ListIterator;
|
|
||||||
|
|
||||||
/** Remove any assignments with {@link dk.camelot64.kickc.model.values.StructUnwoundPlaceholder} as RValue */
|
|
||||||
public class PassNEliminateStructUnwoundPlaceholder extends Pass2SsaOptimization {
|
|
||||||
|
|
||||||
public PassNEliminateStructUnwoundPlaceholder(Program program) {
|
|
||||||
super(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean step() {
|
|
||||||
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
|
||||||
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
|
|
||||||
while(stmtIt.hasNext()) {
|
|
||||||
Statement stmt = stmtIt.next();
|
|
||||||
if(stmt instanceof StatementAssignment) {
|
|
||||||
StatementAssignment assignment = (StatementAssignment) stmt;
|
|
||||||
if(assignment.getOperator() == null && assignment.getrValue2() instanceof StructUnwoundPlaceholder) {
|
|
||||||
getLog().append("Eliminating struct unwound placeholder "+stmt.toString(getProgram(), false));
|
|
||||||
stmtIt.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -6,8 +6,8 @@ import dk.camelot64.kickc.model.VariableReferenceInfos;
|
|||||||
import dk.camelot64.kickc.model.statements.*;
|
import dk.camelot64.kickc.model.statements.*;
|
||||||
import dk.camelot64.kickc.model.symbols.ConstantVar;
|
import dk.camelot64.kickc.model.symbols.ConstantVar;
|
||||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||||
import dk.camelot64.kickc.model.symbols.Scope;
|
|
||||||
import dk.camelot64.kickc.model.symbols.Variable;
|
import dk.camelot64.kickc.model.symbols.Variable;
|
||||||
|
import dk.camelot64.kickc.model.types.SymbolTypeStruct;
|
||||||
import dk.camelot64.kickc.model.values.LValue;
|
import dk.camelot64.kickc.model.values.LValue;
|
||||||
import dk.camelot64.kickc.model.values.VariableRef;
|
import dk.camelot64.kickc.model.values.VariableRef;
|
||||||
|
|
||||||
@ -41,9 +41,27 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
|
|||||||
LValue lValue = assignment.getlValue();
|
LValue lValue = assignment.getlValue();
|
||||||
if(lValue instanceof VariableRef && referenceInfos.isUnused((VariableRef) lValue) && !Pass2ConstantIdentification.isAddressOfUsed((VariableRef) lValue, getProgram())) {
|
if(lValue instanceof VariableRef && referenceInfos.isUnused((VariableRef) lValue) && !Pass2ConstantIdentification.isAddressOfUsed((VariableRef) lValue, getProgram())) {
|
||||||
Variable variable = getScope().getVariable((VariableRef) lValue);
|
Variable variable = getScope().getVariable((VariableRef) lValue);
|
||||||
if(variable==null || !variable.isDeclaredVolatile()) {
|
boolean eliminate = false;
|
||||||
|
if(variable == null) {
|
||||||
|
// Already deleted
|
||||||
|
eliminate = true;
|
||||||
|
} else if(!variable.isDeclaredVolatile()) {
|
||||||
|
// Not volatile
|
||||||
|
eliminate = true;
|
||||||
|
} else if(variable.isDeclaredVolatile() && variable.getType() instanceof SymbolTypeStruct) {
|
||||||
|
// If an unwound volatile struct - eliminate it
|
||||||
|
if(variable.getRef().isVersion()) {
|
||||||
|
String fullNameUnversioned = variable.getRef().getFullNameUnversioned();
|
||||||
|
VariableRef unversionedRef = new VariableRef(fullNameUnversioned);
|
||||||
|
Pass1UnwindStructValues.StructUnwinding.VariableUnwinding variableUnwinding = getProgram().getStructUnwinding().getVariableUnwinding(unversionedRef);
|
||||||
|
if(variableUnwinding != null) {
|
||||||
|
eliminate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(eliminate) {
|
||||||
if(!pass2 && isReturnValue(variable)) {
|
if(!pass2 && isReturnValue(variable)) {
|
||||||
// Do not eliminate reutn variables in pass 1
|
// Do not eliminate return variables in pass 1
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if(pass2 || getLog().isVerbosePass1CreateSsa()) {
|
if(pass2 || getLog().isVerbosePass1CreateSsa()) {
|
||||||
@ -124,11 +142,12 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if a variable is the return value for a procedure
|
* Determines if a variable is the return value for a procedure
|
||||||
|
*
|
||||||
* @param variable The variable
|
* @param variable The variable
|
||||||
* @return true if this is the return variable for a function
|
* @return true if this is the return variable for a function
|
||||||
*/
|
*/
|
||||||
private boolean isReturnValue(Variable variable) {
|
private boolean isReturnValue(Variable variable) {
|
||||||
if(variable==null) return false;
|
if(variable == null) return false;
|
||||||
return variable.getScope() instanceof Procedure && variable.getLocalName().equals("return");
|
return variable.getScope() instanceof Procedure && variable.getLocalName().equals("return");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,86 @@
|
|||||||
|
package dk.camelot64.kickc.passes;
|
||||||
|
|
||||||
|
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||||
|
import dk.camelot64.kickc.model.Program;
|
||||||
|
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
|
||||||
|
import dk.camelot64.kickc.model.operators.Operators;
|
||||||
|
import dk.camelot64.kickc.model.statements.Statement;
|
||||||
|
import dk.camelot64.kickc.model.statements.StatementAssignment;
|
||||||
|
import dk.camelot64.kickc.model.statements.StatementLValue;
|
||||||
|
import dk.camelot64.kickc.model.symbols.Symbol;
|
||||||
|
import dk.camelot64.kickc.model.types.SymbolTypePointer;
|
||||||
|
import dk.camelot64.kickc.model.types.SymbolTypeStruct;
|
||||||
|
import dk.camelot64.kickc.model.values.*;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rewrite struct address-of to use the first member if the struct is unwound
|
||||||
|
*/
|
||||||
|
public class PassNStructAddressOfRewriting extends Pass2SsaOptimization {
|
||||||
|
|
||||||
|
public PassNStructAddressOfRewriting(Program program) {
|
||||||
|
super(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean step() {
|
||||||
|
AtomicBoolean modified = new AtomicBoolean(false);
|
||||||
|
ProgramValueIterator.execute(getProgram(), (programValue, currentStmt, stmtIt, currentBlock) -> {
|
||||||
|
Value value = programValue.get();
|
||||||
|
if(value instanceof ConstantSymbolPointer) {
|
||||||
|
ConstantSymbolPointer constantSymbolPointer = (ConstantSymbolPointer) value;
|
||||||
|
SymbolRef toSymbolRef = constantSymbolPointer.getToSymbol();
|
||||||
|
Symbol toSymbol = getScope().getSymbol(toSymbolRef);
|
||||||
|
if(toSymbol.getType() instanceof SymbolTypeStruct) {
|
||||||
|
RValue rewrite = rewriteStructAddressOf((VariableRef) toSymbol);
|
||||||
|
if(rewrite!=null) {
|
||||||
|
programValue.set(rewrite);
|
||||||
|
}
|
||||||
|
getLog().append("Rewriting struct address-of to first member "+value.toString(getProgram()));
|
||||||
|
modified.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Examine all statements
|
||||||
|
for(ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) {
|
||||||
|
for(Statement statement : block.getStatements()) {
|
||||||
|
if(statement instanceof StatementAssignment) {
|
||||||
|
StatementAssignment assignment = (StatementAssignment) statement;
|
||||||
|
if(Operators.ADDRESS_OF.equals(assignment.getOperator()) ) {
|
||||||
|
RValue rValue = assignment.getrValue2();
|
||||||
|
if(rValue instanceof SymbolVariableRef) {
|
||||||
|
Symbol toSymbol = getScope().getSymbol((SymbolVariableRef) rValue);
|
||||||
|
if(toSymbol.getType() instanceof SymbolTypeStruct) {
|
||||||
|
RValue rewrite = rewriteStructAddressOf((VariableRef) toSymbol.getRef());
|
||||||
|
if(rewrite!=null) {
|
||||||
|
assignment.setOperator(null);
|
||||||
|
assignment.setrValue2(rewrite);
|
||||||
|
getLog().append("Rewriting struct address-of to first member "+assignment.toString(getProgram(), false));
|
||||||
|
}
|
||||||
|
modified.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modified.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private RValue rewriteStructAddressOf(VariableRef toSymbol) {
|
||||||
|
StatementLValue toSymbolAssignment = getGraph().getAssignment(toSymbol);
|
||||||
|
if(toSymbolAssignment instanceof StatementAssignment) {
|
||||||
|
StatementAssignment assignment = (StatementAssignment) toSymbolAssignment;
|
||||||
|
if(assignment.getrValue2() instanceof StructUnwoundPlaceholder) {
|
||||||
|
// Found placeholder assignment!
|
||||||
|
StructUnwoundPlaceholder placeholder = (StructUnwoundPlaceholder) assignment.getrValue2();
|
||||||
|
SymbolRef firstMember = (SymbolRef) placeholder.getUnwoundMembers().get(0);
|
||||||
|
return new CastValue(new SymbolTypePointer(placeholder.getTypeStruct()), new ConstantSymbolPointer(firstMember));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -75,15 +75,13 @@ public class TestPrograms {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStructPtr12Ref() throws IOException, URISyntaxException {
|
public void testStructPtr12Ref() throws IOException, URISyntaxException {
|
||||||
compileAndCompare("struct-ptr-12-ref", log());
|
compileAndCompare("struct-ptr-12-ref");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
@Test
|
@Test
|
||||||
public void testStructPtr12() throws IOException, URISyntaxException {
|
public void testStructPtr12() throws IOException, URISyntaxException {
|
||||||
compileAndCompare("struct-ptr-12", log().verboseCreateSsa().verboseParse().verboseStatementSequence());
|
compileAndCompare("struct-ptr-12");
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStructPtr11() throws IOException, URISyntaxException {
|
public void testStructPtr11() throws IOException, URISyntaxException {
|
||||||
|
@ -5,7 +5,7 @@ struct Point {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
struct Point p = { 2, 3 };
|
volatile struct Point p = { 2, 3 };
|
||||||
struct Point *q = &p;
|
struct Point *q = &p;
|
||||||
const byte* SCREEN = 0x0400;
|
const byte* SCREEN = 0x0400;
|
||||||
SCREEN[0] = q->x;
|
SCREEN[0] = q->x;
|
||||||
|
@ -473,7 +473,7 @@ getCharToProcess::@1: scope:[getCharToProcess] from getCharToProcess::@10 getCh
|
|||||||
(byte) getCharToProcess::return_x#1 ← (byte) getCharToProcess::closest_x#2
|
(byte) getCharToProcess::return_x#1 ← (byte) getCharToProcess::closest_x#2
|
||||||
(byte) getCharToProcess::return_y#1 ← (byte) getCharToProcess::closest_y#2
|
(byte) getCharToProcess::return_y#1 ← (byte) getCharToProcess::closest_y#2
|
||||||
(word) getCharToProcess::return_dist#1 ← (word) getCharToProcess::closest_dist#4
|
(word) getCharToProcess::return_dist#1 ← (word) getCharToProcess::closest_dist#4
|
||||||
(struct ProcessingChar) getCharToProcess::return#0 ← struct-unwound
|
(struct ProcessingChar) getCharToProcess::return#0 ← struct-unwound {(byte) getCharToProcess::return_x#1, (byte) getCharToProcess::return_y#1, (word) getCharToProcess::return_dist#1}
|
||||||
to:getCharToProcess::@return
|
to:getCharToProcess::@return
|
||||||
getCharToProcess::@11: scope:[getCharToProcess] from getCharToProcess::@10
|
getCharToProcess::@11: scope:[getCharToProcess] from getCharToProcess::@10
|
||||||
(word) getCharToProcess::closest_dist#7 ← phi( getCharToProcess::@10/(word) getCharToProcess::closest_dist#3 )
|
(word) getCharToProcess::closest_dist#7 ← phi( getCharToProcess::@10/(word) getCharToProcess::closest_dist#3 )
|
||||||
@ -492,7 +492,7 @@ getCharToProcess::@return: scope:[getCharToProcess] from getCharToProcess::@1
|
|||||||
(byte) getCharToProcess::return_x#2 ← (byte) getCharToProcess::return_x#4
|
(byte) getCharToProcess::return_x#2 ← (byte) getCharToProcess::return_x#4
|
||||||
(byte) getCharToProcess::return_y#2 ← (byte) getCharToProcess::return_y#4
|
(byte) getCharToProcess::return_y#2 ← (byte) getCharToProcess::return_y#4
|
||||||
(word) getCharToProcess::return_dist#2 ← (word) getCharToProcess::return_dist#4
|
(word) getCharToProcess::return_dist#2 ← (word) getCharToProcess::return_dist#4
|
||||||
(struct ProcessingChar) getCharToProcess::return#1 ← struct-unwound
|
(struct ProcessingChar) getCharToProcess::return#1 ← struct-unwound {(byte) getCharToProcess::return_x#2, (byte) getCharToProcess::return_y#2, (word) getCharToProcess::return_dist#2}
|
||||||
return
|
return
|
||||||
to:@return
|
to:@return
|
||||||
startProcessing: scope:[startProcessing] from main::@6
|
startProcessing: scope:[startProcessing] from main::@6
|
||||||
@ -2597,9 +2597,9 @@ Simplifying expression containing zero (word*)processChars::processing#0 in [371
|
|||||||
Simplifying expression containing zero (word*)processChars::processing#0 in [371] *((word*)(struct ProcessingSprite*) processChars::processing#0 + (const byte) OFFSET_STRUCT_PROCESSINGSPRITE_X) ← *((word*)(struct ProcessingSprite*) processChars::processing#0) + *((word*)(struct ProcessingSprite*) processChars::processing#0 + (const byte) OFFSET_STRUCT_PROCESSINGSPRITE_VX)
|
Simplifying expression containing zero (word*)processChars::processing#0 in [371] *((word*)(struct ProcessingSprite*) processChars::processing#0 + (const byte) OFFSET_STRUCT_PROCESSINGSPRITE_X) ← *((word*)(struct ProcessingSprite*) processChars::processing#0) + *((word*)(struct ProcessingSprite*) processChars::processing#0 + (const byte) OFFSET_STRUCT_PROCESSINGSPRITE_VX)
|
||||||
Successful SSA optimization PassNSimplifyExpressionWithZero
|
Successful SSA optimization PassNSimplifyExpressionWithZero
|
||||||
Eliminating unused variable (word) startProcessing::center_dist#0 and assignment [51] (word) startProcessing::center_dist#0 ← (word) main::center_dist#0
|
Eliminating unused variable (word) startProcessing::center_dist#0 and assignment [51] (word) startProcessing::center_dist#0 ← (word) main::center_dist#0
|
||||||
Eliminating unused variable (struct ProcessingChar) getCharToProcess::return#0 and assignment [72] (struct ProcessingChar) getCharToProcess::return#0 ← struct-unwound
|
Eliminating unused variable (struct ProcessingChar) getCharToProcess::return#0 and assignment [72] (struct ProcessingChar) getCharToProcess::return#0 ← struct-unwound {(byte) getCharToProcess::return_x#1, (byte) getCharToProcess::return_y#1, (word) getCharToProcess::return_dist#1}
|
||||||
Eliminating unused variable (byte*~) getCharToProcess::$12 and assignment [76] (byte*~) getCharToProcess::$12 ← (byte*~) getCharToProcess::$11 + (byte) getCharToProcess::return_x#1
|
Eliminating unused variable (byte*~) getCharToProcess::$12 and assignment [76] (byte*~) getCharToProcess::$12 ← (byte*~) getCharToProcess::$11 + (byte) getCharToProcess::return_x#1
|
||||||
Eliminating unused variable (struct ProcessingChar) getCharToProcess::return#1 and assignment [78] (struct ProcessingChar) getCharToProcess::return#1 ← struct-unwound
|
Eliminating unused variable (struct ProcessingChar) getCharToProcess::return#1 and assignment [78] (struct ProcessingChar) getCharToProcess::return#1 ← struct-unwound {(byte) getCharToProcess::return_x#1, (byte) getCharToProcess::return_y#1, (word) getCharToProcess::return_dist#1}
|
||||||
Eliminating unused variable (byte*) processChars::$40 and assignment [157] (byte*) processChars::$40 ← (byte*)(struct ProcessingSprite*) processChars::processing#0 + (const byte) OFFSET_STRUCT_PROCESSINGSPRITE_ID
|
Eliminating unused variable (byte*) processChars::$40 and assignment [157] (byte*) processChars::$40 ← (byte*)(struct ProcessingSprite*) processChars::processing#0 + (const byte) OFFSET_STRUCT_PROCESSINGSPRITE_ID
|
||||||
Eliminating unused variable (byte*) processChars::$41 and assignment [159] (byte*) processChars::$41 ← (byte*)(struct ProcessingSprite*) processChars::processing#0 + (const byte) OFFSET_STRUCT_PROCESSINGSPRITE_STATUS
|
Eliminating unused variable (byte*) processChars::$41 and assignment [159] (byte*) processChars::$41 ← (byte*)(struct ProcessingSprite*) processChars::processing#0 + (const byte) OFFSET_STRUCT_PROCESSINGSPRITE_STATUS
|
||||||
Eliminating unused variable (byte*) processChars::$42 and assignment [164] (byte*) processChars::$42 ← (byte*)(struct ProcessingSprite*) processChars::processing#0 + (const byte) OFFSET_STRUCT_PROCESSINGSPRITE_STATUS
|
Eliminating unused variable (byte*) processChars::$42 and assignment [164] (byte*) processChars::$42 ← (byte*)(struct ProcessingSprite*) processChars::processing#0 + (const byte) OFFSET_STRUCT_PROCESSINGSPRITE_STATUS
|
||||||
|
@ -61,14 +61,14 @@ point: scope:[point] from main
|
|||||||
(byte) point::p_y#0 ← (number) 3
|
(byte) point::p_y#0 ← (number) 3
|
||||||
(byte) point::return_x#1 ← (byte) point::p_x#0
|
(byte) point::return_x#1 ← (byte) point::p_x#0
|
||||||
(byte) point::return_y#1 ← (byte) point::p_y#0
|
(byte) point::return_y#1 ← (byte) point::p_y#0
|
||||||
(struct Point) point::return#0 ← struct-unwound
|
(struct Point) point::return#0 ← struct-unwound {(byte) point::return_x#1, (byte) point::return_y#1}
|
||||||
to:point::@return
|
to:point::@return
|
||||||
point::@return: scope:[point] from point
|
point::@return: scope:[point] from point
|
||||||
(byte) point::return_y#4 ← phi( point/(byte) point::return_y#1 )
|
(byte) point::return_y#4 ← phi( point/(byte) point::return_y#1 )
|
||||||
(byte) point::return_x#4 ← phi( point/(byte) point::return_x#1 )
|
(byte) point::return_x#4 ← phi( point/(byte) point::return_x#1 )
|
||||||
(byte) point::return_x#2 ← (byte) point::return_x#4
|
(byte) point::return_x#2 ← (byte) point::return_x#4
|
||||||
(byte) point::return_y#2 ← (byte) point::return_y#4
|
(byte) point::return_y#2 ← (byte) point::return_y#4
|
||||||
(struct Point) point::return#1 ← struct-unwound
|
(struct Point) point::return#1 ← struct-unwound {(byte) point::return_x#2, (byte) point::return_y#2}
|
||||||
return
|
return
|
||||||
to:@return
|
to:@return
|
||||||
@2: scope:[] from @begin
|
@2: scope:[] from @begin
|
||||||
@ -162,8 +162,8 @@ Constant (const byte) main::q_y#1 = point::return_y#0
|
|||||||
Successful SSA optimization Pass2ConstantIdentification
|
Successful SSA optimization Pass2ConstantIdentification
|
||||||
Simplifying expression containing zero main::SCREEN#0 in [11] *((const byte*) main::SCREEN#0 + (byte) 0) ← (const byte) main::q_x#1
|
Simplifying expression containing zero main::SCREEN#0 in [11] *((const byte*) main::SCREEN#0 + (byte) 0) ← (const byte) main::q_x#1
|
||||||
Successful SSA optimization PassNSimplifyExpressionWithZero
|
Successful SSA optimization PassNSimplifyExpressionWithZero
|
||||||
Eliminating unused variable (struct Point) point::return#0 and assignment [4] (struct Point) point::return#0 ← struct-unwound
|
Eliminating unused variable (struct Point) point::return#0 and assignment [4] (struct Point) point::return#0 ← struct-unwound {(const byte) point::p_x#0, (const byte) point::p_y#0}
|
||||||
Eliminating unused variable (struct Point) point::return#1 and assignment [5] (struct Point) point::return#1 ← struct-unwound
|
Eliminating unused variable (struct Point) point::return#1 and assignment [5] (struct Point) point::return#1 ← struct-unwound {(const byte) point::p_x#0, (const byte) point::p_y#0}
|
||||||
Eliminating unused constant (const byte) main::q_x#0
|
Eliminating unused constant (const byte) main::q_x#0
|
||||||
Eliminating unused constant (const byte) main::q_y#0
|
Eliminating unused constant (const byte) main::q_y#0
|
||||||
Successful SSA optimization PassNEliminateUnusedVars
|
Successful SSA optimization PassNEliminateUnusedVars
|
||||||
|
20
src/test/ref/struct-ptr-12.asm
Normal file
20
src/test/ref/struct-ptr-12.asm
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Minimal struct - using address-of
|
||||||
|
.pc = $801 "Basic"
|
||||||
|
:BasicUpstart(main)
|
||||||
|
.pc = $80d "Program"
|
||||||
|
.const OFFSET_STRUCT_POINT_Y = 1
|
||||||
|
main: {
|
||||||
|
.label q = p_x
|
||||||
|
.label SCREEN = $400
|
||||||
|
.label p_x = 2
|
||||||
|
.label p_y = 3
|
||||||
|
lda #2
|
||||||
|
sta p_x
|
||||||
|
lda #3
|
||||||
|
sta p_y
|
||||||
|
lda q
|
||||||
|
sta SCREEN
|
||||||
|
lda q+OFFSET_STRUCT_POINT_Y
|
||||||
|
sta SCREEN+1
|
||||||
|
rts
|
||||||
|
}
|
18
src/test/ref/struct-ptr-12.cfg
Normal file
18
src/test/ref/struct-ptr-12.cfg
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
@begin: scope:[] from
|
||||||
|
[0] phi()
|
||||||
|
to:@1
|
||||||
|
@1: scope:[] from @begin
|
||||||
|
[1] phi()
|
||||||
|
[2] call main
|
||||||
|
to:@end
|
||||||
|
@end: scope:[] from @1
|
||||||
|
[3] phi()
|
||||||
|
main: scope:[main] from @1
|
||||||
|
[4] (byte) main::p_x#0 ← (byte) 2
|
||||||
|
[5] (byte) main::p_y#0 ← (byte) 3
|
||||||
|
[6] *((const byte*) main::SCREEN#0) ← *((byte*)(const struct Point*) main::q#0)
|
||||||
|
[7] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(const struct Point*) main::q#0+(const byte) OFFSET_STRUCT_POINT_Y)
|
||||||
|
to:main::@return
|
||||||
|
main::@return: scope:[main] from main
|
||||||
|
[8] return
|
||||||
|
to:@return
|
358
src/test/ref/struct-ptr-12.log
Normal file
358
src/test/ref/struct-ptr-12.log
Normal file
@ -0,0 +1,358 @@
|
|||||||
|
Created struct value member variable (byte) main::p_x
|
||||||
|
Created struct value member variable (byte) main::p_y
|
||||||
|
Converted struct value to member variables (struct Point) main::p
|
||||||
|
Adding struct value list initializer (byte) main::p_x ← (number) 2
|
||||||
|
Adding struct value list initializer (byte) main::p_y ← (number) 3
|
||||||
|
Rewriting struct pointer member access *((struct Point*) main::q).x
|
||||||
|
Rewriting struct pointer member access *((struct Point*) main::q).y
|
||||||
|
Adding pointer type conversion cast (byte*) main::SCREEN in (byte*) main::SCREEN ← (number) $400
|
||||||
|
|
||||||
|
CONTROL FLOW GRAPH SSA
|
||||||
|
@begin: scope:[] from
|
||||||
|
to:@1
|
||||||
|
main: scope:[main] from @1
|
||||||
|
(byte) main::p_x#0 ← (number) 2
|
||||||
|
(byte) main::p_y#0 ← (number) 3
|
||||||
|
(struct Point) main::p#0 ← struct-unwound {(byte) main::p_x#0, (byte) main::p_y#0}
|
||||||
|
(struct Point*~) main::$0 ← & (struct Point) main::p#0
|
||||||
|
(struct Point*) main::q#0 ← (struct Point*~) main::$0
|
||||||
|
(byte*) main::SCREEN#0 ← ((byte*)) (number) $400
|
||||||
|
(byte*) main::$1 ← (byte*)(struct Point*) main::q#0 + (const byte) OFFSET_STRUCT_POINT_X
|
||||||
|
*((byte*) main::SCREEN#0 + (number) 0) ← *((byte*) main::$1)
|
||||||
|
(byte*) main::$2 ← (byte*)(struct Point*) main::q#0 + (const byte) OFFSET_STRUCT_POINT_Y
|
||||||
|
*((byte*) main::SCREEN#0 + (number) 1) ← *((byte*) main::$2)
|
||||||
|
to:main::@return
|
||||||
|
main::@return: scope:[main] from main
|
||||||
|
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
|
||||||
|
(void()) main()
|
||||||
|
(struct Point*~) main::$0
|
||||||
|
(byte*) main::$1
|
||||||
|
(byte*) main::$2
|
||||||
|
(label) main::@return
|
||||||
|
(byte*) main::SCREEN
|
||||||
|
(byte*) main::SCREEN#0
|
||||||
|
(struct Point) main::p
|
||||||
|
(struct Point) main::p#0
|
||||||
|
(byte) main::p_x
|
||||||
|
(byte) main::p_x#0
|
||||||
|
(byte) main::p_y
|
||||||
|
(byte) main::p_y#0
|
||||||
|
(struct Point*) main::q
|
||||||
|
(struct Point*) main::q#0
|
||||||
|
|
||||||
|
Adding number conversion cast (unumber) 2 in (byte) main::p_x#0 ← (number) 2
|
||||||
|
Adding number conversion cast (unumber) 3 in (byte) main::p_y#0 ← (number) 3
|
||||||
|
Adding number conversion cast (unumber) 0 in *((byte*) main::SCREEN#0 + (number) 0) ← *((byte*) main::$1)
|
||||||
|
Adding number conversion cast (unumber) 1 in *((byte*) main::SCREEN#0 + (number) 1) ← *((byte*) main::$2)
|
||||||
|
Successful SSA optimization PassNAddNumberTypeConversions
|
||||||
|
Inlining cast (byte) main::p_x#0 ← (unumber)(number) 2
|
||||||
|
Inlining cast (byte) main::p_y#0 ← (unumber)(number) 3
|
||||||
|
Inlining cast (byte*) main::SCREEN#0 ← (byte*)(number) $400
|
||||||
|
Successful SSA optimization Pass2InlineCast
|
||||||
|
Simplifying constant integer cast 2
|
||||||
|
Simplifying constant integer cast 3
|
||||||
|
Simplifying constant pointer cast (byte*) 1024
|
||||||
|
Simplifying constant integer cast 0
|
||||||
|
Simplifying constant integer cast 1
|
||||||
|
Successful SSA optimization PassNCastSimplification
|
||||||
|
Finalized unsigned number type (byte) 2
|
||||||
|
Finalized unsigned number type (byte) 3
|
||||||
|
Finalized unsigned number type (byte) 0
|
||||||
|
Finalized unsigned number type (byte) 1
|
||||||
|
Successful SSA optimization PassNFinalizeNumberTypeConversions
|
||||||
|
Alias (struct Point*) main::q#0 = (struct Point*~) main::$0
|
||||||
|
Successful SSA optimization Pass2AliasElimination
|
||||||
|
Rewriting struct address-of to first member [3] (struct Point*) main::q#0 ← (struct Point*)&(byte) main::p_x#0
|
||||||
|
Successful SSA optimization PassNStructAddressOfRewriting
|
||||||
|
Constant right-side identified [3] (struct Point*) main::q#0 ← (struct Point*)&(byte) main::p_x#0
|
||||||
|
Successful SSA optimization Pass2ConstantRValueConsolidation
|
||||||
|
Constant (const struct Point*) main::q#0 = (struct Point*)&main::p_x#0
|
||||||
|
Constant (const byte*) main::SCREEN#0 = (byte*) 1024
|
||||||
|
Successful SSA optimization Pass2ConstantIdentification
|
||||||
|
Constant value identified (byte*)main::q#0 in [6] (byte*) main::$1 ← (byte*)(const struct Point*) main::q#0 + (const byte) OFFSET_STRUCT_POINT_X
|
||||||
|
Constant value identified (byte*)main::q#0 in [8] (byte*) main::$2 ← (byte*)(const struct Point*) main::q#0 + (const byte) OFFSET_STRUCT_POINT_Y
|
||||||
|
Successful SSA optimization Pass2ConstantValues
|
||||||
|
Converting *(pointer+n) to pointer[n] [7] *((const byte*) main::SCREEN#0 + (byte) 0) ← *((byte*) main::$1) -- *((byte*)main::q#0 + OFFSET_STRUCT_POINT_X)
|
||||||
|
Converting *(pointer+n) to pointer[n] [9] *((const byte*) main::SCREEN#0 + (byte) 1) ← *((byte*) main::$2) -- *((byte*)main::q#0 + OFFSET_STRUCT_POINT_Y)
|
||||||
|
Successful SSA optimization Pass2InlineDerefIdx
|
||||||
|
Simplifying expression containing zero (byte*)main::q#0 in [6] (byte*) main::$1 ← (byte*)(const struct Point*) main::q#0 + (const byte) OFFSET_STRUCT_POINT_X
|
||||||
|
Simplifying expression containing zero (byte*)main::q#0 in [7] *((const byte*) main::SCREEN#0 + (byte) 0) ← *((byte*)(const struct Point*) main::q#0 + (const byte) OFFSET_STRUCT_POINT_X)
|
||||||
|
Simplifying expression containing zero main::SCREEN#0 in [7] *((const byte*) main::SCREEN#0 + (byte) 0) ← *((byte*)(const struct Point*) main::q#0)
|
||||||
|
Successful SSA optimization PassNSimplifyExpressionWithZero
|
||||||
|
Eliminating unused variable (struct Point) main::p#0 and assignment [2] (struct Point) main::p#0 ← struct-unwound {(byte) main::p_x#0, (byte) main::p_y#0}
|
||||||
|
Eliminating unused variable (byte*) main::$1 and assignment [3] (byte*) main::$1 ← (byte*)(const struct Point*) main::q#0
|
||||||
|
Eliminating unused variable (byte*) main::$2 and assignment [5] (byte*) main::$2 ← (byte*)(const struct Point*) main::q#0 + (const byte) OFFSET_STRUCT_POINT_Y
|
||||||
|
Eliminating unused constant (const byte) OFFSET_STRUCT_POINT_X
|
||||||
|
Successful SSA optimization PassNEliminateUnusedVars
|
||||||
|
Consolidated array index constant in *((byte*)main::q#0+OFFSET_STRUCT_POINT_Y)
|
||||||
|
Consolidated array index constant in *(main::SCREEN#0+1)
|
||||||
|
Successful SSA optimization Pass2ConstantAdditionElimination
|
||||||
|
Adding NOP phi() at start of @begin
|
||||||
|
Adding NOP phi() at start of @1
|
||||||
|
Adding NOP phi() at start of @2
|
||||||
|
Adding NOP phi() at start of @end
|
||||||
|
CALL GRAPH
|
||||||
|
Calls in [] to main:2
|
||||||
|
|
||||||
|
Created 0 initial phi equivalence classes
|
||||||
|
Coalesced down to 0 phi equivalence classes
|
||||||
|
Culled Empty Block (label) @2
|
||||||
|
Adding NOP phi() at start of @begin
|
||||||
|
Adding NOP phi() at start of @1
|
||||||
|
Adding NOP phi() at start of @end
|
||||||
|
|
||||||
|
FINAL CONTROL FLOW GRAPH
|
||||||
|
@begin: scope:[] from
|
||||||
|
[0] phi()
|
||||||
|
to:@1
|
||||||
|
@1: scope:[] from @begin
|
||||||
|
[1] phi()
|
||||||
|
[2] call main
|
||||||
|
to:@end
|
||||||
|
@end: scope:[] from @1
|
||||||
|
[3] phi()
|
||||||
|
main: scope:[main] from @1
|
||||||
|
[4] (byte) main::p_x#0 ← (byte) 2
|
||||||
|
[5] (byte) main::p_y#0 ← (byte) 3
|
||||||
|
[6] *((const byte*) main::SCREEN#0) ← *((byte*)(const struct Point*) main::q#0)
|
||||||
|
[7] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(const struct Point*) main::q#0+(const byte) OFFSET_STRUCT_POINT_Y)
|
||||||
|
to:main::@return
|
||||||
|
main::@return: scope:[main] from main
|
||||||
|
[8] return
|
||||||
|
to:@return
|
||||||
|
|
||||||
|
|
||||||
|
VARIABLE REGISTER WEIGHTS
|
||||||
|
(byte) Point::x
|
||||||
|
(byte) Point::y
|
||||||
|
(void()) main()
|
||||||
|
(byte*) main::SCREEN
|
||||||
|
(struct Point) main::p
|
||||||
|
(byte) main::p_x
|
||||||
|
(byte) main::p_x#0 20.0
|
||||||
|
(byte) main::p_y
|
||||||
|
(byte) main::p_y#0 20.0
|
||||||
|
(struct Point*) main::q
|
||||||
|
|
||||||
|
Initial phi equivalence classes
|
||||||
|
Complete equivalence classes
|
||||||
|
[ main::p_x#0 ]
|
||||||
|
[ main::p_y#0 ]
|
||||||
|
Allocated zp ZP_BYTE:2 [ main::p_x#0 ]
|
||||||
|
Allocated zp ZP_BYTE:3 [ main::p_y#0 ]
|
||||||
|
|
||||||
|
INITIAL ASM
|
||||||
|
//SEG0 File Comments
|
||||||
|
// Minimal struct - using address-of
|
||||||
|
//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
|
||||||
|
jsr main
|
||||||
|
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
|
||||||
|
bend_from_b1:
|
||||||
|
jmp bend
|
||||||
|
//SEG8 @end
|
||||||
|
bend:
|
||||||
|
//SEG9 main
|
||||||
|
main: {
|
||||||
|
.label q = p_x
|
||||||
|
.label SCREEN = $400
|
||||||
|
.label p_x = 2
|
||||||
|
.label p_y = 3
|
||||||
|
//SEG10 [4] (byte) main::p_x#0 ← (byte) 2 -- vbuz1=vbuc1
|
||||||
|
lda #2
|
||||||
|
sta p_x
|
||||||
|
//SEG11 [5] (byte) main::p_y#0 ← (byte) 3 -- vbuz1=vbuc1
|
||||||
|
lda #3
|
||||||
|
sta p_y
|
||||||
|
//SEG12 [6] *((const byte*) main::SCREEN#0) ← *((byte*)(const struct Point*) main::q#0) -- _deref_pbuc1=_deref_pbuc2
|
||||||
|
lda q
|
||||||
|
sta SCREEN
|
||||||
|
//SEG13 [7] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(const struct Point*) main::q#0+(const byte) OFFSET_STRUCT_POINT_Y) -- _deref_pbuc1=_deref_pbuc2
|
||||||
|
lda q+OFFSET_STRUCT_POINT_Y
|
||||||
|
sta SCREEN+1
|
||||||
|
jmp breturn
|
||||||
|
//SEG14 main::@return
|
||||||
|
breturn:
|
||||||
|
//SEG15 [8] return
|
||||||
|
rts
|
||||||
|
}
|
||||||
|
|
||||||
|
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||||
|
Statement [4] (byte) main::p_x#0 ← (byte) 2 [ ] ( main:2 [ ] ) always clobbers reg byte a
|
||||||
|
Statement [5] (byte) main::p_y#0 ← (byte) 3 [ ] ( main:2 [ ] ) always clobbers reg byte a
|
||||||
|
Statement [6] *((const byte*) main::SCREEN#0) ← *((byte*)(const struct Point*) main::q#0) [ ] ( main:2 [ ] ) always clobbers reg byte a
|
||||||
|
Statement [7] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(const struct Point*) main::q#0+(const byte) OFFSET_STRUCT_POINT_Y) [ ] ( main:2 [ ] ) always clobbers reg byte a
|
||||||
|
Potential registers zp ZP_BYTE:2 [ main::p_x#0 ] : zp ZP_BYTE:2 ,
|
||||||
|
Potential registers zp ZP_BYTE:3 [ main::p_y#0 ] : zp ZP_BYTE:3 ,
|
||||||
|
|
||||||
|
REGISTER UPLIFT SCOPES
|
||||||
|
Uplift Scope [main] 20: zp ZP_BYTE:2 [ main::p_x#0 ] 20: zp ZP_BYTE:3 [ main::p_y#0 ]
|
||||||
|
Uplift Scope [Point]
|
||||||
|
Uplift Scope []
|
||||||
|
|
||||||
|
Uplifting [main] best 47 combination zp ZP_BYTE:2 [ main::p_x#0 ] zp ZP_BYTE:3 [ main::p_y#0 ]
|
||||||
|
Uplifting [Point] best 47 combination
|
||||||
|
Uplifting [] best 47 combination
|
||||||
|
Attempting to uplift remaining variables inzp ZP_BYTE:2 [ main::p_x#0 ]
|
||||||
|
Uplifting [main] best 47 combination zp ZP_BYTE:2 [ main::p_x#0 ]
|
||||||
|
Attempting to uplift remaining variables inzp ZP_BYTE:3 [ main::p_y#0 ]
|
||||||
|
Uplifting [main] best 47 combination zp ZP_BYTE:3 [ main::p_y#0 ]
|
||||||
|
|
||||||
|
ASSEMBLER BEFORE OPTIMIZATION
|
||||||
|
//SEG0 File Comments
|
||||||
|
// Minimal struct - using address-of
|
||||||
|
//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
|
||||||
|
jsr main
|
||||||
|
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
|
||||||
|
bend_from_b1:
|
||||||
|
jmp bend
|
||||||
|
//SEG8 @end
|
||||||
|
bend:
|
||||||
|
//SEG9 main
|
||||||
|
main: {
|
||||||
|
.label q = p_x
|
||||||
|
.label SCREEN = $400
|
||||||
|
.label p_x = 2
|
||||||
|
.label p_y = 3
|
||||||
|
//SEG10 [4] (byte) main::p_x#0 ← (byte) 2 -- vbuz1=vbuc1
|
||||||
|
lda #2
|
||||||
|
sta p_x
|
||||||
|
//SEG11 [5] (byte) main::p_y#0 ← (byte) 3 -- vbuz1=vbuc1
|
||||||
|
lda #3
|
||||||
|
sta p_y
|
||||||
|
//SEG12 [6] *((const byte*) main::SCREEN#0) ← *((byte*)(const struct Point*) main::q#0) -- _deref_pbuc1=_deref_pbuc2
|
||||||
|
lda q
|
||||||
|
sta SCREEN
|
||||||
|
//SEG13 [7] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(const struct Point*) main::q#0+(const byte) OFFSET_STRUCT_POINT_Y) -- _deref_pbuc1=_deref_pbuc2
|
||||||
|
lda q+OFFSET_STRUCT_POINT_Y
|
||||||
|
sta SCREEN+1
|
||||||
|
jmp breturn
|
||||||
|
//SEG14 main::@return
|
||||||
|
breturn:
|
||||||
|
//SEG15 [8] return
|
||||||
|
rts
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSEMBLER OPTIMIZATIONS
|
||||||
|
Removing instruction jmp b1
|
||||||
|
Removing instruction jmp bend
|
||||||
|
Removing instruction jmp breturn
|
||||||
|
Succesful ASM optimization Pass5NextJumpElimination
|
||||||
|
Removing instruction b1_from_bbegin:
|
||||||
|
Removing instruction b1:
|
||||||
|
Removing instruction bend_from_b1:
|
||||||
|
Succesful ASM optimization Pass5RedundantLabelElimination
|
||||||
|
Removing instruction bend:
|
||||||
|
Removing instruction breturn:
|
||||||
|
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||||
|
Updating BasicUpstart to call main directly
|
||||||
|
Removing instruction jsr main
|
||||||
|
Succesful ASM optimization Pass5SkipBegin
|
||||||
|
Removing instruction bbegin:
|
||||||
|
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||||
|
|
||||||
|
FINAL SYMBOL TABLE
|
||||||
|
(label) @1
|
||||||
|
(label) @begin
|
||||||
|
(label) @end
|
||||||
|
(const byte) OFFSET_STRUCT_POINT_Y OFFSET_STRUCT_POINT_Y = (byte) 1
|
||||||
|
(byte) Point::x
|
||||||
|
(byte) Point::y
|
||||||
|
(void()) main()
|
||||||
|
(label) main::@return
|
||||||
|
(byte*) main::SCREEN
|
||||||
|
(const byte*) main::SCREEN#0 SCREEN = (byte*) 1024
|
||||||
|
(struct Point) main::p
|
||||||
|
(byte) main::p_x
|
||||||
|
(byte) main::p_x#0 p_x zp ZP_BYTE:2 20.0
|
||||||
|
(byte) main::p_y
|
||||||
|
(byte) main::p_y#0 p_y zp ZP_BYTE:3 20.0
|
||||||
|
(struct Point*) main::q
|
||||||
|
(const struct Point*) main::q#0 q = (struct Point*)&(byte) main::p_x#0
|
||||||
|
|
||||||
|
zp ZP_BYTE:2 [ main::p_x#0 ]
|
||||||
|
zp ZP_BYTE:3 [ main::p_y#0 ]
|
||||||
|
|
||||||
|
|
||||||
|
FINAL ASSEMBLER
|
||||||
|
Score: 32
|
||||||
|
|
||||||
|
//SEG0 File Comments
|
||||||
|
// Minimal struct - using address-of
|
||||||
|
//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 [3] phi from @1 to @end [phi:@1->@end]
|
||||||
|
//SEG8 @end
|
||||||
|
//SEG9 main
|
||||||
|
main: {
|
||||||
|
.label q = p_x
|
||||||
|
.label SCREEN = $400
|
||||||
|
.label p_x = 2
|
||||||
|
.label p_y = 3
|
||||||
|
//SEG10 [4] (byte) main::p_x#0 ← (byte) 2 -- vbuz1=vbuc1
|
||||||
|
lda #2
|
||||||
|
sta p_x
|
||||||
|
//SEG11 [5] (byte) main::p_y#0 ← (byte) 3 -- vbuz1=vbuc1
|
||||||
|
lda #3
|
||||||
|
sta p_y
|
||||||
|
//SEG12 [6] *((const byte*) main::SCREEN#0) ← *((byte*)(const struct Point*) main::q#0) -- _deref_pbuc1=_deref_pbuc2
|
||||||
|
lda q
|
||||||
|
sta SCREEN
|
||||||
|
//SEG13 [7] *((const byte*) main::SCREEN#0+(byte) 1) ← *((byte*)(const struct Point*) main::q#0+(const byte) OFFSET_STRUCT_POINT_Y) -- _deref_pbuc1=_deref_pbuc2
|
||||||
|
lda q+OFFSET_STRUCT_POINT_Y
|
||||||
|
sta SCREEN+1
|
||||||
|
//SEG14 main::@return
|
||||||
|
//SEG15 [8] return
|
||||||
|
rts
|
||||||
|
}
|
||||||
|
|
20
src/test/ref/struct-ptr-12.sym
Normal file
20
src/test/ref/struct-ptr-12.sym
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
(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()
|
||||||
|
(label) main::@return
|
||||||
|
(byte*) main::SCREEN
|
||||||
|
(const byte*) main::SCREEN#0 SCREEN = (byte*) 1024
|
||||||
|
(struct Point) main::p
|
||||||
|
(byte) main::p_x
|
||||||
|
(byte) main::p_x#0 p_x zp ZP_BYTE:2 20.0
|
||||||
|
(byte) main::p_y
|
||||||
|
(byte) main::p_y#0 p_y zp ZP_BYTE:3 20.0
|
||||||
|
(struct Point*) main::q
|
||||||
|
(const struct Point*) main::q#0 q = (struct Point*)&(byte) main::p_x#0
|
||||||
|
|
||||||
|
zp ZP_BYTE:2 [ main::p_x#0 ]
|
||||||
|
zp ZP_BYTE:3 [ main::p_y#0 ]
|
Loading…
Reference in New Issue
Block a user