1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2025-04-06 15:41:05 +00:00

Working on stack-parameter-passing. Split calls into prepare/execute/finalize. Still some clobber problems and results are not pushed.

This commit is contained in:
jespergravgaard 2019-09-22 22:20:45 +02:00
parent bcddd821ff
commit dc08f86302
61 changed files with 2383 additions and 345 deletions
src
main
test

@ -0,0 +1,4 @@
pla
sta {z1}
pla
sta {z1}+1

@ -490,10 +490,12 @@ public class Compiler {
new PassNCallingConventionStack(program).execute();
program.clearCallGraph();
program.clearStatementIndices();
program.clearStatementInfos();
program.clearVariableReferenceInfos();
program.clearLiveRangeVariables();
program.clearLiveRangeVariablesEffective();
new PassNStatementIndices(program).execute();
pass2AssertSSA();
getLog().append("\nFINAL CONTROL FLOW GRAPH");

@ -361,6 +361,8 @@ public class AsmFragmentInstanceSpecFactory {
return "_stackget"+paramStackValue.getValueType().getTypeName()+"_"+bind(paramStackValue.getStackOffset());
} else if(value instanceof ParamStackPush) {
return "_stackpush"+((ParamStackPush) value).getType().getTypeName()+"_";
} else if(value instanceof ParamStackPull) {
return "_stackpull"+((ParamStackPull) value).getType().getTypeName()+"_";
}
throw new RuntimeException("Binding of value type not supported " + value.toString(program));
}

