1
0
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:
jespergravgaard 2019-06-16 19:54:03 +02:00
parent 00c8d5c857
commit afc1b74ef8
18 changed files with 617 additions and 71 deletions

View File

@ -274,6 +274,7 @@ public class Compiler {
optimizations.add(new Pass2ConditionalAndOrRewriting(program));
optimizations.add(new PassNAddBooleanCasts(program));
optimizations.add(new PassNStructPointerRewriting(program));
optimizations.add(new PassNStructAddressOfRewriting(program));
optimizations.add(new Pass2ConditionalJumpSequenceImprovement(program));
optimizations.add(new Pass2ConstantRValueConsolidation(program));
optimizations.add(new Pass2ConstantIdentification(program));
@ -387,7 +388,6 @@ public class Compiler {
}
private void pass3Analysis() {
new PassNEliminateStructUnwoundPlaceholder(program).step();
new Pass3AssertNoTypeId(program).check();
new Pass3AssertRValues(program).check();
new Pass3AssertNoNumbers(program).check();

View File

@ -49,10 +49,12 @@ public class LiveRangeEquivalenceClass {
}
LiveRangeVariables liveRanges = set.getProgram().getLiveRangeVariables();
LiveRange varLiveRange = liveRanges.getLiveRange(variable);
if(liveRange.overlaps(varLiveRange)) {
throw new RuntimeException("Compilation error! Variable live range overlaps live range equivalence class live range. " + variable);
if(varLiveRange!=null) {
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);
set.setVarClass(variable, this);
}

View File

@ -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.
*/

View File

@ -188,6 +188,11 @@ public class ProgramValueIterator {
subValues.add(new ProgramValue.ProgramValuePointer((PointerDereference) value));
} else if(value instanceof StructMemberRef) {
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) {
ValueList valueList = (ValueList) value;
int size = valueList.getList().size();
@ -226,7 +231,6 @@ public class ProgramValueIterator {
value instanceof ConstantLiteral ||
value instanceof ConstantRef ||
value instanceof StructZero ||
value instanceof StructUnwoundPlaceholder ||
value instanceof LabelRef
) {
// No sub values

View File

@ -3,23 +3,45 @@ package dk.camelot64.kickc.model.values;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.types.SymbolTypeStruct;
import java.util.List;
/** Used as a placeholder value, where a struct has been unwound. */
public class StructUnwoundPlaceholder implements RValue {
public StructUnwoundPlaceholder(SymbolTypeStruct typeStruct) {
public StructUnwoundPlaceholder(SymbolTypeStruct typeStruct, List<RValue> unwoundMembers) {
this.typeStruct = typeStruct;
this.unwoundMembers = unwoundMembers;
}
/** The type of the struct. */
private SymbolTypeStruct typeStruct;
/** The unwound struct members. */
private List<RValue> unwoundMembers;
public SymbolTypeStruct getTypeStruct() {
return typeStruct;
}
public List<RValue> getUnwoundMembers() {
return unwoundMembers;
}
@Override
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();
}
}

View File

@ -276,9 +276,11 @@ public class Pass1UnwindStructValues extends Pass1Base {
if(assignment.getOperator() == null && assignment.getrValue2() instanceof StructZero && assignment.getlValue() instanceof VariableRef) {
// Zero-initializing a struct - unwind to assigning zero to each member!
List<RValue> membersUnwound = new ArrayList<>();
stmtIt.previous();
for(String memberName : memberUnwinding.getMemberNames()) {
VariableRef memberVarRef = (VariableRef) memberUnwinding.getMemberUnwinding(memberName);
membersUnwound.add(memberVarRef);
Variable memberVar = getScope().getVariable(memberVarRef);
Statement initStmt = Pass0GenerateStatementSequence.createDefaultInitializationStatement(memberVarRef, memberVar.getType(), assignment.getSource(), Comment.NO_COMMENTS);
stmtIt.add(initStmt);
@ -286,7 +288,7 @@ public class Pass1UnwindStructValues extends Pass1Base {
}
stmtIt.next();
if(assignment.getlValue() instanceof VariableRef) {
assignment.setrValue2(new StructUnwoundPlaceholder(structType));
assignment.setrValue2(new StructUnwoundPlaceholder(structType, membersUnwound));
} else {
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);
}
stmtIt.previous();
List<RValue> membersUnwound = new ArrayList<>();
int idx = 0;
for(String memberName : memberUnwinding.getMemberNames()) {
LValue memberLvalue = memberUnwinding.getMemberUnwinding(memberName);
membersUnwound.add(memberLvalue);
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();
if(assignment.getlValue() instanceof VariableRef) {
assignment.setrValue2(new StructUnwoundPlaceholder(structType));
assignment.setrValue2(new StructUnwoundPlaceholder(structType, membersUnwound));
} else {
stmtIt.remove();
}
@ -320,17 +324,19 @@ public class Pass1UnwindStructValues extends Pass1Base {
// Copying a struct - unwind to assigning each member!
StructMemberUnwinding sourceMemberUnwinding = getStructMemberUnwinding((LValue) assignment.getrValue2(), structType, structUnwinding, assignment, stmtIt, currentBlock);
if(sourceMemberUnwinding != null) {
List<RValue> membersUnwound = new ArrayList<>();
stmtIt.previous();
for(String memberName : memberUnwinding.getMemberNames()) {
LValue assignedMemberVarRef = memberUnwinding.getMemberUnwinding(memberName);
LValue sourceMemberVarRef = sourceMemberUnwinding.getMemberUnwinding(memberName);
membersUnwound.add(assignedMemberVarRef);
Statement copyStmt = new StatementAssignment(assignedMemberVarRef, sourceMemberVarRef, assignment.getSource(), Comment.NO_COMMENTS);
stmtIt.add(copyStmt);
getLog().append("Adding struct value member variable copy " + copyStmt.toString(getProgram(), false));
}
stmtIt.next();
if(assignment.getlValue() instanceof VariableRef) {
assignment.setrValue2(new StructUnwoundPlaceholder(structType));
assignment.setrValue2(new StructUnwoundPlaceholder(structType, membersUnwound));
} else {
stmtIt.remove();
}

View File

@ -1,6 +1,7 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.CompileError;
import dk.camelot64.kickc.model.ConstantNotLiteral;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
@ -80,11 +81,18 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
}
if(!SymbolTypeConversion.assignmentTypeMatch(variableType, valueType)) {
throw new CompileError(
"Constant variable has a non-matching type \n variable: " + variable.toString(getProgram()) +
"\n value: (" + valueType.toString() + ") " + constVal.calculateLiteral(getScope()) +
"\n value definition: " + constVal.toString(getProgram())
);
ConstantLiteral constantLiteral = null;
try {
constantLiteral = constVal.calculateLiteral(getScope());
} 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(
@ -197,7 +205,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
// Volatile variables cannot be constant
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));
}
}
@ -324,7 +332,7 @@ public class Pass2ConstantIdentification extends Pass2SsaOptimization {
// If the symbol is part of an unwound struct - look at the struct itself
Pass1UnwindStructValues.StructUnwinding structUnwinding = program.getStructUnwinding();
VariableRef structVarRef = structUnwinding.getContainingStructVariable(symbolRef);
if(structVarRef!=null) {
if(structVarRef != null) {
return isAddressOfUsed(structVarRef, program);
}

View File

@ -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;
}
}

View File

@ -6,8 +6,8 @@ import dk.camelot64.kickc.model.VariableReferenceInfos;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.ConstantVar;
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.types.SymbolTypeStruct;
import dk.camelot64.kickc.model.values.LValue;
import dk.camelot64.kickc.model.values.VariableRef;
@ -41,9 +41,27 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
LValue lValue = assignment.getlValue();
if(lValue instanceof VariableRef && referenceInfos.isUnused((VariableRef) lValue) && !Pass2ConstantIdentification.isAddressOfUsed((VariableRef) lValue, getProgram())) {
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)) {
// Do not eliminate reutn variables in pass 1
// Do not eliminate return variables in pass 1
continue;
}
if(pass2 || getLog().isVerbosePass1CreateSsa()) {
@ -124,11 +142,12 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
/**
* Determines if a variable is the return value for a procedure
*
* @param variable The variable
* @return true if this is the return variable for a function
*/
private boolean isReturnValue(Variable variable) {
if(variable==null) return false;
if(variable == null) return false;
return variable.getScope() instanceof Procedure && variable.getLocalName().equals("return");
}

View File

@ -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;
}
}

View File

@ -75,15 +75,13 @@ public class TestPrograms {
@Test
public void testStructPtr12Ref() throws IOException, URISyntaxException {
compileAndCompare("struct-ptr-12-ref", log());
compileAndCompare("struct-ptr-12-ref");
}
/*
@Test
public void testStructPtr12() throws IOException, URISyntaxException {
compileAndCompare("struct-ptr-12", log().verboseCreateSsa().verboseParse().verboseStatementSequence());
compileAndCompare("struct-ptr-12");
}
*/
@Test
public void testStructPtr11() throws IOException, URISyntaxException {

View File

@ -5,7 +5,7 @@ struct Point {
};
void main() {
struct Point p = { 2, 3 };
volatile struct Point p = { 2, 3 };
struct Point *q = &p;
const byte* SCREEN = 0x0400;
SCREEN[0] = q->x;

View File

@ -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_y#1 ← (byte) getCharToProcess::closest_y#2
(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
getCharToProcess::@11: scope:[getCharToProcess] from getCharToProcess::@10
(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_y#2 ← (byte) getCharToProcess::return_y#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
to:@return
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)
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 (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 (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::$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

View File

@ -61,14 +61,14 @@ point: scope:[point] from main
(byte) point::p_y#0 ← (number) 3
(byte) point::return_x#1 ← (byte) point::p_x#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
point::@return: scope:[point] from point
(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#2 ← (byte) point::return_x#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
to:@return
@2: scope:[] from @begin
@ -162,8 +162,8 @@ Constant (const byte) main::q_y#1 = point::return_y#0
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
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#1 and assignment [5] (struct Point) point::return#1 ← 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 {(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_y#0
Successful SSA optimization PassNEliminateUnusedVars

View 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
}

View 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

View 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
}

View 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 ]