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. #316
This commit is contained in:
parent
bcddd821ff
commit
dc08f86302
src
main
fragment/mos6502-common
java/dk/camelot64/kickc
Compiler.java
fragment
model
CallGraph.javaControlFlowBlock.javaControlFlowGraph.javaControlFlowGraphBaseVisitor.javaControlFlowGraphCopyVisitor.javaProgram.java
iterator
statements
StatementBase.javaStatementCall.javaStatementCallExecute.javaStatementCallFinalize.javaStatementCallPrepare.javaStatementCalling.java
values
passes
Pass1AssertInterrupts.javaPass1AssertProcedureCallParameters.javaPass1AssertUsedVars.javaPass1EliminateUncalledProcedures.javaPass1ExtractInlineStrings.javaPass1ModifiedVarsAnalysis.javaPass1ProcedureCallParameters.javaPass2AssertBlocks.javaPass2EliminateUnusedBlocks.javaPass3AddNopBeforeCallOns.javaPass4CodeGeneration.javaPass4LiveRangeEquivalenceClassesFinalize.javaPass5SkipBegin.javaPassNCallingConventionStack.javaPassNEliminateUnusedVars.javaPassNTypeInference.java
calcs
test
java/dk/camelot64/kickc/test
kc
ref
procedure-callingconvention-stack-0.asmprocedure-callingconvention-stack-0.cfgprocedure-callingconvention-stack-0.logprocedure-callingconvention-stack-0.symprocedure-callingconvention-stack-1.asmprocedure-callingconvention-stack-1.cfgprocedure-callingconvention-stack-1.logprocedure-callingconvention-stack-1.symprocedure-callingconvention-stack-2.asmprocedure-callingconvention-stack-2.cfgprocedure-callingconvention-stack-2.logprocedure-callingconvention-stack-2.symprocedure-callingconvention-stack-3.asmprocedure-callingconvention-stack-3.cfgprocedure-callingconvention-stack-3.logprocedure-callingconvention-stack-3.symprocedure-callingconvention-stack-4.asmprocedure-callingconvention-stack-4.cfgprocedure-callingconvention-stack-4.logprocedure-callingconvention-stack-4.sym
@ -0,0 +1 @@
|
||||
pla
|
@ -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
|
||||
|
14
src/test/kc/procedure-callingconvention-stack-3.kc
Normal file
14
src/test/kc/procedure-callingconvention-stack-3.kc
Normal file
@ -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;
|
||||
}
|
19
src/test/kc/procedure-callingconvention-stack-4.kc
Normal file
19
src/test/kc/procedure-callingconvention-stack-4.kc
Normal file
@ -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 ]
|
||||
|
54
src/test/ref/procedure-callingconvention-stack-3.asm
Normal file
54
src/test/ref/procedure-callingconvention-stack-3.asm
Normal file
@ -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
|
||||
}
|
31
src/test/ref/procedure-callingconvention-stack-3.cfg
Normal file
31
src/test/ref/procedure-callingconvention-stack-3.cfg
Normal file
@ -0,0 +1,31 @@
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
|
||||
(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
|
530
src/test/ref/procedure-callingconvention-stack-3.log
Normal file
530
src/test/ref/procedure-callingconvention-stack-3.log
Normal file
@ -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
|
||||
|
22
src/test/ref/procedure-callingconvention-stack-3.sym
Normal file
22
src/test/ref/procedure-callingconvention-stack-3.sym
Normal file
@ -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 ]
|
45
src/test/ref/procedure-callingconvention-stack-4.asm
Normal file
45
src/test/ref/procedure-callingconvention-stack-4.asm
Normal file
@ -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
|
||||
}
|
38
src/test/ref/procedure-callingconvention-stack-4.cfg
Normal file
38
src/test/ref/procedure-callingconvention-stack-4.cfg
Normal file
@ -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
|
678
src/test/ref/procedure-callingconvention-stack-4.log
Normal file
678
src/test/ref/procedure-callingconvention-stack-4.log
Normal file
@ -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
|
||||
|
35
src/test/ref/procedure-callingconvention-stack-4.sym
Normal file
35
src/test/ref/procedure-callingconvention-stack-4.sym
Normal file
@ -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 ]
|
Loading…
x
Reference in New Issue
Block a user