@ -1,6 +1,6 @@
package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementCalling;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.passes.calcs.PassNCalcCallGraph;
@ -219,8 +219,8 @@ public class CallGraph {
return scopeLabel;
}
public void addCall(ProcedureRef procedureLabel, StatementCall statementCall) {
this.calls.add(new Call(procedureLabel, statementCall));
public void addCall(ProcedureRef procedureLabel, StatementCalling call) {
this.calls.add(new Call(procedureLabel, call));
}
/**
@ -286,8 +286,8 @@ public class CallGraph {
*/
private ProcedureRef procedure;
Call(ProcedureRef procedure, StatementCall statementCall) {
this.callStatementIdx = statementCall.getIndex();
Call(ProcedureRef procedure, StatementCalling call) {
this.callStatementIdx = call.getIndex();
this.procedure = procedure;
}

@ -1,10 +1,9 @@
package dk.camelot64.kickc.model;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementCalling;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Scope;
import dk.camelot64.kickc.model.symbols.Symbol;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ScopeRef;
@ -87,7 +86,7 @@ public class ControlFlowBlock implements Serializable {
ListIterator<Statement> listIterator = statements.listIterator();
while(listIterator.hasNext()) {
Statement statement = listIterator.next();
if(statement instanceof StatementCall) {
if(statement instanceof StatementCalling) {
listIterator.previous();
listIterator.add(newStatement);
return;

@ -95,9 +95,9 @@ public class ControlFlowGraph implements Serializable {
public ControlFlowBlock getAssignmentBlock(VariableRef variable) {
for(ControlFlowBlock block : getAllBlocks()) {
for(Statement statement : block.getStatements()) {
if(statement instanceof StatementAssignment) {
StatementAssignment assignment = (StatementAssignment) statement;
if(assignment.getlValue().equals(variable)) {
if(statement instanceof StatementLValue) {
StatementLValue assignment = (StatementLValue) statement;
if(variable.equals(assignment.getlValue())) {
return block;
}
} else if(statement instanceof StatementPhiBlock) {

@ -35,6 +35,12 @@ public class ControlFlowGraphBaseVisitor<T> {
return visitCall((StatementCall) statement);
} else if(statement instanceof StatementCallPointer) {
return visitCallPointer((StatementCallPointer) statement);
} else if(statement instanceof StatementCallPrepare) {
return visitCallPrepare((StatementCallPrepare) statement);
} else if(statement instanceof StatementCallExecute) {
return visitCallExecute((StatementCallExecute) statement);
} else if(statement instanceof StatementCallFinalize) {
return visitCallFinalize((StatementCallFinalize) statement);
} else if(statement instanceof StatementPhiBlock) {
return visitPhiBlock((StatementPhiBlock) statement);
} else if(statement instanceof StatementReturn) {
@ -92,6 +98,18 @@ public class ControlFlowGraphBaseVisitor<T> {
return null;
}
public T visitCallFinalize(StatementCallFinalize callFinalize) {
return null;
}
public T visitCallExecute(StatementCallExecute callExecute) {
return null;
}
public T visitCallPrepare(StatementCallPrepare callPrepare) {
return null;
}
public T visitAsm(StatementAsm asm) {
return null;
}

@ -181,6 +181,21 @@ public class ControlFlowGraphCopyVisitor extends ControlFlowGraphBaseVisitor<Obj
return new StatementCallPointer(lValue, procedure, parameters, orig.getSource(), orig.getComments());
}
@Override
public Object visitCallPrepare(StatementCallPrepare orig) {
return new StatementCallPrepare(orig.getProcedure(), orig.getParameters(), orig.getSource(), orig.getComments());
}
@Override
public Object visitCallExecute(StatementCallExecute orig) {
return new StatementCallExecute(orig.getProcedure(), orig.getSource(), orig.getComments());
}
@Override
public Object visitCallFinalize(StatementCallFinalize orig) {
return new StatementCallFinalize(orig.getlValue(), orig.getProcedure(), orig.getSource(), orig.getComments());
}
@Override
public StatementProcedureBegin visitProcedureBegin(StatementProcedureBegin orig) {
return new StatementProcedureBegin(orig.getProcedure(), orig.getSource(), orig.getComments());

@ -348,6 +348,10 @@ public class Program {
this.liveRangeVariables = liveRangeVariables;
}
public boolean hasLiveRangeVariables() {
return this.liveRangeVariables != null;
}
public void clearLiveRangeVariables() {
this.liveRangeVariables = null;
}

@ -75,6 +75,16 @@ public class ProgramExpressionIterator {
handler.execute(new ProgramExpressionBinary.ProgramExpressionBinaryCallParameter(paramDefs.get(i).getRef(), new ProgramValue.CallParameter(call, i)), stmt, stmtIt, block);
}
}
} else if(stmt instanceof StatementCallPrepare) {
StatementCallPrepare call = (StatementCallPrepare) stmt;
List<RValue> paramValues = call.getParameters();
Procedure procedure = program.getScope().getProcedure(call.getProcedure());
List<Variable> paramDefs = procedure.getParameters();
if(paramValues != null && paramDefs.size() == paramValues.size()) {
for(int i = 0; i < paramDefs.size(); i++) {
handler.execute(new ProgramExpressionBinary.ProgramExpressionBinaryCallParameter(paramDefs.get(i).getRef(), new ProgramValue.CallPrepareParameter(call, i)), stmt, stmtIt, block);
}
}
}
// Iterate all statement values
ProgramValueIterator.execute(stmt, programValueHandler, stmtIt, block);

@ -55,6 +55,26 @@ public interface ProgramValue {
}
}
class CallPrepareParameter implements ProgramValue {
private final StatementCallPrepare call;
private final int i;
CallPrepareParameter(StatementCallPrepare call, int i) {
this.call = call;
this.i = i;
}
@Override
public Value get() {
return call.getParameters().get(i);
}
@Override
public void set(Value value) {
call.getParameters().set(i, (RValue) value);
}
}
class CallParameter implements ProgramValue {
private final StatementCall call;
private final int i;

@ -106,6 +106,16 @@ public class ProgramValueIterator {
}
}
execute(new ProgramValue.ProgramValueLValue((StatementLValue) statement), handler, statement, statementsIt, block);
} else if(statement instanceof StatementCallPrepare) {
StatementCallPrepare call = (StatementCallPrepare) statement;
if(call.getParameters() != null) {
int size = call.getParameters().size();
for(int i = 0; i < size; i++) {
execute(new ProgramValue.CallPrepareParameter(call, i), handler, statement, statementsIt, block);
}
}
} else if(statement instanceof StatementCallFinalize) {
execute(new ProgramValue.ProgramValueLValue((StatementLValue) statement), handler, statement, statementsIt, block);
} else if(statement instanceof StatementCallPointer) {
StatementCallPointer call = (StatementCallPointer) statement;
execute(new ProgramValue.CallPointerProcedure((StatementCallPointer) statement), handler, statement, statementsIt, block);

@ -75,7 +75,7 @@ public abstract class StatementBase implements Statement {
}
public String aliveString(Program program) {
if(program == null || program.getLiveRangeVariables() == null) {
if(program == null || !program.hasLiveRangeVariables()) {
return "";
}
LiveRangeVariables liveRanges = program.getLiveRangeVariables();

@ -7,13 +7,14 @@ import dk.camelot64.kickc.model.values.LValue;
import dk.camelot64.kickc.model.values.RValue;
import java.util.List;
import java.util.Objects;
/**
* Call procedure in SSA form.
* <br>
* <i> X<sub>i</sub> := call label param1 param2 param3 ... </i>
*/
public class StatementCall extends StatementBase implements StatementLValue {
public class StatementCall extends StatementBase implements StatementLValue, StatementCalling {
/** The variable being assigned a value by the call. Can be null. */
private LValue lValue;
@ -101,22 +102,15 @@ public class StatementCall extends StatementBase implements StatementLValue {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
if(!super.equals(o)) return false;
StatementCall that = (StatementCall) o;
if(lValue != null ? !lValue.equals(that.lValue) : that.lValue != null) return false;
if(!procedureName.equals(that.procedureName)) return false;
if(procedure != null ? !procedure.equals(that.procedure) : that.procedure != null) return false;
return parameters != null ? parameters.equals(that.parameters) : that.parameters == null;
return Objects.equals(lValue, that.lValue) &&
Objects.equals(procedureName, that.procedureName) &&
Objects.equals(procedure, that.procedure) &&
Objects.equals(parameters, that.parameters);
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (lValue != null ? lValue.hashCode() : 0);
result = 31 * result + procedureName.hashCode();
result = 31 * result + (procedure != null ? procedure.hashCode() : 0);
result = 31 * result + (parameters != null ? parameters.hashCode() : 0);
return result;
return Objects.hash(super.hashCode(), lValue, procedureName, procedure, parameters);
}
}

@ -0,0 +1,62 @@
package dk.camelot64.kickc.model.statements;
import dk.camelot64.kickc.model.Comment;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.values.ProcedureRef;
import java.util.List;
import java.util.Objects;
/**
* Call procedure execution in SSA form.
* <br>
* lval = call func(params) are converted to callprepare func(params) / callexecute func() / lval=callfinalize func().
* <br>
* callexecute procedure ... </i>
*/
public class StatementCallExecute extends StatementBase implements StatementCalling {
/** The procedure called. */
private ProcedureRef procedure;
public StatementCallExecute(ProcedureRef procedure, StatementSource source, List<Comment> comments) {
super(null, source, comments);
this.procedure = procedure;
}
public ProcedureRef getProcedure() {
return procedure;
}
public void setProcedure(ProcedureRef procedure) {
this.procedure = procedure;
}
@Override
public String toString(Program program, boolean aliveInfo) {
StringBuilder res = new StringBuilder();
res.append(super.idxString());
res.append("callexecute ");
if(procedure != null) {
res.append(procedure.getFullName() + " ");
}
if(aliveInfo) {
res.append(super.aliveString(program));
}
return res.toString();
}
@Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
if(!super.equals(o)) return false;
StatementCallExecute that = (StatementCallExecute) o;
return Objects.equals(procedure, that.procedure);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), procedure);
}
}

@ -0,0 +1,79 @@
package dk.camelot64.kickc.model.statements;
import dk.camelot64.kickc.model.Comment;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.values.LValue;
import dk.camelot64.kickc.model.values.ProcedureRef;
import java.util.List;
import java.util.Objects;
/**
* Call procedure finalization in SSA form.
* <br>
* lval = call func(params) are converted to callprepare func(params) / callexecute func() / lval=callfinalize func().
* <br>
* lvalue=callfinalize procedure </i>
*/
public class StatementCallFinalize extends StatementBase implements StatementLValue {
/** The variable being assigned a value by the call. Can be null. */
private LValue lValue;
/** The procedure called. */
private ProcedureRef procedure;
public StatementCallFinalize(LValue lValue, ProcedureRef procedure, StatementSource source, List<Comment> comments) {
super(null, source, comments);
this.lValue = lValue;
this.procedure = procedure;
}
public LValue getlValue() {
return lValue;
}
public void setlValue(LValue lValue) {
this.lValue = lValue;
}
public ProcedureRef getProcedure() {
return procedure;
}
public void setProcedure(ProcedureRef procedure) {
this.procedure = procedure;
}
@Override
public String toString(Program program, boolean aliveInfo) {
StringBuilder res = new StringBuilder();
res.append(super.idxString());
if(lValue != null) {
res.append(lValue.toString(program));
res.append("");
}
res.append("callfinalize ");
if(procedure != null) {
res.append(procedure.getFullName()).append(" ");
}
if(aliveInfo) {
res.append(super.aliveString(program));
}
return res.toString();
}
@Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
if(!super.equals(o)) return false;
StatementCallFinalize that = (StatementCallFinalize) o;
return Objects.equals(lValue, that.lValue) &&
Objects.equals(procedure, that.procedure);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), lValue, procedure);
}
}

@ -0,0 +1,92 @@
package dk.camelot64.kickc.model.statements;
import dk.camelot64.kickc.model.Comment;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.model.values.RValue;
import java.util.List;
import java.util.Objects;
/**
* Call procedure prepararion in SSA form.
* <br>
* lval = call func(params) are converted to callprepare func(params) / callexecute func() / lval=callfinalize func().
* <br>
* callprepare procedure param1 param2 param3 ... </i>
*/
public class StatementCallPrepare extends StatementBase {
/** The procedure called. */
private ProcedureRef procedure;
/** The parameter values passed to the procedure. */
private List<RValue> parameters;
public StatementCallPrepare(ProcedureRef procedure, List<RValue> parameters, StatementSource source, List<Comment> comments) {
super(null, source, comments);
this.procedure = procedure;
this.parameters = parameters;
}
public ProcedureRef getProcedure() {
return procedure;
}
public void setProcedure(ProcedureRef procedure) {
this.procedure = procedure;
}
public List<RValue> getParameters() {
return parameters;
}
public void setParameters(List<RValue> parameters) {
this.parameters = parameters;
}
public int getNumParameters() {
return parameters.size();
}
public RValue getParameter(int idx) {
return parameters.get(idx);
}
public void clearParameters() {
this.parameters = null;
}
@Override
public String toString(Program program, boolean aliveInfo) {
StringBuilder res = new StringBuilder();
res.append(super.idxString());
res.append("callprepare ");
if(procedure != null) {
res.append(procedure.getFullName() + " ");
}
if(parameters != null) {
for(RValue parameter : parameters) {
res.append(parameter.toString(program) + " ");
}
}
if(aliveInfo) {
res.append(super.aliveString(program));
}
return res.toString();
}
@Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
if(!super.equals(o)) return false;
StatementCallPrepare that = (StatementCallPrepare) o;
return Objects.equals(procedure, that.procedure) &&
Objects.equals(parameters, that.parameters);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), procedure, parameters);
}
}

@ -0,0 +1,17 @@
package dk.camelot64.kickc.model.statements;
import dk.camelot64.kickc.model.values.ProcedureRef;
/**
* Statement performing a call to a procedure
*/
public interface StatementCalling extends Statement {
/**
* Get the procedure being called
* @return The procedure reference
*/
ProcedureRef getProcedure();
}

@ -0,0 +1,24 @@
package dk.camelot64.kickc.model.values;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.types.SymbolType;
/** A value puslled from the stack. */
public class ParamStackPull implements RValue {
/** The type of value being pushed. */
private SymbolType type;
public ParamStackPull(SymbolType type) {
this.type = type;
}
public SymbolType getType() {
return type;
}
@Override
public String toString(Program program) {
return "paramstackpull(" + type.getTypeName()+ ")";
}
}

@ -4,7 +4,7 @@ import dk.camelot64.kickc.model.CompileError;
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.StatementCall;
import dk.camelot64.kickc.model.statements.StatementCalling;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.values.ProcedureRef;
@ -20,8 +20,8 @@ public class Pass1AssertInterrupts extends Pass1Base {
public boolean step() {
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
for(Statement statement : block.getStatements()) {
if(statement instanceof StatementCall) {
ProcedureRef procedureRef = ((StatementCall) statement).getProcedure();
if(statement instanceof StatementCalling) {
ProcedureRef procedureRef = ((StatementCalling) statement).getProcedure();
Procedure procedure = getScope().getProcedure(procedureRef);
if(procedure.getInterruptType()!=null) {
throw new CompileError("Error! Interrupts cannot be called.", statement.getSource());

@ -4,19 +4,14 @@ import dk.camelot64.kickc.model.CompileError;
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.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Variable;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeConversion;
import dk.camelot64.kickc.model.types.SymbolTypeInference;
import dk.camelot64.kickc.model.values.*;
import dk.camelot64.kickc.model.values.RValue;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
/**

@ -6,10 +6,7 @@ import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.VariableReferenceInfos;
import dk.camelot64.kickc.model.iterator.ProgramValue;
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.SymbolRef;
@ -83,7 +80,16 @@ public class Pass1AssertUsedVars extends Pass1Base {
defined.add(procedure.getVariable(paramName).getRef());
}
ControlFlowBlock procedureStart = getProgram().getGraph().getBlock(call.getProcedure().getLabelRef());
assertUsedVars(procedureStart, block.getLabel(), referenceInfos, defined, visited);
} else if(statement instanceof StatementCallPrepare) {
StatementCallPrepare call = (StatementCallPrepare) statement;
Procedure procedure = getProgram().getScope().getProcedure(call.getProcedure());
for(String paramName : procedure.getParameterNames()) {
defined.add(procedure.getVariable(paramName).getRef());
}
} else if(statement instanceof StatementCallExecute) {
StatementCallExecute call = (StatementCallExecute) statement;
ControlFlowBlock procedureStart = getProgram().getGraph().getBlock(call.getProcedure().getLabelRef());
assertUsedVars(procedureStart, block.getLabel(), referenceInfos, defined, visited);
} else if(statement instanceof StatementConditionalJump) {
StatementConditionalJump cond = (StatementConditionalJump) statement;

@ -3,7 +3,7 @@ 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.StatementCall;
import dk.camelot64.kickc.model.statements.StatementCalling;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.ProcedureRef;
@ -24,8 +24,8 @@ public class Pass1EliminateUncalledProcedures extends Pass1Base {
Set<ProcedureRef> calledProcedures = new LinkedHashSet<>();
for(ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) {
for(Statement statement : block.getStatements()) {
if(statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
if(statement instanceof StatementCalling) {
StatementCalling call = (StatementCalling) statement;
ProcedureRef procedureRef = call.getProcedure();
calledProcedures.add(procedureRef);
}

@ -2,6 +2,7 @@ package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.iterator.ProgramValueIterator;
import dk.camelot64.kickc.model.statements.StatementCallPrepare;
import dk.camelot64.kickc.model.values.ConstantString;
import dk.camelot64.kickc.model.values.RValue;
import dk.camelot64.kickc.model.statements.StatementCall;
@ -38,6 +39,18 @@ public class Pass1ExtractInlineStrings extends Pass1Base {
break;
}
}
} else if(currentStmt instanceof StatementCallPrepare) {
StatementCallPrepare call = (StatementCallPrepare) currentStmt;
List<RValue> parameters = call.getParameters();
for(int i = 0; i < parameters.size(); i++) {
RValue parameter = parameters.get(i);
if(parameter.equals(programValue.get())) {
// The programValue value is the parameter - use the parameter name as name hint
Procedure procedure = Pass1ExtractInlineStrings.this.getProgram().getScope().getProcedure(call.getProcedure());
nameHint = procedure.getParameterNames().get(i);
break;
}
}
}
Scope blockScope = Pass1ExtractInlineStrings.this.getProgram().getScope().getScope(currentBlock.getScope());
Value value = programValue.get();

@ -1,14 +1,16 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.ControlFlowBlock;
import dk.camelot64.kickc.model.ProcedureModifiedVars;
import dk.camelot64.kickc.model.Program;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementCalling;
import dk.camelot64.kickc.model.statements.StatementLValue;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.LValue;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.model.values.ScopeRef;
import dk.camelot64.kickc.model.values.VariableRef;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementLValue;
import dk.camelot64.kickc.model.symbols.Procedure;
import java.util.*;
@ -51,8 +53,8 @@ public class Pass1ModifiedVarsAnalysis extends Pass1Base {
modified.add(var);
}
}
if(statement instanceof StatementCall) {
ProcedureRef called = ((StatementCall) statement).getProcedure();
if(statement instanceof StatementCalling) {
ProcedureRef called = ((StatementCalling) statement).getProcedure();
Procedure calledProc = getScope().getProcedure(called);
modified.addAll(getModifiedVars(calledProc));
}

@ -74,7 +74,6 @@ public class Pass1ProcedureCallParameters extends ControlFlowGraphCopyVisitor {
LValue procReturnVarRef = null;
if(procReturnVar != null) {
procReturnVarRef = procReturnVar.getRef();
// Special handing of struct value returns
if(procReturnVar.getType() instanceof SymbolTypeStruct) {
StructUnwinding.VariableUnwinding returnVarUnwinding = program.getStructUnwinding().getVariableUnwinding((VariableRef) procReturnVarRef);

@ -1,10 +1,7 @@
package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
import dk.camelot64.kickc.model.statements.StatementJump;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.model.values.SymbolRef;
@ -86,6 +83,14 @@ public class Pass2AssertBlocks extends Pass2SsaAssertion {
return super.visitCall(call);
}
@Override
public Void visitCallExecute(StatementCallExecute callExecute) {
ProcedureRef procedure = callExecute.getProcedure();
LabelRef procLabelRef = procedure.getLabelRef();
assertBlock(procLabelRef);
return super.visitCallExecute(callExecute);
}
@Override
public Void visitPhiBlock(StatementPhiBlock phi) {
for(StatementPhiBlock.PhiVariable phiVariable : phi.getPhiVariables()) {

@ -6,7 +6,6 @@ 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.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.symbols.Label;
import dk.camelot64.kickc.model.symbols.Procedure;

@ -5,9 +5,11 @@ package dk.camelot64.kickc.passes;
* This ensures that the live range propagation can propagate from method out to caller properly.
*/
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.Comment;
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.StatementCall;
import dk.camelot64.kickc.model.statements.StatementCalling;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import java.util.List;
@ -29,7 +31,7 @@ public class Pass3AddNopBeforeCallOns extends Pass2Base {
getLog().append("Adding NOP phi() at start of " + block.getLabel());
} else {
Statement firstStmt = statements.get(0);
if(firstStmt instanceof StatementCall) {
if(firstStmt instanceof StatementCalling) {
statements.add(0, new StatementPhiBlock(Comment.NO_COMMENTS));
getLog().append("Adding NOP phi() at start of " + block.getLabel());
}

@ -734,7 +734,6 @@ public class Pass4CodeGeneration {
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
} else if(statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
Procedure procedure = getScope().getProcedure(call.getProcedure());
if(Procedure.CallingConvension.PHI_CALL.equals(procedure.getCallingConvension())) {
// Generate PHI transition
@ -748,7 +747,12 @@ public class Pass4CodeGeneration {
genBlockPhiTransition(asm, block, callSuccessor, block.getScope());
}
}
} else if(Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) {
}
asm.addInstruction("jsr", AsmAddressingMode.ABS, call.getProcedure().getFullName(), false);
} else if(statement instanceof StatementCallPrepare) {
StatementCallPrepare call = (StatementCallPrepare) statement;
Procedure procedure = getScope().getProcedure(call.getProcedure());
if(Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) {
// Push parameters to the stack
for(RValue parameter : call.getParameters()) {
SymbolType parameterType = SymbolTypeInference.inferType(program.getScope(), parameter);
@ -758,20 +762,43 @@ public class Pass4CodeGeneration {
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
}
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
asm.getCurrentChunk().setFragment("jsr");
}
asm.addInstruction("jsr", AsmAddressingMode.ABS, call.getProcedure().getFullName(), false);
// Clean up the stack
} else if(statement instanceof StatementCallExecute) {
StatementCallExecute call = (StatementCallExecute) statement;
Procedure procedure = getScope().getProcedure(call.getProcedure());
if(Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) {
asm.getCurrentChunk().setFragment("jsr");
asm.addInstruction("jsr", AsmAddressingMode.ABS, call.getProcedure().getFullName(), false);
}
} else if(statement instanceof StatementCallFinalize) {
StatementCallFinalize call = (StatementCallFinalize) statement;
Procedure procedure = getScope().getProcedure(call.getProcedure());
if(Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) {
// Find parameter/return stack size
int parameterBytes = 0;
for(RValue parameter : call.getParameters()) {
SymbolType parameterType = SymbolTypeInference.inferType(program.getScope(), parameter);
parameterBytes += parameterType.getSizeBytes();
for(Variable parameter : procedure.getParameters()) {
parameterBytes += parameter.getType().getSizeBytes();
}
String pullSignature = "_stackpullbyte_" + Integer.toString(parameterBytes);
int stackSizeBytes = parameterBytes;
if(call.getlValue() != null) {
SymbolType returnType = procedure.getReturnType();
stackSizeBytes -= returnType.getSizeBytes();
}
// Clean up the stack
String pullSignature = "_stackpullbyte_" + Integer.toString(stackSizeBytes);
AsmFragmentInstanceSpec pullFragmentInstanceSpec = new AsmFragmentInstanceSpec(program, pullSignature, new LinkedHashMap<>(), block.getScope());
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
generateAsm(asm, pullFragmentInstanceSpec);
// Pull result from the stack
if(call.getlValue() != null) {
SymbolType returnType = procedure.getReturnType();
AsmFragmentInstanceSpecFactory asmFragmentInstanceSpecFactory = new AsmFragmentInstanceSpecFactory(call.getlValue(), new ParamStackPull(returnType), program, block.getScope());
asm.startChunk(block.getScope(), statement.getIndex(), statement.toString(program, verboseAliveInfo));
ensureEncoding(asm, asmFragmentInstanceSpecFactory);
generateAsm(asm, asmFragmentInstanceSpecFactory.getAsmFragmentInstanceSpec());
}
}
} else if(statement instanceof StatementReturn) {
Procedure.InterruptType interruptType = null;

@ -2,6 +2,7 @@ package dk.camelot64.kickc.passes;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementCallFinalize;
import dk.camelot64.kickc.model.symbols.VariableVersion;
import dk.camelot64.kickc.model.values.VariableRef;
import dk.camelot64.kickc.model.statements.StatementAssignment;
@ -94,6 +95,16 @@ public class Pass4LiveRangeEquivalenceClassesFinalize extends Pass2Base {
return null;
}
@Override
public Void visitCallFinalize(StatementCallFinalize callFinalize) {
if(callFinalize.getlValue() instanceof VariableRef) {
VariableRef lValVar = (VariableRef) callFinalize.getlValue();
List<VariableRef> preferences = new ArrayList<>();
addToEquivalenceClassSet(lValVar, preferences);
}
return null;
}
private void addToEquivalenceClassSet(VariableRef lValVar, List<VariableRef> preferences) {
LiveRangeVariables liveRangeVariables = getProgram().getLiveRangeVariables();
LiveRangeEquivalenceClass lValEquivalenceClass =

@ -6,10 +6,7 @@ import dk.camelot64.kickc.asm.AsmInstruction;
import dk.camelot64.kickc.asm.AsmLine;
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.StatementCall;
import dk.camelot64.kickc.model.statements.StatementKickAsm;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.statements.*;
import dk.camelot64.kickc.model.values.LabelRef;
import dk.camelot64.kickc.model.values.ProcedureRef;
import dk.camelot64.kickc.model.values.SymbolRef;
@ -71,8 +68,8 @@ public class Pass5SkipBegin extends Pass5AsmOptimization {
if(((StatementPhiBlock) statement).getPhiVariables().size() > 0) {
return false;
}
} else if(statement instanceof StatementCall) {
ProcedureRef procedure = ((StatementCall) statement).getProcedure();
} else if(statement instanceof StatementCalling) {
ProcedureRef procedure = ((StatementCalling) statement).getProcedure();
if(!SymbolRef.MAIN_PROC_NAME.equals(procedure.getFullName())) {
return false;
}

@ -1,13 +1,16 @@
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.statements.*;
import dk.camelot64.kickc.model.symbols.*;
import dk.camelot64.kickc.model.types.SymbolType;
import dk.camelot64.kickc.model.types.SymbolTypeInference;
import dk.camelot64.kickc.model.values.*;
import java.util.HashMap;
import java.util.ListIterator;
import java.util.Map;
/** Handle calling convention {@link Procedure.CallingConvension#STACK_CALL} by converting the making control flow graph and symbols calling convention specific. */
@ -32,6 +35,26 @@ public class PassNCallingConventionStack extends Pass2SsaOptimization {
}
}
// Transform STACK_CALL calls to call-prepare, call-execute, call-finalize
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
ListIterator<Statement> stmtIt = block.getStatements().listIterator();
while(stmtIt.hasNext()) {
Statement statement = stmtIt.next();
if(statement instanceof StatementCall) {
StatementCall call = (StatementCall) statement;
ProcedureRef procedureRef = call.getProcedure();
Procedure procedure = getScope().getProcedure(procedureRef);
if(Procedure.CallingConvension.STACK_CALL.equals(procedure.getCallingConvension())) {
stmtIt.remove();
stmtIt.add(new StatementCallPrepare(procedureRef, call.getParameters(), call.getSource(), call.getComments()));
stmtIt.add(new StatementCallExecute(procedureRef, call.getSource(), call.getComments()));
stmtIt.add(new StatementCallFinalize(call.getlValue(), procedureRef, call.getSource(), call.getComments()));
getLog().append("Calling convention " + Procedure.CallingConvension.STACK_CALL + " adding prepare/execute/finalize for "+call.toString(getProgram(), false) );
}
}
}
}
if(offsetConstants.size() > 0) {
// Add global STACK_BASE constant
long STACK_BASE = 0x103L;
@ -82,7 +105,6 @@ public class PassNCallingConventionStack extends Pass2SsaOptimization {
* @return The name of the constant
*/
private static String getParameterOffsetConstantName(String parameterName) {
// TODO: Maybe use asmName?
return "OFFSET_STACK_" + parameterName.toUpperCase();
}

@ -86,6 +86,22 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
modified = true;
}
}
} else if(statement instanceof StatementCallFinalize) {
StatementCallFinalize call = (StatementCallFinalize) statement;
LValue lValue = call.getlValue();
if(lValue instanceof VariableRef && referenceInfos.isUnused((VariableRef) lValue)) {
Variable variable = getScope().getVariable((VariableRef) lValue);
if(!variable.isVolatile()) {
if(pass2 || getLog().isVerbosePass1CreateSsa()) {
getLog().append("Eliminating unused variable - keeping the call " + lValue.toString(getProgram()));
}
if(variable != null) {
variable.getScope().remove(variable);
}
call.setlValue(null);
modified = true;
}
}
} else if(statement instanceof StatementCallPointer) {
StatementCallPointer call = (StatementCallPointer) statement;
LValue lValue = call.getlValue();

@ -47,6 +47,8 @@ public class PassNTypeInference extends Pass2SsaOptimization {
updateInferedTypeAssignmentLValue(program, (StatementAssignment) statementLValue);
} else if(statementLValue instanceof StatementCall) {
updateInferedTypeCallLValue(program, (StatementCall) statementLValue);
} else if(statementLValue instanceof StatementCallFinalize) {
updateInferedTypeCallFinalizeLValue(program, (StatementCallFinalize) statementLValue);
} else if(statementLValue instanceof StatementCallPointer) {
updateInferedTypeCallPointerLValue(program, (StatementCallPointer) statementLValue);
} else {
@ -68,6 +70,19 @@ public class PassNTypeInference extends Pass2SsaOptimization {
}
}
private static void updateInferedTypeCallFinalizeLValue(Program program, StatementCallFinalize call) {
ProgramScope programScope = program.getScope();
LValue lValue = call.getlValue();
if(lValue instanceof VariableRef) {
Variable symbol = programScope.getVariable((VariableRef) lValue);
if(SymbolType.VAR.equals(symbol.getType()) || SymbolType.NUMBER.equals(symbol.getType())|| SymbolType.UNUMBER.equals(symbol.getType())|| SymbolType.SNUMBER.equals(symbol.getType())) {
Procedure procedure = programScope.getProcedure(call.getProcedure());
SymbolType type = procedure.getReturnType();
setInferedType(program, call, symbol, type);
}
}
}
private static void updateInferedTypeCallPointerLValue(Program program, StatementCallPointer call) {
ProgramScope programScope = program.getScope();
LValue lValue = call.getlValue();

@ -4,7 +4,7 @@ import dk.camelot64.kickc.model.CallGraph;
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.StatementCall;
import dk.camelot64.kickc.model.statements.StatementCalling;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.symbols.Scope;
import dk.camelot64.kickc.model.symbols.Symbol;
@ -23,10 +23,10 @@ public class PassNCalcCallGraph extends PassNCalcBase<CallGraph> {
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
ScopeRef scopeRef = getScopeRef(block, getProgram());
for(Statement statement : block.getStatements()) {
if(statement instanceof StatementCall) {
ProcedureRef procedure = ((StatementCall) statement).getProcedure();
if(statement instanceof StatementCalling) {
ProcedureRef procedure = ((StatementCalling) statement).getProcedure();
CallGraph.CallBlock callBlock = callGraph.getOrCreateCallBlock(scopeRef);
callBlock.addCall(procedure, (StatementCall) statement);
callBlock.addCall(procedure, (StatementCalling) statement);
}
}
}

@ -2,7 +2,7 @@ package dk.camelot64.kickc.passes.calcs;
import dk.camelot64.kickc.model.*;
import dk.camelot64.kickc.model.statements.Statement;
import dk.camelot64.kickc.model.statements.StatementCall;
import dk.camelot64.kickc.model.statements.StatementCalling;
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
import dk.camelot64.kickc.model.symbols.Procedure;
import dk.camelot64.kickc.model.values.LabelRef;
@ -72,7 +72,7 @@ public class PassNCalcLiveRangeVariables extends PassNCalcBase<LiveRangeVariable
* <p>
* Special consideration is giving when handling statements at the end/start of blocks, as multiple blocks may jump to the same block resulting in multiple stmt/nextstmt pairs.
* Calls to methods are also given special consideration.
* Variables used inside the method must be propagated bak to all callers while variables not used inside the method must skip the method entirely back to the statement before the call.
* Variables used inside the method must be propagated back to all callers while variables not used inside the method must skip the method entirely back to the statement before the call.
*
* @param liveRanges The live ranges to propagate.
* @return true if any live ranges was modified. false if no modification was performed (and the propagation is complete)
@ -82,15 +82,15 @@ public class PassNCalcLiveRangeVariables extends PassNCalcBase<LiveRangeVariable
boolean modified = false;
LiveRangeVariables.LiveRangeVariablesByStatement liveRangeVariablesByStatement = liveRanges.getLiveRangeVariablesByStatement();
for(ControlFlowBlock block : getProgram().getGraph().getAllBlocks()) {
for(Statement stmt : block.getStatements()) {
List<VariableRef> aliveNextStmt = liveRangeVariablesByStatement.getAlive(stmt.getIndex());
Collection<VariableRef> definedNextStmt = referenceInfo.getDefinedVars(stmt);
for(Statement nextStmt : block.getStatements()) {
List<VariableRef> aliveNextStmt = liveRangeVariablesByStatement.getAlive(nextStmt.getIndex());
Collection<VariableRef> definedNextStmt = referenceInfo.getDefinedVars(nextStmt);
initLiveRange(liveRanges, definedNextStmt);
Collection<PreviousStatement> previousStmts = getPreviousStatements(stmt);
Collection<PreviousStatement> previousStmts = getPreviousStatements(nextStmt);
for(PreviousStatement previousStmt : previousStmts) {
if(PreviousStatement.Type.NORMAL.equals(previousStmt.getType())) {
// Add all used variables to the previous statement (taking into account phi from blocks)
modified |= initUsedVars(liveRanges, stmt, previousStmt);
modified |= initUsedVars(liveRanges, nextStmt, previousStmt);
// Add all vars alive in the next statement
for(VariableRef aliveVar : aliveNextStmt) {
if(!definedNextStmt.contains(aliveVar)) {
@ -103,7 +103,7 @@ public class PassNCalcLiveRangeVariables extends PassNCalcBase<LiveRangeVariable
}
} else if(PreviousStatement.Type.LAST_IN_METHOD.equals(previousStmt.getType())) {
// Add all vars that are referenced in the method
StatementCall call = (StatementCall) stmt;
StatementCalling call = (StatementCalling) nextStmt;
ProcedureRef procedure = call.getProcedure();
Collection<VariableRef> procUsed = procedureReferencedVars.get(procedure);
// The call statement has no used or defined by itself so only work with the alive vars
@ -119,13 +119,13 @@ public class PassNCalcLiveRangeVariables extends PassNCalcBase<LiveRangeVariable
}
} else if(PreviousStatement.Type.SKIP_METHOD.equals(previousStmt.getType())) {
// Add all vars that the method does not use
StatementCall call = (StatementCall) stmt;
StatementCalling call = (StatementCalling) nextStmt;
ProcedureRef procedure = call.getProcedure();
Collection<VariableRef> procUsed = procedureReferencedVars.get(procedure);
// The call statement has no used or defined by itself so only work with the alive vars
for(VariableRef aliveVar : aliveNextStmt) {
// Add all variables to previous that are not used inside the method
if(!procUsed.contains(aliveVar)) {
if(!procUsed.contains(aliveVar) && !definedNextStmt.contains(aliveVar)) {
boolean addSkipVar = liveRanges.addAlive(aliveVar, previousStmt.getStatementIdx());
modified |= addSkipVar;
if(addSkipVar && getLog().isVerboseLiveRanges()) {
@ -135,9 +135,9 @@ public class PassNCalcLiveRangeVariables extends PassNCalcBase<LiveRangeVariable
}
} else if(PreviousStatement.Type.BEFORE_METHOD.equals(previousStmt.getType())) {
// Add all used variables to the previous statement (taking into account phi from blocks)
modified |= initUsedVars(liveRanges, stmt, previousStmt);
modified |= initUsedVars(liveRanges, nextStmt, previousStmt);
// Add all alive variables to previous that are used inside the method
ControlFlowBlock procBlock = getProgram().getStatementInfos().getBlock(stmt);
ControlFlowBlock procBlock = getProgram().getStatementInfos().getBlock(nextStmt);
Procedure procedure = (Procedure) getProgram().getScope().getSymbol(procBlock.getLabel());
Collection<VariableRef> procUsed = procedureReferencedVars.get(procedure.getRef());
// The call statement has no used or defined by itself so only work with the alive vars
@ -153,7 +153,7 @@ public class PassNCalcLiveRangeVariables extends PassNCalcBase<LiveRangeVariable
}
} else {
// Do nothing
// getLog().append("Not propagating "+aliveVar.toString(getProgram()) +" in BEFORE_METHOD case from "+stmt.toString(getProgram(), false)+ " to "+previousStmt.getStatement().toString(getProgram(), false));
// getLog().append("Not propagating "+aliveVar.toString(getProgram()) +" in BEFORE_METHOD case from "+nextStmt.toString(getProgram(), false)+ " to "+previousStmt.getStatement().toString(getProgram(), false));
}
}
}
@ -212,7 +212,33 @@ public class PassNCalcLiveRangeVariables extends PassNCalcBase<LiveRangeVariable
/**
* Find the statement(s) executed just before the passed statement.
* There may be multiple previous statements if the current statement is the first in a block.
* There may be multiple previous statements if the current statement is the first in a block or a call.
* Special consideration is giving when handling statements at the end/start of blocks,
* as multiple blocks may jump to the same block resulting in multiple stmt/nextstmt pairs.
* Calls to methods are also given special consideration.
* The following illustrates how the different types of previous statements work.
<pre>
b1: {
[1] stmt; prev: b3[1]/NORMAL, b2[2]/NORMAL
[2] call b4; prev: b1[1]/SKIP_METHOD, b4[3]/LAST_IN_METHOD
[3] stmt; prev: b1[2]/NORMAL
goto b2;
}
b2: {
[1] stmt; prev: b1[3]/NORMAL
[2] if(x) b1; prev: b2[1]/NORMAL
goto b3;
}
b3: {
[1] stmt; prev: b2[2]/NORMAL
goto b1
}
b4: {
[1] stmt; prev: b1[1]/BEFORE_METHOD
[2] stmt; prev: b4[1]/NORMAL
[3] return; prev: b4[3]/NORMAL
}
</pre> *
*
* @param statement The statement to find previous for
* @return statement(s) executed just before the passed statement
@ -221,13 +247,13 @@ public class PassNCalcLiveRangeVariables extends PassNCalcBase<LiveRangeVariable
ArrayList<PreviousStatement> previousStatements = new ArrayList<>();
// Find the statement(s) just before the current statement (disregarding if the current statement is a call - this will be handled later)
Collection<Statement> precedingStatements = getPrecedingStatement(statement);
if(statement instanceof StatementCall) {
if(statement instanceof StatementCalling) {
// Add the statement(s) just before the call
for(Statement precedingStatement : precedingStatements) {
previousStatements.add(new PreviousStatement(precedingStatement, PreviousStatement.Type.SKIP_METHOD));
}
// Add the last statement of the called method
StatementCall call = (StatementCall) statement;
StatementCalling call = (StatementCalling) statement;
ProcedureRef procedure = call.getProcedure();
LabelRef procedureReturnBlock = procedure.getReturnBlock();
ControlFlowBlock returnBlock = getProgram().getGraph().getBlock(procedureReturnBlock);

@ -35,6 +35,16 @@ public class TestPrograms {
public TestPrograms() {
}
@Test
public void testProcedureCallingConventionStack4() throws IOException, URISyntaxException {
compileAndCompare("procedure-callingconvention-stack-4"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence());
}
@Test
public void testProcedureCallingConventionStack3() throws IOException, URISyntaxException {
compileAndCompare("procedure-callingconvention-stack-3"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence());
}
@Test
public void testProcedureCallingConventionStack2() throws IOException, URISyntaxException {
compileAndCompare("procedure-callingconvention-stack-2"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence());
@ -47,7 +57,7 @@ public class TestPrograms {
@Test
public void testProcedureCallingConventionStack0() throws IOException, URISyntaxException {
compileAndCompare("procedure-callingconvention-stack-0"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence());
compileAndCompare("procedure-callingconvention-stack-0"); //, log().verboseCreateSsa().verboseParse().verboseStatementSequence().verboseUplift().verboseLiveRanges().verboseSSAOptimize());
}
@Test

@ -0,0 +1,14 @@
// Test a procedure with calling convention stack
// Test casting of parameter types
// Currently fails because the pushed are done based on the actual value instead of the decalred parameter type
// https://gitlab.com/camelot/kickc/issues/319
const word* SCREEN = 0x0400;
void main(void) {
SCREEN[0] = plus('0', 7);
}
word __stackcall plus(word a, word b) {
return a+b;
}

@ -0,0 +1,19 @@
// Test a procedure with calling convention stack
// A slightly more complex call
const char* SCREEN = 0x0400;
char i = 0;
void main(void) {
for(char a:0..1) {
char v = a+1;
char w = plus('0', v);
SCREEN[i] = w+a;
}
}
char __stackcall plus(char a, char b) {
i++;
return a+b;
}

@ -5,6 +5,7 @@
.label SCREEN = $400
.const STACK_BASE = $103
main: {
.label _0 = 2
lda #'0'
pha
lda #7
@ -12,7 +13,8 @@ main: {
jsr plus
pla
pla
sty SCREEN
sta.z _0
sta SCREEN
rts
}
// plus(byte zeropage(2) a, byte register(A) b)

@ -11,19 +11,21 @@
(void()) main()
main: scope:[main] from @1
[4] phi()
[5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
[6] *((const byte*) SCREEN#0) ← (byte~) main::$0
[5] callprepare plus (byte) '0' (byte) 7
[6] callexecute plus
[7] (byte~) main::$0 ← callfinalize plus
[8] *((const byte*) SCREEN#0) ← (byte~) main::$0
to:main::@return
main::@return: scope:[main] from main
[7] return
[9] return
to:@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
plus: scope:[plus] from
[8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
[10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[11] return (byte) plus::return#0
[13] return (byte) plus::return#0
to:@return

@ -89,6 +89,7 @@ Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
Calling convention STACK_CALL adding prepare/execute/finalize for [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
Calling convention STACK_CALL replacing param((byte) plus::a) with paramstack(byte,plus::OFFSET_STACK_A)
Calling convention STACK_CALL replacing param((byte) plus::b) with paramstack(byte,plus::OFFSET_STACK_B)
@ -106,28 +107,30 @@ FINAL CONTROL FLOW GRAPH
(void()) main()
main: scope:[main] from @1
[4] phi()
[5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
[6] *((const byte*) SCREEN#0) ← (byte~) main::$0
[5] callprepare plus (byte) '0' (byte) 7
[6] callexecute plus
[7] (byte~) main::$0 ← callfinalize plus
[8] *((const byte*) SCREEN#0) ← (byte~) main::$0
to:main::@return
main::@return: scope:[main] from main
[7] return
[9] return
to:@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
plus: scope:[plus] from
[8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
[10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[11] return (byte) plus::return#0
[13] return (byte) plus::return#0
to:@return
VARIABLE REGISTER WEIGHTS
(byte*) SCREEN
(void()) main()
(byte~) main::$0 0.5
(byte~) main::$0 2.0
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(byte) plus::a
(byte) plus::a#0 2.0
@ -181,25 +184,29 @@ bend:
// main
main: {
.label _0 = 2
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
// [5] callprepare plus (byte) '0' (byte) 7
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
// [5] callprepare plus (byte) '0' (byte) 7
// [6] callexecute plus -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
// [7] (byte~) main::$0 ← callfinalize plus
// [7] (byte~) main::$0 ← callfinalize plus -- _stackpullbyte_1
pla
// [7] (byte~) main::$0 ← callfinalize plus -- vbuz1=_stackpullbyte_
pla
// [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1
sta.z _0
// [8] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1
lda.z _0
sta SCREEN
jmp breturn
// main::@return
breturn:
// [7] return
// [9] return
rts
}
// plus
@ -210,15 +217,15 @@ plus: {
.label a = 3
.label b = 4
.label return = 5
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
// [10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackgetbyte_vbuc1
// [11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
// [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuz1=vbuz2_plus_vbuz3
// [12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuz1=vbuz2_plus_vbuz3
lda.z a
clc
adc.z b
@ -226,40 +233,43 @@ plus: {
jmp breturn
// plus::@return
breturn:
// [11] return (byte) plus::return#0
// [13] return (byte) plus::return#0
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 ] ) always clobbers reg byte a reg byte x
Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::$0 ]
Removing always clobbered register reg byte x as potential for zp ZP_BYTE:2 [ main::$0 ]
Statement [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [5] callprepare plus (byte) '0' (byte) 7 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [7] (byte~) main::$0 ← callfinalize plus [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:6 [ plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:6 [ plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Removing always clobbered register reg byte a as potential for zp ZP_BYTE:3 [ plus::a#0 ]
Removing always clobbered register reg byte x as potential for zp ZP_BYTE:3 [ plus::a#0 ]
Statement [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:5 [ main::$0 plus::return#0 ] ) always clobbers reg byte a
Statement [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:5 [ main::$0 plus::return#0 ] ) always clobbers reg byte a
Potential registers zp ZP_BYTE:2 [ main::$0 ] : zp ZP_BYTE:2 , reg byte y ,
Statement [12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:6 [ plus::return#0 ] ) always clobbers reg byte a
Statement [5] callprepare plus (byte) '0' (byte) 7 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [7] (byte~) main::$0 ← callfinalize plus [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:6 [ plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:6 [ plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:6 [ plus::return#0 ] ) always clobbers reg byte a
Potential registers zp ZP_BYTE:2 [ main::$0 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp ZP_BYTE:3 [ plus::a#0 ] : zp ZP_BYTE:3 , reg byte y ,
Potential registers zp ZP_BYTE:4 [ plus::b#0 ] : zp ZP_BYTE:4 , reg byte a , reg byte x , reg byte y ,
Potential registers zp ZP_BYTE:5 [ plus::return#0 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [plus] 4: zp ZP_BYTE:4 [ plus::b#0 ] 2: zp ZP_BYTE:3 [ plus::a#0 ] 2: zp ZP_BYTE:5 [ plus::return#0 ]
Uplift Scope [main] 0.5: zp ZP_BYTE:2 [ main::$0 ]
Uplift Scope [main] 2: zp ZP_BYTE:2 [ main::$0 ]
Uplift Scope []
Uplifting [plus] best 81 combination reg byte a [ plus::b#0 ] zp ZP_BYTE:3 [ plus::a#0 ] reg byte a [ plus::return#0 ]
Uplifting [main] best 78 combination reg byte y [ main::$0 ]
Uplifting [] best 78 combination
Uplifting [plus] best 84 combination reg byte a [ plus::b#0 ] zp ZP_BYTE:3 [ plus::a#0 ] reg byte a [ plus::return#0 ]
Uplifting [main] best 84 combination zp ZP_BYTE:2 [ main::$0 ]
Uplifting [] best 84 combination
Attempting to uplift remaining variables inzp ZP_BYTE:2 [ main::$0 ]
Uplifting [main] best 84 combination zp ZP_BYTE:2 [ main::$0 ]
Attempting to uplift remaining variables inzp ZP_BYTE:3 [ plus::a#0 ]
Uplifting [plus] best 78 combination zp ZP_BYTE:3 [ plus::a#0 ]
Allocated (was zp ZP_BYTE:3) zp ZP_BYTE:2 [ plus::a#0 ]
Uplifting [plus] best 84 combination zp ZP_BYTE:3 [ plus::a#0 ]
Coalescing zero page register [ zp ZP_BYTE:3 [ plus::a#0 ] ] with [ zp ZP_BYTE:2 [ main::$0 ] ]
Allocated (was zp ZP_BYTE:3) zp ZP_BYTE:2 [ plus::a#0 main::$0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
@ -289,24 +299,30 @@ bend_from_b1:
bend:
// main
main: {
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
.label _0 = 2
// [5] callprepare plus (byte) '0' (byte) 7
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
// [5] callprepare plus (byte) '0' (byte) 7
// [6] callexecute plus -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
// [7] (byte~) main::$0 ← callfinalize plus
// [7] (byte~) main::$0 ← callfinalize plus -- _stackpullbyte_1
pla
// [7] (byte~) main::$0 ← callfinalize plus -- vbuz1=_stackpullbyte_
pla
// [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy
sty SCREEN
sta.z _0
// [8] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1
lda.z _0
sta SCREEN
jmp breturn
// main::@return
breturn:
// [7] return
// [9] return
rts
}
// plus
@ -315,20 +331,20 @@ plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
// [10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
// [11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
// [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
// [12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
jmp breturn
// plus::@return
breturn:
// [11] return (byte) plus::return#0
// [13] return (byte) plus::return#0
rts
}
// File Data
@ -339,6 +355,8 @@ Removing instruction jmp bend
Removing instruction jmp breturn
Removing instruction jmp breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction lda.z _0
Succesful ASM optimization Pass5UnnecesaryLoadElimination
Removing instruction b1_from_bbegin:
Removing instruction b1:
Removing instruction main_from_b1:
@ -362,7 +380,7 @@ FINAL SYMBOL TABLE
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(byte~) main::$0 reg byte y 0.5
(byte~) main::$0 $0 zp ZP_BYTE:2 2.0
(label) main::@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(label) plus::@return
@ -375,14 +393,13 @@ __stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(byte) plus::return
(byte) plus::return#0 reg byte a 2.0
reg byte y [ main::$0 ]
zp ZP_BYTE:2 [ plus::a#0 ]
zp ZP_BYTE:2 [ plus::a#0 main::$0 ]
reg byte a [ plus::b#0 ]
reg byte a [ plus::return#0 ]
FINAL ASSEMBLER
Score: 60
Score: 63
// File Comments
// Test a procedure with calling convention stack
@ -402,25 +419,30 @@ Score: 60
// @end
// main
main: {
.label _0 = 2
// plus('0', 7)
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
// [5] callprepare plus (byte) '0' (byte) 7
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
// [5] callprepare plus (byte) '0' (byte) 7
// [6] callexecute plus -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
// [7] (byte~) main::$0 ← callfinalize plus
// [7] (byte~) main::$0 ← callfinalize plus -- _stackpullbyte_1
pla
// [7] (byte~) main::$0 ← callfinalize plus -- vbuz1=_stackpullbyte_
pla
sta.z _0
// SCREEN[0] = plus('0', 7)
// [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy
sty SCREEN
// [8] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1
sta SCREEN
// main::@return
// }
// [7] return
// [9] return
rts
}
// plus
@ -429,20 +451,20 @@ plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
// [10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
// [11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
// return a+b;
// [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
// [12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
// plus::@return
// }
// [11] return (byte) plus::return#0
// [13] return (byte) plus::return#0
rts
}
// File Data

@ -5,7 +5,7 @@
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(byte~) main::$0 reg byte y 0.5
(byte~) main::$0 $0 zp ZP_BYTE:2 2.0
(label) main::@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(label) plus::@return
@ -18,7 +18,6 @@ __stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(byte) plus::return
(byte) plus::return#0 reg byte a 2.0
reg byte y [ main::$0 ]
zp ZP_BYTE:2 [ plus::a#0 ]
zp ZP_BYTE:2 [ plus::a#0 main::$0 ]
reg byte a [ plus::b#0 ]
reg byte a [ plus::return#0 ]

@ -5,6 +5,7 @@
.label SCREEN = $400
.const STACK_BASE = $103
main: {
.label _0 = 2
lda #'0'
pha
lda #7
@ -12,7 +13,8 @@ main: {
jsr plus
pla
pla
sty SCREEN
sta.z _0
sta SCREEN
rts
}
// plus(byte zeropage(2) a, byte register(A) b)

@ -11,19 +11,21 @@
(void()) main()
main: scope:[main] from @1
[4] phi()
[5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
[6] *((const byte*) SCREEN#0) ← (byte~) main::$0
[5] callprepare plus (byte) '0' (byte) 7
[6] callexecute plus
[7] (byte~) main::$0 ← callfinalize plus
[8] *((const byte*) SCREEN#0) ← (byte~) main::$0
to:main::@return
main::@return: scope:[main] from main
[7] return
[9] return
to:@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
plus: scope:[plus] from
[8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
[10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[11] return (byte) plus::return#0
[13] return (byte) plus::return#0
to:@return

@ -89,6 +89,7 @@ Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
Calling convention STACK_CALL adding prepare/execute/finalize for [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
Calling convention STACK_CALL replacing param((byte) plus::a) with paramstack(byte,plus::OFFSET_STACK_A)
Calling convention STACK_CALL replacing param((byte) plus::b) with paramstack(byte,plus::OFFSET_STACK_B)
@ -106,28 +107,30 @@ FINAL CONTROL FLOW GRAPH
(void()) main()
main: scope:[main] from @1
[4] phi()
[5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
[6] *((const byte*) SCREEN#0) ← (byte~) main::$0
[5] callprepare plus (byte) '0' (byte) 7
[6] callexecute plus
[7] (byte~) main::$0 ← callfinalize plus
[8] *((const byte*) SCREEN#0) ← (byte~) main::$0
to:main::@return
main::@return: scope:[main] from main
[7] return
[9] return
to:@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
plus: scope:[plus] from
[8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
[10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[11] return (byte) plus::return#0
[13] return (byte) plus::return#0
to:@return
VARIABLE REGISTER WEIGHTS
(byte*) SCREEN
(void()) main()
(byte~) main::$0 0.5
(byte~) main::$0 2.0
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(byte) plus::a
(byte) plus::a#0 2.0
@ -181,25 +184,29 @@ bend:
// main
main: {
.label _0 = 2
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
// [5] callprepare plus (byte) '0' (byte) 7
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
// [5] callprepare plus (byte) '0' (byte) 7
// [6] callexecute plus -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
// [7] (byte~) main::$0 ← callfinalize plus
// [7] (byte~) main::$0 ← callfinalize plus -- _stackpullbyte_1
pla
// [7] (byte~) main::$0 ← callfinalize plus -- vbuz1=_stackpullbyte_
pla
// [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1
sta.z _0
// [8] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1
lda.z _0
sta SCREEN
jmp breturn
// main::@return
breturn:
// [7] return
// [9] return
rts
}
// plus
@ -210,15 +217,15 @@ plus: {
.label a = 3
.label b = 4
.label return = 5
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
// [10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackgetbyte_vbuc1
// [11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
// [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuz1=vbuz2_plus_vbuz3
// [12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuz1=vbuz2_plus_vbuz3
lda.z a
clc
adc.z b
@ -226,40 +233,43 @@ plus: {
jmp breturn
// plus::@return
breturn:
// [11] return (byte) plus::return#0
// [13] return (byte) plus::return#0
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 ] ) always clobbers reg byte a reg byte x
Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::$0 ]
Removing always clobbered register reg byte x as potential for zp ZP_BYTE:2 [ main::$0 ]
Statement [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [5] callprepare plus (byte) '0' (byte) 7 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [7] (byte~) main::$0 ← callfinalize plus [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:6 [ plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:6 [ plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Removing always clobbered register reg byte a as potential for zp ZP_BYTE:3 [ plus::a#0 ]
Removing always clobbered register reg byte x as potential for zp ZP_BYTE:3 [ plus::a#0 ]
Statement [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:5 [ main::$0 plus::return#0 ] ) always clobbers reg byte a
Statement [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:5 [ main::$0 plus::return#0 ] ) always clobbers reg byte a
Potential registers zp ZP_BYTE:2 [ main::$0 ] : zp ZP_BYTE:2 , reg byte y ,
Statement [12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:6 [ plus::return#0 ] ) always clobbers reg byte a
Statement [5] callprepare plus (byte) '0' (byte) 7 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [7] (byte~) main::$0 ← callfinalize plus [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:6 [ plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:6 [ plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:6 [ plus::return#0 ] ) always clobbers reg byte a
Potential registers zp ZP_BYTE:2 [ main::$0 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y ,
Potential registers zp ZP_BYTE:3 [ plus::a#0 ] : zp ZP_BYTE:3 , reg byte y ,
Potential registers zp ZP_BYTE:4 [ plus::b#0 ] : zp ZP_BYTE:4 , reg byte a , reg byte x , reg byte y ,
Potential registers zp ZP_BYTE:5 [ plus::return#0 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [plus] 4: zp ZP_BYTE:4 [ plus::b#0 ] 2: zp ZP_BYTE:3 [ plus::a#0 ] 2: zp ZP_BYTE:5 [ plus::return#0 ]
Uplift Scope [main] 0.5: zp ZP_BYTE:2 [ main::$0 ]
Uplift Scope [main] 2: zp ZP_BYTE:2 [ main::$0 ]
Uplift Scope []
Uplifting [plus] best 81 combination reg byte a [ plus::b#0 ] zp ZP_BYTE:3 [ plus::a#0 ] reg byte a [ plus::return#0 ]
Uplifting [main] best 78 combination reg byte y [ main::$0 ]
Uplifting [] best 78 combination
Uplifting [plus] best 84 combination reg byte a [ plus::b#0 ] zp ZP_BYTE:3 [ plus::a#0 ] reg byte a [ plus::return#0 ]
Uplifting [main] best 84 combination zp ZP_BYTE:2 [ main::$0 ]
Uplifting [] best 84 combination
Attempting to uplift remaining variables inzp ZP_BYTE:2 [ main::$0 ]
Uplifting [main] best 84 combination zp ZP_BYTE:2 [ main::$0 ]
Attempting to uplift remaining variables inzp ZP_BYTE:3 [ plus::a#0 ]
Uplifting [plus] best 78 combination zp ZP_BYTE:3 [ plus::a#0 ]
Allocated (was zp ZP_BYTE:3) zp ZP_BYTE:2 [ plus::a#0 ]
Uplifting [plus] best 84 combination zp ZP_BYTE:3 [ plus::a#0 ]
Coalescing zero page register [ zp ZP_BYTE:3 [ plus::a#0 ] ] with [ zp ZP_BYTE:2 [ main::$0 ] ]
Allocated (was zp ZP_BYTE:3) zp ZP_BYTE:2 [ plus::a#0 main::$0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
@ -289,24 +299,30 @@ bend_from_b1:
bend:
// main
main: {
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
.label _0 = 2
// [5] callprepare plus (byte) '0' (byte) 7
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
// [5] callprepare plus (byte) '0' (byte) 7
// [6] callexecute plus -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
// [7] (byte~) main::$0 ← callfinalize plus
// [7] (byte~) main::$0 ← callfinalize plus -- _stackpullbyte_1
pla
// [7] (byte~) main::$0 ← callfinalize plus -- vbuz1=_stackpullbyte_
pla
// [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy
sty SCREEN
sta.z _0
// [8] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1
lda.z _0
sta SCREEN
jmp breturn
// main::@return
breturn:
// [7] return
// [9] return
rts
}
// plus
@ -315,20 +331,20 @@ plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
// [10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
// [11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
// [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
// [12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
jmp breturn
// plus::@return
breturn:
// [11] return (byte) plus::return#0
// [13] return (byte) plus::return#0
rts
}
// File Data
@ -339,6 +355,8 @@ Removing instruction jmp bend
Removing instruction jmp breturn
Removing instruction jmp breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction lda.z _0
Succesful ASM optimization Pass5UnnecesaryLoadElimination
Removing instruction b1_from_bbegin:
Removing instruction b1:
Removing instruction main_from_b1:
@ -362,7 +380,7 @@ FINAL SYMBOL TABLE
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(byte~) main::$0 reg byte y 0.5
(byte~) main::$0 $0 zp ZP_BYTE:2 2.0
(label) main::@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(label) plus::@return
@ -375,14 +393,13 @@ __stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(byte) plus::return
(byte) plus::return#0 reg byte a 2.0
reg byte y [ main::$0 ]
zp ZP_BYTE:2 [ plus::a#0 ]
zp ZP_BYTE:2 [ plus::a#0 main::$0 ]
reg byte a [ plus::b#0 ]
reg byte a [ plus::return#0 ]
FINAL ASSEMBLER
Score: 60
Score: 63
// File Comments
// Test a procedure with calling convention stack
@ -402,25 +419,30 @@ Score: 60
// @end
// main
main: {
.label _0 = 2
// plus('0', 7)
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
// [5] callprepare plus (byte) '0' (byte) 7
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- jsr
// [5] callprepare plus (byte) '0' (byte) 7
// [6] callexecute plus -- jsr
jsr plus
// [5] (byte~) main::$0 ← call plus (byte) '0' (byte) 7 -- _stackpullbyte_2
// [7] (byte~) main::$0 ← callfinalize plus
// [7] (byte~) main::$0 ← callfinalize plus -- _stackpullbyte_1
pla
// [7] (byte~) main::$0 ← callfinalize plus -- vbuz1=_stackpullbyte_
pla
sta.z _0
// SCREEN[0] = plus('0', 7)
// [6] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuyy
sty SCREEN
// [8] *((const byte*) SCREEN#0) ← (byte~) main::$0 -- _deref_pbuc1=vbuz1
sta SCREEN
// main::@return
// }
// [7] return
// [9] return
rts
}
// plus
@ -429,20 +451,20 @@ plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
// [8] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
// [10] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [9] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
// [11] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
// return a+b;
// [10] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
// [12] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
// plus::@return
// }
// [11] return (byte) plus::return#0
// [13] return (byte) plus::return#0
rts
}
// File Data

@ -5,7 +5,7 @@
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(byte~) main::$0 reg byte y 0.5
(byte~) main::$0 $0 zp ZP_BYTE:2 2.0
(label) main::@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(label) plus::@return
@ -18,7 +18,6 @@ __stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(byte) plus::return
(byte) plus::return#0 reg byte a 2.0
reg byte y [ main::$0 ]
zp ZP_BYTE:2 [ plus::a#0 ]
zp ZP_BYTE:2 [ plus::a#0 main::$0 ]
reg byte a [ plus::b#0 ]
reg byte a [ plus::return#0 ]

@ -15,23 +15,25 @@ main: {
lda #<$2345
pha
jsr plus
tsx
txa
axs #-4
txs
pla
pla
pla
sta.z _0
pla
sta.z _0+1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
rts
}
// plus(word zeropage(4) a, word zeropage(6) b)
// plus(word zeropage(2) a, word zeropage(4) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.label a = 4
.label b = 6
.label return = 4
.label a = 2
.label b = 4
.label return = 2
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a

@ -11,19 +11,21 @@
(void()) main()
main: scope:[main] from @1
[4] phi()
[5] (word~) main::$0 ← call plus (word) $1234 (word) $2345
[6] *((const word*) SCREEN#0) ← (word~) main::$0
[5] callprepare plus (word) $1234 (word) $2345
[6] callexecute plus
[7] (word~) main::$0 ← callfinalize plus
[8] *((const word*) SCREEN#0) ← (word~) main::$0
to:main::@return
main::@return: scope:[main] from main
[7] return
[9] return
to:@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
plus: scope:[plus] from
[8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A)
[9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B)
[10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0
[10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A)
[11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B)
[12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[11] return (word) plus::return#0
[13] return (word) plus::return#0
to:@return

@ -106,6 +106,7 @@ Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
Calling convention STACK_CALL adding prepare/execute/finalize for [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345
Calling convention STACK_CALL replacing param((word) plus::a) with paramstack(word,plus::OFFSET_STACK_A)
Calling convention STACK_CALL replacing param((word) plus::b) with paramstack(word,plus::OFFSET_STACK_B)
@ -123,28 +124,30 @@ FINAL CONTROL FLOW GRAPH
(void()) main()
main: scope:[main] from @1
[4] phi()
[5] (word~) main::$0 ← call plus (word) $1234 (word) $2345
[6] *((const word*) SCREEN#0) ← (word~) main::$0
[5] callprepare plus (word) $1234 (word) $2345
[6] callexecute plus
[7] (word~) main::$0 ← callfinalize plus
[8] *((const word*) SCREEN#0) ← (word~) main::$0
to:main::@return
main::@return: scope:[main] from main
[7] return
[9] return
to:@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
plus: scope:[plus] from
[8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A)
[9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B)
[10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0
[10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A)
[11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B)
[12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[11] return (word) plus::return#0
[13] return (word) plus::return#0
to:@return
VARIABLE REGISTER WEIGHTS
(word*) SCREEN
(void()) main()
(word~) main::$0 0.5
(word~) main::$0 2.0
__stackcall (word()) plus((word) plus::a , (word) plus::b)
(word) plus::a
(word) plus::a#0 2.0
@ -198,25 +201,30 @@ bend:
// main
main: {
.label _0 = 2
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
// [5] callprepare plus (word) $1234 (word) $2345
// [5] callprepare plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$1234
pha
lda #<$1234
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
// [5] callprepare plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$2345
pha
lda #<$2345
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- jsr
// [5] callprepare plus (word) $1234 (word) $2345
// [6] callexecute plus -- jsr
jsr plus
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpullbyte_4
tsx
txa
axs #-4
txs
// [6] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
// [7] (word~) main::$0 ← callfinalize plus
// [7] (word~) main::$0 ← callfinalize plus -- _stackpullbyte_2
pla
pla
// [7] (word~) main::$0 ← callfinalize plus -- vwuz1=_stackpullword_
pla
sta.z _0
pla
sta.z _0+1
// [8] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
lda.z _0
sta SCREEN
lda.z _0+1
@ -224,7 +232,7 @@ main: {
jmp breturn
// main::@return
breturn:
// [7] return
// [9] return
rts
}
// plus
@ -235,19 +243,19 @@ plus: {
.label a = 4
.label b = 6
.label return = 8
// [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
// [10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
lda STACK_BASE+OFFSET_STACK_A+1,x
sta.z a+1
// [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
// [11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
lda STACK_BASE+OFFSET_STACK_B+1,x
sta.z b+1
// [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz2_plus_vwuz3
// [12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz2_plus_vwuz3
lda.z a
clc
adc.z b
@ -258,17 +266,18 @@ plus: {
jmp breturn
// plus::@return
breturn:
// [11] return (word) plus::return#0
// [13] return (word) plus::return#0
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a reg byte x
Statement [6] *((const word*) SCREEN#0) ← (word~) main::$0 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:5 [ main::$0 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 [ plus::return#0 ] ( main:2::plus:5 [ main::$0 plus::return#0 ] ) always clobbers reg byte a
Statement [5] callprepare plus (word) $1234 (word) $2345 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [7] (word~) main::$0 ← callfinalize plus [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [8] *((const word*) SCREEN#0) ← (word~) main::$0 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:6 [ plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:6 [ plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 [ plus::return#0 ] ( main:2::plus:6 [ plus::return#0 ] ) always clobbers reg byte a
Potential registers zp ZP_WORD:2 [ main::$0 ] : zp ZP_WORD:2 ,
Potential registers zp ZP_WORD:4 [ plus::a#0 ] : zp ZP_WORD:4 ,
Potential registers zp ZP_WORD:6 [ plus::b#0 ] : zp ZP_WORD:6 ,
@ -276,13 +285,16 @@ Potential registers zp ZP_WORD:8 [ plus::return#0 ] : zp ZP_WORD:8 ,
REGISTER UPLIFT SCOPES
Uplift Scope [plus] 4: zp ZP_WORD:6 [ plus::b#0 ] 2: zp ZP_WORD:4 [ plus::a#0 ] 2: zp ZP_WORD:8 [ plus::return#0 ]
Uplift Scope [main] 0.5: zp ZP_WORD:2 [ main::$0 ]
Uplift Scope [main] 2: zp ZP_WORD:2 [ main::$0 ]
Uplift Scope []
Uplifting [plus] best 132 combination zp ZP_WORD:6 [ plus::b#0 ] zp ZP_WORD:4 [ plus::a#0 ] zp ZP_WORD:8 [ plus::return#0 ]
Uplifting [main] best 132 combination zp ZP_WORD:2 [ main::$0 ]
Uplifting [] best 132 combination
Uplifting [plus] best 146 combination zp ZP_WORD:6 [ plus::b#0 ] zp ZP_WORD:4 [ plus::a#0 ] zp ZP_WORD:8 [ plus::return#0 ]
Uplifting [main] best 146 combination zp ZP_WORD:2 [ main::$0 ]
Uplifting [] best 146 combination
Coalescing zero page register [ zp ZP_WORD:4 [ plus::a#0 ] ] with [ zp ZP_WORD:8 [ plus::return#0 ] ] - score: 1
Coalescing zero page register [ zp ZP_WORD:4 [ plus::a#0 plus::return#0 ] ] with [ zp ZP_WORD:2 [ main::$0 ] ]
Allocated (was zp ZP_WORD:4) zp ZP_WORD:2 [ plus::a#0 plus::return#0 main::$0 ]
Allocated (was zp ZP_WORD:6) zp ZP_WORD:4 [ plus::b#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
@ -313,25 +325,30 @@ bend:
// main
main: {
.label _0 = 2
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
// [5] callprepare plus (word) $1234 (word) $2345
// [5] callprepare plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$1234
pha
lda #<$1234
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
// [5] callprepare plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$2345
pha
lda #<$2345
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- jsr
// [5] callprepare plus (word) $1234 (word) $2345
// [6] callexecute plus -- jsr
jsr plus
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpullbyte_4
tsx
txa
axs #-4
txs
// [6] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
// [7] (word~) main::$0 ← callfinalize plus
// [7] (word~) main::$0 ← callfinalize plus -- _stackpullbyte_2
pla
pla
// [7] (word~) main::$0 ← callfinalize plus -- vwuz1=_stackpullword_
pla
sta.z _0
pla
sta.z _0+1
// [8] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
lda.z _0
sta SCREEN
lda.z _0+1
@ -339,30 +356,30 @@ main: {
jmp breturn
// main::@return
breturn:
// [7] return
// [9] return
rts
}
// plus
// plus(word zeropage(4) a, word zeropage(6) b)
// plus(word zeropage(2) a, word zeropage(4) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.label a = 4
.label b = 6
.label return = 4
// [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
.label a = 2
.label b = 4
.label return = 2
// [10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
lda STACK_BASE+OFFSET_STACK_A+1,x
sta.z a+1
// [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
// [11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
lda STACK_BASE+OFFSET_STACK_B+1,x
sta.z b+1
// [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz1_plus_vwuz2
// [12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz1_plus_vwuz2
lda.z return
clc
adc.z b
@ -373,7 +390,7 @@ plus: {
jmp breturn
// plus::@return
breturn:
// [11] return (word) plus::return#0
// [13] return (word) plus::return#0
rts
}
// File Data
@ -407,26 +424,25 @@ FINAL SYMBOL TABLE
(const word*) SCREEN#0 SCREEN = (word*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(word~) main::$0 $0 zp ZP_WORD:2 0.5
(word~) main::$0 $0 zp ZP_WORD:2 2.0
(label) main::@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
(label) plus::@return
(const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0
(const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 2
(word) plus::a
(word) plus::a#0 a zp ZP_WORD:4 2.0
(word) plus::a#0 a zp ZP_WORD:2 2.0
(word) plus::b
(word) plus::b#0 b zp ZP_WORD:6 4.0
(word) plus::b#0 b zp ZP_WORD:4 4.0
(word) plus::return
(word) plus::return#0 return zp ZP_WORD:4 2.0
(word) plus::return#0 return zp ZP_WORD:2 2.0
zp ZP_WORD:2 [ main::$0 ]
zp ZP_WORD:4 [ plus::a#0 plus::return#0 ]
zp ZP_WORD:6 [ plus::b#0 ]
zp ZP_WORD:2 [ plus::a#0 plus::return#0 main::$0 ]
zp ZP_WORD:4 [ plus::b#0 ]
FINAL ASSEMBLER
Score: 114
Score: 128
// File Comments
// Test a procedure with calling convention stack - and enough parameters to use fast ASM for cleaning stack
@ -448,57 +464,62 @@ Score: 114
main: {
.label _0 = 2
// plus(0x1234, 0x2345)
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
// [5] callprepare plus (word) $1234 (word) $2345
// [5] callprepare plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$1234
pha
lda #<$1234
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
// [5] callprepare plus (word) $1234 (word) $2345 -- _stackpushword_=vwuc1
lda #>$2345
pha
lda #<$2345
pha
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- jsr
// [5] callprepare plus (word) $1234 (word) $2345
// [6] callexecute plus -- jsr
jsr plus
// [5] (word~) main::$0 ← call plus (word) $1234 (word) $2345 -- _stackpullbyte_4
tsx
txa
axs #-4
txs
// [7] (word~) main::$0 ← callfinalize plus
// [7] (word~) main::$0 ← callfinalize plus -- _stackpullbyte_2
pla
pla
// [7] (word~) main::$0 ← callfinalize plus -- vwuz1=_stackpullword_
pla
sta.z _0
pla
sta.z _0+1
// SCREEN[0] = plus(0x1234, 0x2345)
// [6] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
// [8] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
// main::@return
// }
// [7] return
// [9] return
rts
}
// plus
// plus(word zeropage(4) a, word zeropage(6) b)
// plus(word zeropage(2) a, word zeropage(4) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.label a = 4
.label b = 6
.label return = 4
// [8] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
.label a = 2
.label b = 4
.label return = 2
// [10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
lda STACK_BASE+OFFSET_STACK_A+1,x
sta.z a+1
// [9] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
// [11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
lda STACK_BASE+OFFSET_STACK_B+1,x
sta.z b+1
// return a+b;
// [10] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz1_plus_vwuz2
// [12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz1_plus_vwuz2
lda.z return
clc
adc.z b
@ -508,7 +529,7 @@ plus: {
sta.z return+1
// plus::@return
// }
// [11] return (word) plus::return#0
// [13] return (word) plus::return#0
rts
}
// File Data

@ -5,19 +5,18 @@
(const word*) SCREEN#0 SCREEN = (word*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(word~) main::$0 $0 zp ZP_WORD:2 0.5
(word~) main::$0 $0 zp ZP_WORD:2 2.0
(label) main::@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
(label) plus::@return
(const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0
(const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 2
(word) plus::a
(word) plus::a#0 a zp ZP_WORD:4 2.0
(word) plus::a#0 a zp ZP_WORD:2 2.0
(word) plus::b
(word) plus::b#0 b zp ZP_WORD:6 4.0
(word) plus::b#0 b zp ZP_WORD:4 4.0
(word) plus::return
(word) plus::return#0 return zp ZP_WORD:4 2.0
(word) plus::return#0 return zp ZP_WORD:2 2.0
zp ZP_WORD:2 [ main::$0 ]
zp ZP_WORD:4 [ plus::a#0 plus::return#0 ]
zp ZP_WORD:6 [ plus::b#0 ]
zp ZP_WORD:2 [ plus::a#0 plus::return#0 main::$0 ]
zp ZP_WORD:4 [ plus::b#0 ]

@ -0,0 +1,54 @@
// Test a procedure with calling convention stack
// Test casting of parameter types
// Currently fails because the pushed are done based on the actual value instead of the decalred parameter type
// https://gitlab.com/camelot/kickc/issues/319
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.label SCREEN = $400
.const STACK_BASE = $103
main: {
.label _0 = 2
lda #'0'
pha
lda #7
pha
jsr plus
pla
pla
pla
sta.z _0
pla
sta.z _0+1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
rts
}
// plus(word zeropage(2) a, word zeropage(4) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.label a = 2
.label b = 4
.label return = 2
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
lda STACK_BASE+OFFSET_STACK_A+1,x
sta.z a+1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
lda STACK_BASE+OFFSET_STACK_B+1,x
sta.z b+1
lda.z return
clc
adc.z b
sta.z return
lda.z return+1
adc.z b+1
sta.z return+1
rts
}

@ -0,0 +1,31 @@
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
(void()) main()
main: scope:[main] from @1
[4] phi()
[5] callprepare plus (byte) '0' (byte) 7
[6] callexecute plus
[7] (word~) main::$0 ← callfinalize plus
[8] *((const word*) SCREEN#0) ← (word~) main::$0
to:main::@return
main::@return: scope:[main] from main
[9] return
to:@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
plus: scope:[plus] from
[10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A)
[11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B)
[12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[13] return (word) plus::return#0
to:@return

@ -0,0 +1,530 @@
Fixing pointer array-indexing *((word*) SCREEN + (number) 0)
Culled Empty Block (label) @1
Culled Empty Block (label) plus::@1
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
(word*) SCREEN#0 ← ((word*)) (number) $400
to:@2
(void()) main()
main: scope:[main] from @2
(word~) main::$0 ← call plus (byte) '0' (number) 7
(number~) main::$1 ← (number) 0 * (const byte) SIZEOF_WORD
*((word*) SCREEN#0 + (number~) main::$1) ← (word~) main::$0
to:main::@return
main::@return: scope:[main] from main
return
to:@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
plus: scope:[plus] from
(word) plus::a#0 ← param((word) plus::a)
(word) plus::b#0 ← param((word) plus::b)
(word~) plus::$0 ← (word) plus::a#0 + (word) plus::b#0
(word) plus::return#0 ← (word~) plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus
(word) plus::return#1 ← phi( plus/(word) plus::return#0 )
return (word) plus::return#1
to:@return
@2: scope:[] from @begin
call main
to:@3
@3: scope:[] from @2
to:@end
@end: scope:[] from @3
SYMBOL TABLE SSA
(label) @2
(label) @3
(label) @begin
(label) @end
(word*) SCREEN
(word*) SCREEN#0
(const byte) SIZEOF_WORD = (byte) 2
(void()) main()
(word~) main::$0
(number~) main::$1
(label) main::@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
(word~) plus::$0
(label) plus::@return
(word) plus::a
(word) plus::a#0
(word) plus::b
(word) plus::b#0
(word) plus::return
(word) plus::return#0
(word) plus::return#1
Adding number conversion cast (unumber) 7 in (word~) main::$0 ← call plus (byte) '0' (number) 7
Adding number conversion cast (unumber) 0 in (number~) main::$1 ← (number) 0 * (const byte) SIZEOF_WORD
Adding number conversion cast (unumber) main::$1 in (number~) main::$1 ← (unumber)(number) 0 * (const byte) SIZEOF_WORD
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast (word*) SCREEN#0 ← (word*)(number) $400
Successful SSA optimization Pass2InlineCast
Simplifying constant pointer cast (word*) 1024
Simplifying constant integer cast 7
Simplifying constant integer cast 0
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) 7
Finalized unsigned number type (byte) 0
Successful SSA optimization PassNFinalizeNumberTypeConversions
Inferred type updated to byte in (unumber~) main::$1 ← (byte) 0 * (const byte) SIZEOF_WORD
Alias (word) plus::return#0 = (word~) plus::$0 (word) plus::return#1
Successful SSA optimization Pass2AliasElimination
Constant right-side identified [2] (byte~) main::$1 ← (byte) 0 * (const byte) SIZEOF_WORD
Successful SSA optimization Pass2ConstantRValueConsolidation
Constant (const word*) SCREEN#0 = (word*) 1024
Constant (const byte) main::$1 = 0*SIZEOF_WORD
Successful SSA optimization Pass2ConstantIdentification
Simplifying constant evaluating to zero (byte) 0*(const byte) SIZEOF_WORD in
Successful SSA optimization PassNSimplifyConstantZero
Simplifying expression containing zero SCREEN#0 in [3] *((const word*) SCREEN#0 + (const byte) main::$1) ← (word~) main::$0
Successful SSA optimization PassNSimplifyExpressionWithZero
Eliminating unused constant (const byte) main::$1
Eliminating unused constant (const byte) SIZEOF_WORD
Successful SSA optimization PassNEliminateUnusedVars
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @2
Adding NOP phi() at start of @3
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
CALL GRAPH
Calls in [] to main:2
Calls in [main] to plus:6
Created 0 initial phi equivalence classes
Coalesced down to 0 phi equivalence classes
Culled Empty Block (label) @3
Renumbering block @2 to @1
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
Calling convention STACK_CALL adding prepare/execute/finalize for [5] (word~) main::$0 ← call plus (byte) '0' (byte) 7
Calling convention STACK_CALL replacing param((word) plus::a) with paramstack(word,plus::OFFSET_STACK_A)
Calling convention STACK_CALL replacing param((word) plus::b) with paramstack(word,plus::OFFSET_STACK_B)
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()
(void()) main()
main: scope:[main] from @1
[4] phi()
[5] callprepare plus (byte) '0' (byte) 7
[6] callexecute plus
[7] (word~) main::$0 ← callfinalize plus
[8] *((const word*) SCREEN#0) ← (word~) main::$0
to:main::@return
main::@return: scope:[main] from main
[9] return
to:@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
plus: scope:[plus] from
[10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A)
[11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B)
[12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[13] return (word) plus::return#0
to:@return
VARIABLE REGISTER WEIGHTS
(word*) SCREEN
(void()) main()
(word~) main::$0 2.0
__stackcall (word()) plus((word) plus::a , (word) plus::b)
(word) plus::a
(word) plus::a#0 2.0
(word) plus::b
(word) plus::b#0 4.0
(word) plus::return
(word) plus::return#0 2.0
Initial phi equivalence classes
Added variable main::$0 to zero page equivalence class [ main::$0 ]
Added variable plus::a#0 to zero page equivalence class [ plus::a#0 ]
Added variable plus::b#0 to zero page equivalence class [ plus::b#0 ]
Added variable plus::return#0 to zero page equivalence class [ plus::return#0 ]
Complete equivalence classes
[ main::$0 ]
[ plus::a#0 ]
[ plus::b#0 ]
[ plus::return#0 ]
Allocated zp ZP_WORD:2 [ main::$0 ]
Allocated zp ZP_WORD:4 [ plus::a#0 ]
Allocated zp ZP_WORD:6 [ plus::b#0 ]
Allocated zp ZP_WORD:8 [ plus::return#0 ]
INITIAL ASM
Target platform is c64basic / MOS6502X
// File Comments
// Test a procedure with calling convention stack
// Test casting of parameter types
// Currently fails because the pushed are done based on the actual value instead of the decalred parameter type
// https://gitlab.com/camelot/kickc/issues/319
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.const STACK_BASE = $103
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
// @1
b1:
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
.label _0 = 2
// [5] callprepare plus (byte) '0' (byte) 7
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] callprepare plus (byte) '0' (byte) 7
// [6] callexecute plus -- jsr
jsr plus
// [7] (word~) main::$0 ← callfinalize plus
// [7] (word~) main::$0 ← callfinalize plus -- _stackpullbyte_2
pla
pla
// [7] (word~) main::$0 ← callfinalize plus -- vwuz1=_stackpullword_
pla
sta.z _0
pla
sta.z _0+1
// [8] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
jmp breturn
// main::@return
breturn:
// [9] return
rts
}
// plus
// plus(word zeropage(4) a, word zeropage(6) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.label a = 4
.label b = 6
.label return = 8
// [10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
lda STACK_BASE+OFFSET_STACK_A+1,x
sta.z a+1
// [11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
lda STACK_BASE+OFFSET_STACK_B+1,x
sta.z b+1
// [12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz2_plus_vwuz3
lda.z a
clc
adc.z b
sta.z return
lda.z a+1
adc.z b+1
sta.z return+1
jmp breturn
// plus::@return
breturn:
// [13] return (word) plus::return#0
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [5] callprepare plus (byte) '0' (byte) 7 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [7] (word~) main::$0 ← callfinalize plus [ main::$0 ] ( main:2 [ main::$0 ] ) always clobbers reg byte a
Statement [8] *((const word*) SCREEN#0) ← (word~) main::$0 [ ] ( main:2 [ ] ) always clobbers reg byte a
Statement [10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:6 [ plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:6 [ plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 [ plus::return#0 ] ( main:2::plus:6 [ plus::return#0 ] ) always clobbers reg byte a
Potential registers zp ZP_WORD:2 [ main::$0 ] : zp ZP_WORD:2 ,
Potential registers zp ZP_WORD:4 [ plus::a#0 ] : zp ZP_WORD:4 ,
Potential registers zp ZP_WORD:6 [ plus::b#0 ] : zp ZP_WORD:6 ,
Potential registers zp ZP_WORD:8 [ plus::return#0 ] : zp ZP_WORD:8 ,
REGISTER UPLIFT SCOPES
Uplift Scope [plus] 4: zp ZP_WORD:6 [ plus::b#0 ] 2: zp ZP_WORD:4 [ plus::a#0 ] 2: zp ZP_WORD:8 [ plus::return#0 ]
Uplift Scope [main] 2: zp ZP_WORD:2 [ main::$0 ]
Uplift Scope []
Uplifting [plus] best 136 combination zp ZP_WORD:6 [ plus::b#0 ] zp ZP_WORD:4 [ plus::a#0 ] zp ZP_WORD:8 [ plus::return#0 ]
Uplifting [main] best 136 combination zp ZP_WORD:2 [ main::$0 ]
Uplifting [] best 136 combination
Coalescing zero page register [ zp ZP_WORD:4 [ plus::a#0 ] ] with [ zp ZP_WORD:8 [ plus::return#0 ] ] - score: 1
Coalescing zero page register [ zp ZP_WORD:4 [ plus::a#0 plus::return#0 ] ] with [ zp ZP_WORD:2 [ main::$0 ] ]
Allocated (was zp ZP_WORD:4) zp ZP_WORD:2 [ plus::a#0 plus::return#0 main::$0 ]
Allocated (was zp ZP_WORD:6) zp ZP_WORD:4 [ plus::b#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test a procedure with calling convention stack
// Test casting of parameter types
// Currently fails because the pushed are done based on the actual value instead of the decalred parameter type
// https://gitlab.com/camelot/kickc/issues/319
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.const STACK_BASE = $103
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
// @1
b1:
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
.label _0 = 2
// [5] callprepare plus (byte) '0' (byte) 7
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] callprepare plus (byte) '0' (byte) 7
// [6] callexecute plus -- jsr
jsr plus
// [7] (word~) main::$0 ← callfinalize plus
// [7] (word~) main::$0 ← callfinalize plus -- _stackpullbyte_2
pla
pla
// [7] (word~) main::$0 ← callfinalize plus -- vwuz1=_stackpullword_
pla
sta.z _0
pla
sta.z _0+1
// [8] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
jmp breturn
// main::@return
breturn:
// [9] return
rts
}
// plus
// plus(word zeropage(2) a, word zeropage(4) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.label a = 2
.label b = 4
.label return = 2
// [10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
lda STACK_BASE+OFFSET_STACK_A+1,x
sta.z a+1
// [11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
lda STACK_BASE+OFFSET_STACK_B+1,x
sta.z b+1
// [12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz1_plus_vwuz2
lda.z return
clc
adc.z b
sta.z return
lda.z return+1
adc.z b+1
sta.z return+1
jmp breturn
// plus::@return
breturn:
// [13] return (word) plus::return#0
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp b1
Removing instruction jmp bend
Removing instruction jmp breturn
Removing instruction jmp breturn
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction b1_from_bbegin:
Removing instruction b1:
Removing instruction main_from_b1:
Removing instruction bend_from_b1:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction bend:
Removing instruction breturn:
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
(word*) SCREEN
(const word*) SCREEN#0 SCREEN = (word*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(word~) main::$0 $0 zp ZP_WORD:2 2.0
(label) main::@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
(label) plus::@return
(const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0
(const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 2
(word) plus::a
(word) plus::a#0 a zp ZP_WORD:2 2.0
(word) plus::b
(word) plus::b#0 b zp ZP_WORD:4 4.0
(word) plus::return
(word) plus::return#0 return zp ZP_WORD:2 2.0
zp ZP_WORD:2 [ plus::a#0 plus::return#0 main::$0 ]
zp ZP_WORD:4 [ plus::b#0 ]
FINAL ASSEMBLER
Score: 118
// File Comments
// Test a procedure with calling convention stack
// Test casting of parameter types
// Currently fails because the pushed are done based on the actual value instead of the decalred parameter type
// https://gitlab.com/camelot/kickc/issues/319
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.const STACK_BASE = $103
// @begin
// [1] phi from @begin to @1 [phi:@begin->@1]
// @1
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
// [3] phi from @1 to @end [phi:@1->@end]
// @end
// main
main: {
.label _0 = 2
// plus('0', 7)
// [5] callprepare plus (byte) '0' (byte) 7
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [5] callprepare plus (byte) '0' (byte) 7 -- _stackpushbyte_=vbuc1
lda #7
pha
// [5] callprepare plus (byte) '0' (byte) 7
// [6] callexecute plus -- jsr
jsr plus
// [7] (word~) main::$0 ← callfinalize plus
// [7] (word~) main::$0 ← callfinalize plus -- _stackpullbyte_2
pla
pla
// [7] (word~) main::$0 ← callfinalize plus -- vwuz1=_stackpullword_
pla
sta.z _0
pla
sta.z _0+1
// SCREEN[0] = plus('0', 7)
// [8] *((const word*) SCREEN#0) ← (word~) main::$0 -- _deref_pwuc1=vwuz1
lda.z _0
sta SCREEN
lda.z _0+1
sta SCREEN+1
// main::@return
// }
// [9] return
rts
}
// plus
// plus(word zeropage(2) a, word zeropage(4) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 2
.label a = 2
.label b = 4
.label return = 2
// [10] (word) plus::a#0 ← paramstack(word,plus::OFFSET_STACK_A) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
lda STACK_BASE+OFFSET_STACK_A+1,x
sta.z a+1
// [11] (word) plus::b#0 ← paramstack(word,plus::OFFSET_STACK_B) -- vwuz1=_stackgetword_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
lda STACK_BASE+OFFSET_STACK_B+1,x
sta.z b+1
// return a+b;
// [12] (word) plus::return#0 ← (word) plus::a#0 + (word) plus::b#0 -- vwuz1=vwuz1_plus_vwuz2
lda.z return
clc
adc.z b
sta.z return
lda.z return+1
adc.z b+1
sta.z return+1
// plus::@return
// }
// [13] return (word) plus::return#0
rts
}
// File Data

@ -0,0 +1,22 @@
(label) @1
(label) @begin
(label) @end
(word*) SCREEN
(const word*) SCREEN#0 SCREEN = (word*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(void()) main()
(word~) main::$0 $0 zp ZP_WORD:2 2.0
(label) main::@return
__stackcall (word()) plus((word) plus::a , (word) plus::b)
(label) plus::@return
(const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0
(const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 2
(word) plus::a
(word) plus::a#0 a zp ZP_WORD:2 2.0
(word) plus::b
(word) plus::b#0 b zp ZP_WORD:4 4.0
(word) plus::return
(word) plus::return#0 return zp ZP_WORD:2 2.0
zp ZP_WORD:2 [ plus::a#0 plus::return#0 main::$0 ]
zp ZP_WORD:4 [ plus::b#0 ]

@ -0,0 +1,45 @@
// Test a procedure with calling convention stack
// A slightly more complex call
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
.label SCREEN = $400
.const STACK_BASE = $103
main: {
.label w = 2
ldy #0
b1:
tya
tax
inx
lda #'0'
pha
txa
pha
jsr plus
pla
pla
sta.z w
tya
clc
adc.z w
sta SCREEN
iny
cpy #2
bne b1
rts
}
// plus(byte zeropage(2) a, byte register(A) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
tsx
lda STACK_BASE+OFFSET_STACK_B,x
clc
adc.z a
rts
}

@ -0,0 +1,38 @@
@begin: scope:[] from
[0] phi()
to:@1
@1: scope:[] from @begin
[1] phi()
[2] call main
to:@end
@end: scope:[] from @1
[3] phi()
(void()) main()
main: scope:[main] from @1
[4] phi()
to:main::@1
main::@1: scope:[main] from main main::@1
[5] (byte) main::a#2 ← phi( main/(byte) 0 main::@1/(byte) main::a#1 )
[6] (byte) main::v#0 ← (byte) main::a#2 + (byte) 1
[7] callprepare plus (byte) '0' (byte) main::v#0
[8] callexecute plus
[9] (byte) main::w#0 ← callfinalize plus
[10] (byte~) main::$2 ← (byte) main::w#0 + (byte) main::a#2
[11] *((const byte*) SCREEN#0) ← (byte~) main::$2
[12] (byte) main::a#1 ← ++ (byte) main::a#2
[13] if((byte) main::a#1!=(byte) 2) goto main::@1
to:main::@return
main::@return: scope:[main] from main::@1
[14] return
to:@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
plus: scope:[plus] from
[15] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[16] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[17] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[18] return (byte) plus::return#0
to:@return

@ -0,0 +1,678 @@
Culled Empty Block (label) main::@2
Culled Empty Block (label) @1
Culled Empty Block (label) plus::@1
CONTROL FLOW GRAPH SSA
@begin: scope:[] from
(byte*) SCREEN#0 ← ((byte*)) (number) $400
(byte) i#0 ← (number) 0
to:@2
(void()) main()
main: scope:[main] from @2
(byte) i#8 ← phi( @2/(byte) i#9 )
(byte) main::a#0 ← (byte) 0
to:main::@1
main::@1: scope:[main] from main main::@1
(byte) i#4 ← phi( main/(byte) i#8 main::@1/(byte) i#4 )
(byte) main::a#2 ← phi( main/(byte) main::a#0 main::@1/(byte) main::a#1 )
(number~) main::$0 ← (byte) main::a#2 + (number) 1
(byte) main::v#0 ← (number~) main::$0
(byte~) main::$1 ← call plus (byte) '0' (byte) main::v#0
(byte) main::w#0 ← (byte~) main::$1
(byte~) main::$2 ← (byte) main::w#0 + (byte) main::a#2
*((byte*) SCREEN#0 + (byte) i#4) ← (byte~) main::$2
(byte) main::a#1 ← (byte) main::a#2 + rangenext(0,1)
(bool~) main::$3 ← (byte) main::a#1 != rangelast(0,1)
if((bool~) main::$3) goto main::@1
to:main::@return
main::@return: scope:[main] from main::@1
(byte) i#5 ← phi( main::@1/(byte) i#4 )
(byte) i#1 ← (byte) i#5
return
to:@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
plus: scope:[plus] from
(byte) i#6 ← phi( )
(byte) plus::a#0 ← param((byte) plus::a)
(byte) plus::b#0 ← param((byte) plus::b)
(byte) i#2 ← ++ (byte) i#6
(byte~) plus::$0 ← (byte) plus::a#0 + (byte) plus::b#0
(byte) plus::return#0 ← (byte~) plus::$0
to:plus::@return
plus::@return: scope:[plus] from plus
(byte) plus::return#1 ← phi( plus/(byte) plus::return#0 )
return (byte) plus::return#1
to:@return
@2: scope:[] from @begin
(byte) i#9 ← phi( @begin/(byte) i#0 )
call main
to:@3
@3: scope:[] from @2
(byte) i#7 ← phi( @2/(byte) i#1 )
(byte) i#3 ← (byte) i#7
to:@end
@end: scope:[] from @3
SYMBOL TABLE SSA
(label) @2
(label) @3
(label) @begin
(label) @end
(byte*) SCREEN
(byte*) SCREEN#0
(byte) i
(byte) i#0
(byte) i#1
(byte) i#2
(byte) i#3
(byte) i#4
(byte) i#5
(byte) i#6
(byte) i#7
(byte) i#8
(byte) i#9
(void()) main()
(number~) main::$0
(byte~) main::$1
(byte~) main::$2
(bool~) main::$3
(label) main::@1
(label) main::@return
(byte) main::a
(byte) main::a#0
(byte) main::a#1
(byte) main::a#2
(byte) main::v
(byte) main::v#0
(byte) main::w
(byte) main::w#0
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(byte~) plus::$0
(label) plus::@return
(byte) plus::a
(byte) plus::a#0
(byte) plus::b
(byte) plus::b#0
(byte) plus::return
(byte) plus::return#0
(byte) plus::return#1
Adding number conversion cast (unumber) 0 in (byte) i#0 ← (number) 0
Adding number conversion cast (unumber) 1 in (number~) main::$0 ← (byte) main::a#2 + (number) 1
Adding number conversion cast (unumber) main::$0 in (number~) main::$0 ← (byte) main::a#2 + (unumber)(number) 1
Successful SSA optimization PassNAddNumberTypeConversions
Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400
Inlining cast (byte) i#0 ← (unumber)(number) 0
Successful SSA optimization Pass2InlineCast
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) 0
Finalized unsigned number type (byte) 1
Successful SSA optimization PassNFinalizeNumberTypeConversions
Inferred type updated to byte in (unumber~) main::$0 ← (byte) main::a#2 + (byte) 1
Alias (byte) main::v#0 = (byte~) main::$0
Alias (byte) main::w#0 = (byte~) main::$1
Alias (byte) i#1 = (byte) i#5 (byte) i#4
Alias (byte) plus::return#0 = (byte~) plus::$0 (byte) plus::return#1
Alias (byte) i#0 = (byte) i#9
Alias (byte) i#3 = (byte) i#7
Successful SSA optimization Pass2AliasElimination
Identical Phi Values (byte) i#8 (byte) i#0
Identical Phi Values (byte) i#1 (byte) i#8
Identical Phi Values (byte) i#3 (byte) i#1
Successful SSA optimization Pass2IdenticalPhiElimination
Simple Condition (bool~) main::$3 [13] if((byte) main::a#1!=rangelast(0,1)) goto main::@1
Successful SSA optimization Pass2ConditionalJumpSimplification
Constant (const byte*) SCREEN#0 = (byte*) 1024
Constant (const byte) i#0 = 0
Constant (const byte) main::a#0 = 0
Successful SSA optimization Pass2ConstantIdentification
Resolved ranged next value [11] main::a#1 ← ++ main::a#2 to ++
Resolved ranged comparison value [13] if(main::a#1!=rangelast(0,1)) goto main::@1 to (number) 2
Simplifying expression containing zero SCREEN#0 in [10] *((const byte*) SCREEN#0 + (const byte) i#0) ← (byte~) main::$2
Successful SSA optimization PassNSimplifyExpressionWithZero
Eliminating unused variable (byte) i#2 and assignment [11] (byte) i#2 ← ++ (byte) i#6
Eliminating unused constant (const byte) i#0
Successful SSA optimization PassNEliminateUnusedVars
Eliminating unused variable - keeping the phi block (byte) i#6
Successful SSA optimization PassNEliminateUnusedVars
Adding number conversion cast (unumber) 2 in if((byte) main::a#1!=(number) 2) goto main::@1
Successful SSA optimization PassNAddNumberTypeConversions
Simplifying constant integer cast 2
Successful SSA optimization PassNCastSimplification
Finalized unsigned number type (byte) 2
Successful SSA optimization PassNFinalizeNumberTypeConversions
Inlining constant with var siblings (const byte) main::a#0
Constant inlined main::a#0 = (byte) 0
Successful SSA optimization Pass2ConstantInlining
Added new block during phi lifting main::@3(between main::@1 and main::@1)
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @2
Adding NOP phi() at start of @3
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
CALL GRAPH
Calls in [] to main:2
Calls in [main] to plus:8
Created 1 initial phi equivalence classes
Coalesced [14] main::a#3 ← main::a#1
Coalesced down to 1 phi equivalence classes
Culled Empty Block (label) @3
Culled Empty Block (label) main::@3
Renumbering block @2 to @1
Adding NOP phi() at start of @begin
Adding NOP phi() at start of @1
Adding NOP phi() at start of @end
Adding NOP phi() at start of main
Calling convention STACK_CALL adding prepare/execute/finalize for [7] (byte) main::w#0 ← call plus (byte) '0' (byte) main::v#0
Calling convention STACK_CALL replacing param((byte) plus::a) with paramstack(byte,plus::OFFSET_STACK_A)
Calling convention STACK_CALL replacing param((byte) plus::b) with paramstack(byte,plus::OFFSET_STACK_B)
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()
(void()) main()
main: scope:[main] from @1
[4] phi()
to:main::@1
main::@1: scope:[main] from main main::@1
[5] (byte) main::a#2 ← phi( main/(byte) 0 main::@1/(byte) main::a#1 )
[6] (byte) main::v#0 ← (byte) main::a#2 + (byte) 1
[7] callprepare plus (byte) '0' (byte) main::v#0
[8] callexecute plus
[9] (byte) main::w#0 ← callfinalize plus
[10] (byte~) main::$2 ← (byte) main::w#0 + (byte) main::a#2
[11] *((const byte*) SCREEN#0) ← (byte~) main::$2
[12] (byte) main::a#1 ← ++ (byte) main::a#2
[13] if((byte) main::a#1!=(byte) 2) goto main::@1
to:main::@return
main::@return: scope:[main] from main::@1
[14] return
to:@return
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
plus: scope:[plus] from
[15] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A)
[16] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B)
[17] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0
to:plus::@return
plus::@return: scope:[plus] from plus
[18] return (byte) plus::return#0
to:@return
VARIABLE REGISTER WEIGHTS
(byte*) SCREEN
(byte) i
(void()) main()
(byte~) main::$2 22.0
(byte) main::a
(byte) main::a#1 16.5
(byte) main::a#2 6.285714285714286
(byte) main::v
(byte) main::v#0 11.0
(byte) main::w
(byte) main::w#0 11.0
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(byte) plus::a
(byte) plus::a#0 2.0
(byte) plus::b
(byte) plus::b#0 4.0
(byte) plus::return
(byte) plus::return#0 2.0
Initial phi equivalence classes
[ main::a#2 main::a#1 ]
Added variable main::v#0 to zero page equivalence class [ main::v#0 ]
Added variable main::w#0 to zero page equivalence class [ main::w#0 ]
Added variable main::$2 to zero page equivalence class [ main::$2 ]
Added variable plus::a#0 to zero page equivalence class [ plus::a#0 ]
Added variable plus::b#0 to zero page equivalence class [ plus::b#0 ]
Added variable plus::return#0 to zero page equivalence class [ plus::return#0 ]
Complete equivalence classes
[ main::a#2 main::a#1 ]
[ main::v#0 ]
[ main::w#0 ]
[ main::$2 ]
[ plus::a#0 ]
[ plus::b#0 ]
[ plus::return#0 ]
Allocated zp ZP_BYTE:2 [ main::a#2 main::a#1 ]
Allocated zp ZP_BYTE:3 [ main::v#0 ]
Allocated zp ZP_BYTE:4 [ main::w#0 ]
Allocated zp ZP_BYTE:5 [ main::$2 ]
Allocated zp ZP_BYTE:6 [ plus::a#0 ]
Allocated zp ZP_BYTE:7 [ plus::b#0 ]
Allocated zp ZP_BYTE:8 [ plus::return#0 ]
INITIAL ASM
Target platform is c64basic / MOS6502X
// File Comments
// Test a procedure with calling convention stack
// A slightly more complex call
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.const STACK_BASE = $103
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
// @1
b1:
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
.label _2 = 5
.label v = 3
.label w = 4
.label a = 2
// [5] phi from main to main::@1 [phi:main->main::@1]
b1_from_main:
// [5] phi (byte) main::a#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1
lda #0
sta.z a
jmp b1
// [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1]
b1_from_b1:
// [5] phi (byte) main::a#2 = (byte) main::a#1 [phi:main::@1->main::@1#0] -- register_copy
jmp b1
// main::@1
b1:
// [6] (byte) main::v#0 ← (byte) main::a#2 + (byte) 1 -- vbuz1=vbuz2_plus_1
ldy.z a
iny
sty.z v
// [7] callprepare plus (byte) '0' (byte) main::v#0
// [7] callprepare plus (byte) '0' (byte) main::v#0 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [7] callprepare plus (byte) '0' (byte) main::v#0 -- _stackpushbyte_=vbuz1
lda.z v
pha
// [7] callprepare plus (byte) '0' (byte) main::v#0
// [8] callexecute plus -- jsr
jsr plus
// [9] (byte) main::w#0 ← callfinalize plus
// [9] (byte) main::w#0 ← callfinalize plus -- _stackpullbyte_1
pla
// [9] (byte) main::w#0 ← callfinalize plus -- vbuz1=_stackpullbyte_
pla
sta.z w
// [10] (byte~) main::$2 ← (byte) main::w#0 + (byte) main::a#2 -- vbuz1=vbuz2_plus_vbuz3
lda.z w
clc
adc.z a
sta.z _2
// [11] *((const byte*) SCREEN#0) ← (byte~) main::$2 -- _deref_pbuc1=vbuz1
lda.z _2
sta SCREEN
// [12] (byte) main::a#1 ← ++ (byte) main::a#2 -- vbuz1=_inc_vbuz1
inc.z a
// [13] if((byte) main::a#1!=(byte) 2) goto main::@1 -- vbuz1_neq_vbuc1_then_la1
lda #2
cmp.z a
bne b1_from_b1
jmp breturn
// main::@return
breturn:
// [14] return
rts
}
// plus
// plus(byte zeropage(6) a, byte zeropage(7) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 6
.label b = 7
.label return = 8
// [15] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [16] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
sta.z b
// [17] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuz1=vbuz2_plus_vbuz3
lda.z a
clc
adc.z b
sta.z return
jmp breturn
// plus::@return
breturn:
// [18] return (byte) plus::return#0
rts
}
// File Data
REGISTER UPLIFT POTENTIAL REGISTERS
Statement [7] callprepare plus (byte) '0' (byte) main::v#0 [ main::a#2 ] ( main:2 [ main::a#2 ] ) always clobbers reg byte a
Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::a#2 main::a#1 ]
Statement [9] (byte) main::w#0 ← callfinalize plus [ main::a#2 main::w#0 ] ( main:2 [ main::a#2 main::w#0 ] ) always clobbers reg byte a
Statement [10] (byte~) main::$2 ← (byte) main::w#0 + (byte) main::a#2 [ main::a#2 main::$2 ] ( main:2 [ main::a#2 main::$2 ] ) always clobbers reg byte a
Statement [15] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:8 [ main::a#2 plus::a#0 ] ) always clobbers reg byte a reg byte x
Removing always clobbered register reg byte x as potential for zp ZP_BYTE:2 [ main::a#2 main::a#1 ]
Statement [16] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:8 [ main::a#2 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Removing always clobbered register reg byte a as potential for zp ZP_BYTE:6 [ plus::a#0 ]
Removing always clobbered register reg byte x as potential for zp ZP_BYTE:6 [ plus::a#0 ]
Statement [17] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:8 [ main::a#2 plus::return#0 ] ) always clobbers reg byte a
Statement [7] callprepare plus (byte) '0' (byte) main::v#0 [ main::a#2 ] ( main:2 [ main::a#2 ] ) always clobbers reg byte a
Statement [9] (byte) main::w#0 ← callfinalize plus [ main::a#2 main::w#0 ] ( main:2 [ main::a#2 main::w#0 ] ) always clobbers reg byte a
Statement [10] (byte~) main::$2 ← (byte) main::w#0 + (byte) main::a#2 [ main::a#2 main::$2 ] ( main:2 [ main::a#2 main::$2 ] ) always clobbers reg byte a
Statement [15] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) [ plus::a#0 ] ( main:2::plus:8 [ main::a#2 plus::a#0 ] ) always clobbers reg byte a reg byte x
Statement [16] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) [ plus::a#0 plus::b#0 ] ( main:2::plus:8 [ main::a#2 plus::a#0 plus::b#0 ] ) always clobbers reg byte a reg byte x
Statement [17] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 [ plus::return#0 ] ( main:2::plus:8 [ main::a#2 plus::return#0 ] ) always clobbers reg byte a
Potential registers zp ZP_BYTE:2 [ main::a#2 main::a#1 ] : zp ZP_BYTE:2 , reg byte y ,
Potential registers zp ZP_BYTE:3 [ main::v#0 ] : zp ZP_BYTE:3 , reg byte a , reg byte x , reg byte y ,
Potential registers zp ZP_BYTE:4 [ main::w#0 ] : zp ZP_BYTE:4 , reg byte a , reg byte x , reg byte y ,
Potential registers zp ZP_BYTE:5 [ main::$2 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y ,
Potential registers zp ZP_BYTE:6 [ plus::a#0 ] : zp ZP_BYTE:6 , reg byte y ,
Potential registers zp ZP_BYTE:7 [ plus::b#0 ] : zp ZP_BYTE:7 , reg byte a , reg byte x , reg byte y ,
Potential registers zp ZP_BYTE:8 [ plus::return#0 ] : zp ZP_BYTE:8 , reg byte a , reg byte x , reg byte y ,
REGISTER UPLIFT SCOPES
Uplift Scope [main] 22.79: zp ZP_BYTE:2 [ main::a#2 main::a#1 ] 22: zp ZP_BYTE:5 [ main::$2 ] 11: zp ZP_BYTE:3 [ main::v#0 ] 11: zp ZP_BYTE:4 [ main::w#0 ]
Uplift Scope [plus] 4: zp ZP_BYTE:7 [ plus::b#0 ] 2: zp ZP_BYTE:6 [ plus::a#0 ] 2: zp ZP_BYTE:8 [ plus::return#0 ]
Uplift Scope []
Uplifting [main] best 671 combination reg byte y [ main::a#2 main::a#1 ] reg byte a [ main::$2 ] reg byte x [ main::v#0 ] zp ZP_BYTE:4 [ main::w#0 ]
Limited combination testing to 100 combinations of 128 possible.
Uplifting [plus] best 662 combination reg byte a [ plus::b#0 ] zp ZP_BYTE:6 [ plus::a#0 ] reg byte a [ plus::return#0 ]
Uplifting [] best 662 combination
Attempting to uplift remaining variables inzp ZP_BYTE:4 [ main::w#0 ]
Uplifting [main] best 662 combination zp ZP_BYTE:4 [ main::w#0 ]
Attempting to uplift remaining variables inzp ZP_BYTE:6 [ plus::a#0 ]
Uplifting [plus] best 662 combination zp ZP_BYTE:6 [ plus::a#0 ]
Coalescing zero page register [ zp ZP_BYTE:6 [ plus::a#0 ] ] with [ zp ZP_BYTE:4 [ main::w#0 ] ]
Allocated (was zp ZP_BYTE:6) zp ZP_BYTE:2 [ plus::a#0 main::w#0 ]
ASSEMBLER BEFORE OPTIMIZATION
// File Comments
// Test a procedure with calling convention stack
// A slightly more complex call
// Upstart
.pc = $801 "Basic"
:BasicUpstart(bbegin)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.const STACK_BASE = $103
// @begin
bbegin:
// [1] phi from @begin to @1 [phi:@begin->@1]
b1_from_bbegin:
jmp b1
// @1
b1:
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
main_from_b1:
jsr main
// [3] phi from @1 to @end [phi:@1->@end]
bend_from_b1:
jmp bend
// @end
bend:
// main
main: {
.label w = 2
// [5] phi from main to main::@1 [phi:main->main::@1]
b1_from_main:
// [5] phi (byte) main::a#2 = (byte) 0 [phi:main->main::@1#0] -- vbuyy=vbuc1
ldy #0
jmp b1
// [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1]
b1_from_b1:
// [5] phi (byte) main::a#2 = (byte) main::a#1 [phi:main::@1->main::@1#0] -- register_copy
jmp b1
// main::@1
b1:
// [6] (byte) main::v#0 ← (byte) main::a#2 + (byte) 1 -- vbuxx=vbuyy_plus_1
tya
tax
inx
// [7] callprepare plus (byte) '0' (byte) main::v#0
// [7] callprepare plus (byte) '0' (byte) main::v#0 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [7] callprepare plus (byte) '0' (byte) main::v#0 -- _stackpushbyte_=vbuxx
txa
pha
// [7] callprepare plus (byte) '0' (byte) main::v#0
// [8] callexecute plus -- jsr
jsr plus
// [9] (byte) main::w#0 ← callfinalize plus
// [9] (byte) main::w#0 ← callfinalize plus -- _stackpullbyte_1
pla
// [9] (byte) main::w#0 ← callfinalize plus -- vbuz1=_stackpullbyte_
pla
sta.z w
// [10] (byte~) main::$2 ← (byte) main::w#0 + (byte) main::a#2 -- vbuaa=vbuz1_plus_vbuyy
tya
clc
adc.z w
// [11] *((const byte*) SCREEN#0) ← (byte~) main::$2 -- _deref_pbuc1=vbuaa
sta SCREEN
// [12] (byte) main::a#1 ← ++ (byte) main::a#2 -- vbuyy=_inc_vbuyy
iny
// [13] if((byte) main::a#1!=(byte) 2) goto main::@1 -- vbuyy_neq_vbuc1_then_la1
cpy #2
bne b1_from_b1
jmp breturn
// main::@return
breturn:
// [14] return
rts
}
// plus
// plus(byte zeropage(2) a, byte register(A) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
// [15] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [16] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
// [17] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
jmp breturn
// plus::@return
breturn:
// [18] return (byte) plus::return#0
rts
}
// File Data
ASSEMBLER OPTIMIZATIONS
Removing instruction jmp b1
Removing instruction jmp bend
Removing instruction jmp b1
Removing instruction jmp breturn
Removing instruction jmp breturn
Succesful ASM optimization Pass5NextJumpElimination
Replacing label b1_from_b1 with b1
Removing instruction b1_from_bbegin:
Removing instruction b1:
Removing instruction main_from_b1:
Removing instruction bend_from_b1:
Removing instruction b1_from_b1:
Succesful ASM optimization Pass5RedundantLabelElimination
Removing instruction bend:
Removing instruction b1_from_main:
Removing instruction breturn:
Removing instruction breturn:
Succesful ASM optimization Pass5UnusedLabelElimination
Updating BasicUpstart to call main directly
Removing instruction jsr main
Succesful ASM optimization Pass5SkipBegin
Removing instruction jmp b1
Succesful ASM optimization Pass5NextJumpElimination
Removing instruction bbegin:
Succesful ASM optimization Pass5UnusedLabelElimination
FINAL SYMBOL TABLE
(label) @1
(label) @begin
(label) @end
(byte*) SCREEN
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(byte) i
(void()) main()
(byte~) main::$2 reg byte a 22.0
(label) main::@1
(label) main::@return
(byte) main::a
(byte) main::a#1 reg byte y 16.5
(byte) main::a#2 reg byte y 6.285714285714286
(byte) main::v
(byte) main::v#0 reg byte x 11.0
(byte) main::w
(byte) main::w#0 w zp ZP_BYTE:2 11.0
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(label) plus::@return
(const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0
(const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 1
(byte) plus::a
(byte) plus::a#0 a zp ZP_BYTE:2 2.0
(byte) plus::b
(byte) plus::b#0 reg byte a 4.0
(byte) plus::return
(byte) plus::return#0 reg byte a 2.0
reg byte y [ main::a#2 main::a#1 ]
reg byte x [ main::v#0 ]
reg byte a [ main::$2 ]
zp ZP_BYTE:2 [ plus::a#0 main::w#0 ]
reg byte a [ plus::b#0 ]
reg byte a [ plus::return#0 ]
FINAL ASSEMBLER
Score: 557
// File Comments
// Test a procedure with calling convention stack
// A slightly more complex call
// Upstart
.pc = $801 "Basic"
:BasicUpstart(main)
.pc = $80d "Program"
// Global Constants & labels
.label SCREEN = $400
.const STACK_BASE = $103
// @begin
// [1] phi from @begin to @1 [phi:@begin->@1]
// @1
// [2] call main
// [4] phi from @1 to main [phi:@1->main]
// [3] phi from @1 to @end [phi:@1->@end]
// @end
// main
main: {
.label w = 2
// [5] phi from main to main::@1 [phi:main->main::@1]
// [5] phi (byte) main::a#2 = (byte) 0 [phi:main->main::@1#0] -- vbuyy=vbuc1
ldy #0
// [5] phi from main::@1 to main::@1 [phi:main::@1->main::@1]
// [5] phi (byte) main::a#2 = (byte) main::a#1 [phi:main::@1->main::@1#0] -- register_copy
// main::@1
b1:
// v = a+1
// [6] (byte) main::v#0 ← (byte) main::a#2 + (byte) 1 -- vbuxx=vbuyy_plus_1
tya
tax
inx
// w = plus('0', v)
// [7] callprepare plus (byte) '0' (byte) main::v#0
// [7] callprepare plus (byte) '0' (byte) main::v#0 -- _stackpushbyte_=vbuc1
lda #'0'
pha
// [7] callprepare plus (byte) '0' (byte) main::v#0 -- _stackpushbyte_=vbuxx
txa
pha
// [7] callprepare plus (byte) '0' (byte) main::v#0
// [8] callexecute plus -- jsr
jsr plus
// [9] (byte) main::w#0 ← callfinalize plus
// [9] (byte) main::w#0 ← callfinalize plus -- _stackpullbyte_1
pla
// [9] (byte) main::w#0 ← callfinalize plus -- vbuz1=_stackpullbyte_
pla
sta.z w
// w+a
// [10] (byte~) main::$2 ← (byte) main::w#0 + (byte) main::a#2 -- vbuaa=vbuz1_plus_vbuyy
tya
clc
adc.z w
// SCREEN[i] = w+a
// [11] *((const byte*) SCREEN#0) ← (byte~) main::$2 -- _deref_pbuc1=vbuaa
sta SCREEN
// for(char a:0..1)
// [12] (byte) main::a#1 ← ++ (byte) main::a#2 -- vbuyy=_inc_vbuyy
iny
// [13] if((byte) main::a#1!=(byte) 2) goto main::@1 -- vbuyy_neq_vbuc1_then_la1
cpy #2
bne b1
// main::@return
// }
// [14] return
rts
}
// plus
// plus(byte zeropage(2) a, byte register(A) b)
plus: {
.const OFFSET_STACK_A = 0
.const OFFSET_STACK_B = 1
.label a = 2
// [15] (byte) plus::a#0 ← paramstack(byte,plus::OFFSET_STACK_A) -- vbuz1=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_A,x
sta.z a
// [16] (byte) plus::b#0 ← paramstack(byte,plus::OFFSET_STACK_B) -- vbuaa=_stackgetbyte_vbuc1
tsx
lda STACK_BASE+OFFSET_STACK_B,x
// return a+b;
// [17] (byte) plus::return#0 ← (byte) plus::a#0 + (byte) plus::b#0 -- vbuaa=vbuz1_plus_vbuaa
clc
adc.z a
// plus::@return
// }
// [18] return (byte) plus::return#0
rts
}
// File Data

@ -0,0 +1,35 @@
(label) @1
(label) @begin
(label) @end
(byte*) SCREEN
(const byte*) SCREEN#0 SCREEN = (byte*) 1024
(const word) STACK_BASE STACK_BASE = (word) $103
(byte) i
(void()) main()
(byte~) main::$2 reg byte a 22.0
(label) main::@1
(label) main::@return
(byte) main::a
(byte) main::a#1 reg byte y 16.5
(byte) main::a#2 reg byte y 6.285714285714286
(byte) main::v
(byte) main::v#0 reg byte x 11.0
(byte) main::w
(byte) main::w#0 w zp ZP_BYTE:2 11.0
__stackcall (byte()) plus((byte) plus::a , (byte) plus::b)
(label) plus::@return
(const byte) plus::OFFSET_STACK_A OFFSET_STACK_A = (byte) 0
(const byte) plus::OFFSET_STACK_B OFFSET_STACK_B = (byte) 1
(byte) plus::a
(byte) plus::a#0 a zp ZP_BYTE:2 2.0
(byte) plus::b
(byte) plus::b#0 reg byte a 4.0
(byte) plus::return
(byte) plus::return#0 reg byte a 2.0
reg byte y [ main::a#2 main::a#1 ]
reg byte x [ main::v#0 ]
reg byte a [ main::$2 ]
zp ZP_BYTE:2 [ plus::a#0 main::w#0 ]
reg byte a [ plus::b#0 ]
reg byte a [ plus::return#0 ]