mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-12-30 15:31:17 +00:00
Function pointers now working both when constant and variable (in simple noargs non-scope-crossing cases with no local variables in the referenced functions.)
This commit is contained in:
parent
3434a695d2
commit
e258b9593d
1
src/main/fragment/vbuaa=_byte_pprz1.asm
Normal file
1
src/main/fragment/vbuaa=_byte_pprz1.asm
Normal file
@ -0,0 +1 @@
|
||||
lda {z1}
|
@ -229,7 +229,7 @@ public class AsmFormat {
|
||||
* @param boundVar The variable
|
||||
* @return The ASM parameter to use in the ASM code
|
||||
*/
|
||||
static String getAsmParamName(Variable boundVar, ScopeRef codeScopeRef) {
|
||||
public static String getAsmParamName(Variable boundVar, ScopeRef codeScopeRef) {
|
||||
ScopeRef varScopeRef = boundVar.getScope().getRef();
|
||||
String asmName = boundVar.getAsmName() == null ? boundVar.getLocalName() : boundVar.getAsmName();
|
||||
return getAsmParamName(varScopeRef, asmName, codeScopeRef);
|
||||
|
@ -33,6 +33,8 @@ public class ControlFlowGraphBaseVisitor<T> {
|
||||
return visitJumpTarget((StatementLabel) statement);
|
||||
} else if(statement instanceof StatementCall) {
|
||||
return visitCall((StatementCall) statement);
|
||||
} else if(statement instanceof StatementCallPointer) {
|
||||
return visitCallPointer((StatementCallPointer) statement);
|
||||
} else if(statement instanceof StatementPhiBlock) {
|
||||
return visitPhiBlock((StatementPhiBlock) statement);
|
||||
} else if(statement instanceof StatementReturn) {
|
||||
@ -86,6 +88,10 @@ public class ControlFlowGraphBaseVisitor<T> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public T visitCallPointer(StatementCallPointer call) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public T visitAsm(StatementAsm asm) {
|
||||
return null;
|
||||
}
|
||||
|
@ -173,6 +173,14 @@ public class ControlFlowGraphCopyVisitor extends ControlFlowGraphBaseVisitor<Obj
|
||||
return new StatementCall(lValue, procedureName, parameters, orig.getSource(), orig.getComments());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object visitCallPointer(StatementCallPointer orig) {
|
||||
LValue lValue = orig.getlValue();
|
||||
RValue procedure = orig.getProcedure();
|
||||
List<RValue> parameters = orig.getParameters();
|
||||
return new StatementCallPointer(lValue, procedure, parameters, orig.getSource(), orig.getComments());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatementProcedureBegin visitProcedureBegin(StatementProcedureBegin orig) {
|
||||
return new StatementProcedureBegin(orig.getProcedure(), orig.getSource(), orig.getComments());
|
||||
|
@ -542,6 +542,44 @@ public abstract class ProgramValue {
|
||||
}
|
||||
}
|
||||
|
||||
public static class CallPointerProcedure extends ProgramValue {
|
||||
private final StatementCallPointer call;
|
||||
|
||||
CallPointerProcedure(StatementCallPointer call) {
|
||||
this.call = call;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get() {
|
||||
return call.getProcedure();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(Value value) {
|
||||
call.setProcedure((RValue) value);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CallPointerParameter extends ProgramValue {
|
||||
private final StatementCallPointer call;
|
||||
private final int i;
|
||||
|
||||
CallPointerParameter(StatementCallPointer 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);
|
||||
}
|
||||
}
|
||||
|
||||
public static class CondRValue1 extends ProgramValue {
|
||||
private final StatementConditionalJump statement;
|
||||
|
||||
|
@ -108,6 +108,16 @@ public class ProgramValueIterator {
|
||||
}
|
||||
}
|
||||
execute(new ProgramValue.LValue((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);
|
||||
if(call.getParameters() != null) {
|
||||
int size = call.getParameters().size();
|
||||
for(int i = 0; i < size; i++) {
|
||||
execute(new ProgramValue.CallPointerParameter(call, i), handler, statement, statementsIt, block);
|
||||
}
|
||||
}
|
||||
execute(new ProgramValue.LValue((StatementLValue) statement), handler, statement, statementsIt, block);
|
||||
} else if(statement instanceof StatementConditionalJump) {
|
||||
execute(new ProgramValue.CondRValue1((StatementConditionalJump) statement), handler, statement, statementsIt, block);
|
||||
execute(new ProgramValue.CondRValue2((StatementConditionalJump) statement), handler, statement, statementsIt, block);
|
||||
|
@ -15,12 +15,13 @@ import java.util.List;
|
||||
*/
|
||||
public class StatementCall extends StatementBase implements StatementLValue {
|
||||
|
||||
/**
|
||||
* The variable being assigned a value by the call. Can be null.
|
||||
*/
|
||||
/** The variable being assigned a value by the call. Can be null. */
|
||||
private LValue lValue;
|
||||
/** The name of the procedure called (if the call is simple). Null if the called procedure is calculated through an expression. */
|
||||
private String procedureName;
|
||||
/** The procedure called (if the call is simple). Null if the called procedure is calculated through an expression. */
|
||||
private ProcedureRef procedure;
|
||||
/** The parameter values passed to the procedure. */
|
||||
private List<RValue> parameters;
|
||||
|
||||
public StatementCall(LValue lValue, String procedureName, List<RValue> parameters, StatementSource source, List<Comment> comments) {
|
||||
|
@ -0,0 +1,103 @@
|
||||
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 dk.camelot64.kickc.model.values.RValue;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Call of a procedure where the called procedure is not directly named, but the result of an expression.
|
||||
*/
|
||||
public class StatementCallPointer extends StatementBase implements StatementLValue {
|
||||
|
||||
/** The variable being assigned a value by the call. Can be null. */
|
||||
private LValue lValue;
|
||||
/** The expression calculating the procedure being called. */
|
||||
private RValue procedure;
|
||||
/** The parameter values passed to the procedure. */
|
||||
private List<RValue> parameters;
|
||||
|
||||
public StatementCallPointer(LValue lValue, RValue procedure, List<RValue> parameters, StatementSource source, List<Comment> comments) {
|
||||
super(null, source, comments);
|
||||
this.lValue = lValue;
|
||||
this.procedure = procedure;
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
public RValue getProcedure() {
|
||||
return procedure;
|
||||
}
|
||||
|
||||
public void setProcedure(RValue procedure) {
|
||||
this.procedure = procedure;
|
||||
}
|
||||
|
||||
public LValue getlValue() {
|
||||
return lValue;
|
||||
}
|
||||
|
||||
public void setlValue(LValue lValue) {
|
||||
this.lValue = lValue;
|
||||
}
|
||||
|
||||
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());
|
||||
if(lValue != null) {
|
||||
res.append(lValue.toString(program));
|
||||
res.append(" ← ");
|
||||
}
|
||||
res.append("call ");
|
||||
res.append(procedure.toString(program) + " ");
|
||||
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;
|
||||
StatementCallPointer that = (StatementCallPointer) o;
|
||||
return Objects.equals(lValue, that.lValue) &&
|
||||
Objects.equals(procedure, that.procedure) &&
|
||||
Objects.equals(parameters, that.parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(super.hashCode(), lValue, procedure, parameters);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import dk.camelot64.kickc.model.operators.OperatorBinary;
|
||||
import dk.camelot64.kickc.model.operators.OperatorUnary;
|
||||
import dk.camelot64.kickc.model.statements.StatementAssignment;
|
||||
import dk.camelot64.kickc.model.statements.StatementCall;
|
||||
import dk.camelot64.kickc.model.statements.StatementCallPointer;
|
||||
import dk.camelot64.kickc.model.statements.StatementLValue;
|
||||
import dk.camelot64.kickc.model.symbols.*;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
@ -327,6 +328,21 @@ public class SymbolTypeInference {
|
||||
}
|
||||
}
|
||||
|
||||
public static void inferCallPointerLValue(Program program, StatementCallPointer call, boolean reinfer) {
|
||||
ProgramScope programScope = program.getScope();
|
||||
LValue lValue = call.getlValue();
|
||||
if(lValue instanceof VariableRef) {
|
||||
Variable symbol = programScope.getVariable((VariableRef) lValue);
|
||||
if(SymbolType.VAR.equals(symbol.getType()) || (reinfer && symbol.isInferredType())) {
|
||||
SymbolType procedureType = inferType(programScope, call.getProcedure());
|
||||
if(procedureType instanceof SymbolTypeProcedure) {
|
||||
SymbolType returnType = ((SymbolTypeProcedure) procedureType).getReturnType();
|
||||
setInferedType(program, call, symbol, returnType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void inferAssignmentLValue(Program program, StatementAssignment assignment, boolean reinfer) {
|
||||
ProgramScope programScope = program.getScope();
|
||||
LValue lValue = assignment.getlValue();
|
||||
@ -377,6 +393,8 @@ public class SymbolTypeInference {
|
||||
inferAssignmentLValue(program, (StatementAssignment) statementLValue, reinfer);
|
||||
} else if(statementLValue instanceof StatementCall) {
|
||||
inferCallLValue(program, (StatementCall) statementLValue, reinfer);
|
||||
} else if(statementLValue instanceof StatementCallPointer) {
|
||||
inferCallPointerLValue(program, (StatementCallPointer) statementLValue, reinfer);
|
||||
} else {
|
||||
throw new RuntimeException("LValue statement not implemented " + statementLValue);
|
||||
}
|
||||
|
@ -1120,13 +1120,6 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
@Override
|
||||
public Object visitExprCall(KickCParser.ExprCallContext ctx) {
|
||||
|
||||
String procedureName;
|
||||
if(ctx.expr() instanceof KickCParser.ExprIdContext) {
|
||||
procedureName = ctx.expr().getText();
|
||||
} else {
|
||||
throw new CompileError("Function pointer calls not supported.", new StatementSource(ctx));
|
||||
}
|
||||
|
||||
List<RValue> parameters;
|
||||
KickCParser.ParameterListContext parameterList = ctx.parameterList();
|
||||
if(parameterList != null) {
|
||||
@ -1136,7 +1129,15 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
|
||||
}
|
||||
VariableIntermediate tmpVar = getCurrentScope().addVariableIntermediate();
|
||||
VariableRef tmpVarRef = tmpVar.getRef();
|
||||
sequence.addStatement(new StatementCall(tmpVarRef, procedureName, parameters, new StatementSource(ctx), ensureUnusedComments(getCommentsSymbol(ctx))));
|
||||
|
||||
String procedureName;
|
||||
if(ctx.expr() instanceof KickCParser.ExprIdContext) {
|
||||
procedureName = ctx.expr().getText();
|
||||
sequence.addStatement(new StatementCall(tmpVarRef, procedureName, parameters, new StatementSource(ctx), ensureUnusedComments(getCommentsSymbol(ctx))));
|
||||
} else {
|
||||
RValue procedurePointer = (RValue) this.visit(ctx.expr());
|
||||
sequence.addStatement(new StatementCallPointer(tmpVarRef, procedurePointer, parameters, new StatementSource(ctx), ensureUnusedComments(getCommentsSymbol(ctx))));
|
||||
}
|
||||
return tmpVarRef;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ 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.StatementCallPointer;
|
||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||
import dk.camelot64.kickc.model.symbols.Scope;
|
||||
import dk.camelot64.kickc.model.types.SymbolTypeInference;
|
||||
@ -44,7 +45,10 @@ public class Pass1TypeInference extends Pass1Base {
|
||||
throw new CompileError("Wrong number of parameters in call. Expected " + procedure.getParameters().size() + ". " + statement.toString(), statement.getSource());
|
||||
}
|
||||
SymbolTypeInference.inferCallLValue(getProgram(), (StatementCall) statement, false);
|
||||
} else if(statement instanceof StatementCallPointer) {
|
||||
SymbolTypeInference.inferCallPointerLValue(getProgram(), (StatementCallPointer) statement, false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -19,7 +19,7 @@ public class Pass2AssertSymbols extends Pass2SsaAssertion {
|
||||
SymbolFinder symbolFinder = new SymbolFinder(getScope());
|
||||
symbolFinder.visitGraph(getGraph());
|
||||
HashSet<Symbol> codeSymbols = symbolFinder.getSymbols();
|
||||
// Check that all symbols found in the code is also oin the symbol tabel
|
||||
// Check that all symbols found in the code is also in the symbol table
|
||||
for(Symbol codeSymbol : codeSymbols) {
|
||||
if(codeSymbol.getFullName().equals(SymbolRef.PROCEXIT_BLOCK_NAME)) continue;
|
||||
Symbol tableSymbol = getScope().getSymbol(codeSymbol.getFullName());
|
||||
@ -155,6 +155,18 @@ public class Pass2AssertSymbols extends Pass2SsaAssertion {
|
||||
return super.visitCall(call);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitCallPointer(StatementCallPointer call) {
|
||||
addSymbol(call.getlValue());
|
||||
addSymbol(call.getProcedure());
|
||||
if(call.getParameters() != null) {
|
||||
for(RValue param : call.getParameters()) {
|
||||
addSymbol(param);
|
||||
}
|
||||
}
|
||||
return super.visitCallPointer(call);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void visitPhiBlock(StatementPhiBlock phi) {
|
||||
for(StatementPhiBlock.PhiVariable phiVariable : phi.getPhiVariables()) {
|
||||
|
@ -64,10 +64,7 @@ public class Pass4CodeGeneration {
|
||||
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||
if(!block.getScope().equals(currentScope)) {
|
||||
// The current block is in a different scope. End the old scope.
|
||||
if(!ScopeRef.ROOT.equals(currentScope)) {
|
||||
addData(asm, currentScope);
|
||||
asm.addScopeEnd();
|
||||
}
|
||||
generateScopeEnding(asm, currentScope);
|
||||
currentScope = block.getScope();
|
||||
asm.startSegment(currentScope, null, block.getLabel().getFullName());
|
||||
// Add any procedure comments
|
||||
@ -121,10 +118,7 @@ public class Pass4CodeGeneration {
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!ScopeRef.ROOT.equals(currentScope)) {
|
||||
addData(asm, currentScope);
|
||||
asm.addScopeEnd();
|
||||
}
|
||||
generateScopeEnding(asm, currentScope);
|
||||
addData(asm, ScopeRef.ROOT);
|
||||
// Add all absolutely placed inline KickAsm
|
||||
for(ControlFlowBlock block : getGraph().getAllBlocks()) {
|
||||
@ -143,10 +137,32 @@ public class Pass4CodeGeneration {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
program.setAsm(asm);
|
||||
}
|
||||
|
||||
/**
|
||||
* ASM names of variables being used for indirect calls in the current scope (procedure).
|
||||
* These will all be added as indirect JMP's at the end of the procedure scope.
|
||||
*/
|
||||
private List<String> indirectCallAsmNames = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Generate the end of a scope
|
||||
* @param asm The assembler program being generated
|
||||
* @param currentScope The current scope, which is ending here
|
||||
*/
|
||||
private void generateScopeEnding(AsmProgram asm, ScopeRef currentScope) {
|
||||
if(!ScopeRef.ROOT.equals(currentScope)) {
|
||||
for(String indirectCallAsmName : indirectCallAsmNames) {
|
||||
asm.addLabel("bi_"+indirectCallAsmName);
|
||||
asm.addInstruction("jmp", AsmAddressingMode.IND, indirectCallAsmName, false);
|
||||
}
|
||||
indirectCallAsmNames = new ArrayList<>();
|
||||
addData(asm, currentScope);
|
||||
asm.addScopeEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a comment that describes the procedure signature and parameter transfer
|
||||
* @param asm The assembler program being generated
|
||||
@ -563,9 +579,29 @@ public class Pass4CodeGeneration {
|
||||
if(statementKasm.getLocation() == null) {
|
||||
addKickAsm(asm, statementKasm);
|
||||
}
|
||||
if(statementKasm.getDeclaredClobber()!=null) {
|
||||
if(statementKasm.getDeclaredClobber() != null) {
|
||||
asm.getCurrentSegment().setClobberOverwrite(statementKasm.getDeclaredClobber());
|
||||
}
|
||||
} else if(statement instanceof StatementCallPointer) {
|
||||
StatementCallPointer callPointer = (StatementCallPointer) statement;
|
||||
RValue procedure = callPointer.getProcedure();
|
||||
boolean supported = false;
|
||||
if(procedure instanceof PointerDereferenceSimple) {
|
||||
RValue pointer = ((PointerDereferenceSimple) procedure).getPointer();
|
||||
if(pointer instanceof ConstantValue) {
|
||||
asm.addInstruction("jsr", AsmAddressingMode.ABS, AsmFormat.getAsmConstant(program, (ConstantValue) pointer, 99, block.getScope()), false);
|
||||
supported = true;
|
||||
} else if(pointer instanceof VariableRef) {
|
||||
Variable variable = getScope().getVariable((VariableRef) pointer);
|
||||
String varAsmName = AsmFormat.getAsmParamName(variable, block.getScope());
|
||||
indirectCallAsmNames.add(varAsmName);
|
||||
asm.addInstruction("jsr", AsmAddressingMode.ABS, "bi_"+varAsmName,false);
|
||||
supported = true;
|
||||
}
|
||||
}
|
||||
if(!supported) {
|
||||
throw new RuntimeException("Call Pointer not supported " + statement);
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Statement not supported " + statement);
|
||||
}
|
||||
|
@ -3,10 +3,7 @@ package dk.camelot64.kickc.passes;
|
||||
import dk.camelot64.kickc.model.ControlFlowBlock;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.VariableReferenceInfos;
|
||||
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.statements.*;
|
||||
import dk.camelot64.kickc.model.symbols.ConstantVar;
|
||||
import dk.camelot64.kickc.model.symbols.Variable;
|
||||
import dk.camelot64.kickc.model.values.LValue;
|
||||
@ -64,6 +61,20 @@ public class PassNEliminateUnusedVars extends Pass2SsaOptimization {
|
||||
call.setlValue(null);
|
||||
modified = true;
|
||||
}
|
||||
} else if(statement instanceof StatementCallPointer) {
|
||||
StatementCallPointer call = (StatementCallPointer) statement;
|
||||
LValue lValue = call.getlValue();
|
||||
if(lValue instanceof VariableRef && referenceInfos.isUnused((VariableRef) lValue) && !Pass2ConstantIdentification.isAddressOfUsed((VariableRef) lValue, getProgram())) {
|
||||
if(getLog().isVerbosePass1CreateSsa() || getLog().isVerboseSSAOptimize()) {
|
||||
getLog().append("Eliminating unused variable - keeping the call " + lValue.toString(getProgram()));
|
||||
}
|
||||
Variable variable = getScope().getVariable((VariableRef) lValue);
|
||||
if(variable != null) {
|
||||
variable.getScope().remove(variable);
|
||||
}
|
||||
call.setlValue(null);
|
||||
modified = true;
|
||||
}
|
||||
} else if(statement instanceof StatementPhiBlock) {
|
||||
StatementPhiBlock statementPhi = (StatementPhiBlock) statement;
|
||||
ListIterator<StatementPhiBlock.PhiVariable> phiVarIt = statementPhi.getPhiVariables().listIterator();
|
||||
|
@ -5,10 +5,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.StatementAssignment;
|
||||
import dk.camelot64.kickc.model.statements.StatementCall;
|
||||
import dk.camelot64.kickc.model.statements.StatementPhiBlock;
|
||||
import dk.camelot64.kickc.model.statements.*;
|
||||
import dk.camelot64.kickc.model.symbols.SymbolVariable;
|
||||
import dk.camelot64.kickc.model.values.*;
|
||||
|
||||
@ -223,6 +220,12 @@ public class PassNVariableReferenceInfos extends Pass2SsaOptimization {
|
||||
defined.add((VariableRef) ((StatementCall) stmt).getlValue());
|
||||
}
|
||||
return defined;
|
||||
} else if(stmt instanceof StatementCallPointer) {
|
||||
List<VariableRef> defined = new ArrayList<>();
|
||||
if(((StatementCallPointer) stmt).getlValue() instanceof VariableRef) {
|
||||
defined.add((VariableRef) ((StatementCallPointer) stmt).getlValue());
|
||||
}
|
||||
return defined;
|
||||
}
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
@ -32,6 +32,21 @@ public class TestPrograms {
|
||||
public TestPrograms() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionPointerNoargCall2() throws IOException, URISyntaxException {
|
||||
compileAndCompare("function-pointer-noarg-call-2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionPointerNoargCall() throws IOException, URISyntaxException {
|
||||
compileAndCompare("function-pointer-noarg-call");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionPointerReturn() throws IOException, URISyntaxException {
|
||||
compileAndCompare("function-pointer-return");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionPointerNoarg3() throws IOException, URISyntaxException {
|
||||
compileAndCompare("function-pointer-noarg-3");
|
||||
|
28
src/test/kc/function-pointer-noarg-call-2.kc
Normal file
28
src/test/kc/function-pointer-noarg-call-2.kc
Normal file
@ -0,0 +1,28 @@
|
||||
// Tests creating, assigning and calling pointers to non-args no-return functions
|
||||
|
||||
void main() {
|
||||
const byte* SCREEN = $400;
|
||||
|
||||
void()* f;
|
||||
|
||||
byte i = 0;
|
||||
while(true) {
|
||||
++i;
|
||||
if((i&1)==0) {
|
||||
f = &fn1;
|
||||
} else {
|
||||
f = &fn2;
|
||||
}
|
||||
(*f)();
|
||||
}
|
||||
}
|
||||
|
||||
void fn1() {
|
||||
const byte* BORDERCOL = $d020;
|
||||
(*BORDERCOL)++;
|
||||
}
|
||||
|
||||
void fn2() {
|
||||
const byte* BGCOL = $d021;
|
||||
(*BGCOL)++;
|
||||
}
|
11
src/test/kc/function-pointer-noarg-call.kc
Normal file
11
src/test/kc/function-pointer-noarg-call.kc
Normal file
@ -0,0 +1,11 @@
|
||||
// Tests creating, assigning and calling pointers to non-args no-return functions
|
||||
|
||||
void main() {
|
||||
void()* f = &fn1;
|
||||
(*f)();
|
||||
}
|
||||
|
||||
void fn1() {
|
||||
const byte* BORDERCOL = $d020;
|
||||
(*BORDERCOL)++;
|
||||
}
|
30
src/test/kc/function-pointer-return.kc
Normal file
30
src/test/kc/function-pointer-return.kc
Normal file
@ -0,0 +1,30 @@
|
||||
// Tests creating and assigning pointers to non-args return with function value
|
||||
|
||||
void main() {
|
||||
const byte* SCREEN = $400;
|
||||
|
||||
byte()* f;
|
||||
|
||||
byte i = 0;
|
||||
while(true) {
|
||||
++i;
|
||||
if((i&1)==0) {
|
||||
f = &fn1;
|
||||
} else {
|
||||
f = &fn2;
|
||||
}
|
||||
SCREEN[0] = (byte)f;
|
||||
}
|
||||
}
|
||||
|
||||
byte fn1() {
|
||||
const byte* BORDERCOL = $d020;
|
||||
(*BORDERCOL)++;
|
||||
return *BORDERCOL;
|
||||
}
|
||||
|
||||
byte fn2() {
|
||||
const byte* BGCOL = $d021;
|
||||
(*BGCOL)++;
|
||||
return *BGCOL;
|
||||
}
|
39
src/test/ref/function-pointer-noarg-call-2.asm
Normal file
39
src/test/ref/function-pointer-noarg-call-2.asm
Normal file
@ -0,0 +1,39 @@
|
||||
// Tests creating, assigning and calling pointers to non-args no-return functions
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
main: {
|
||||
.label f = 2
|
||||
ldx #0
|
||||
b2:
|
||||
inx
|
||||
txa
|
||||
and #1
|
||||
cmp #0
|
||||
beq b1
|
||||
lda #<fn2
|
||||
sta f
|
||||
lda #>fn2
|
||||
sta f+1
|
||||
jmp b3
|
||||
b1:
|
||||
lda #<fn1
|
||||
sta f
|
||||
lda #>fn1
|
||||
sta f+1
|
||||
b3:
|
||||
jsr bi_f
|
||||
jmp b2
|
||||
bi_f:
|
||||
jmp (f)
|
||||
}
|
||||
fn2: {
|
||||
.label BGCOL = $d021
|
||||
inc BGCOL
|
||||
rts
|
||||
}
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
inc BORDERCOL
|
||||
rts
|
||||
}
|
39
src/test/ref/function-pointer-noarg-call-2.cfg
Normal file
39
src/test/ref/function-pointer-noarg-call-2.cfg
Normal file
@ -0,0 +1,39 @@
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
main: scope:[main] from @1
|
||||
[4] phi()
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@3
|
||||
[5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@3/(byte) main::i#1 )
|
||||
to:main::@2
|
||||
main::@2: scope:[main] from main::@1
|
||||
[6] (byte) main::i#1 ← ++ (byte) main::i#2
|
||||
[7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1
|
||||
[8] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3
|
||||
to:main::@4
|
||||
main::@4: scope:[main] from main::@2
|
||||
[9] phi()
|
||||
to:main::@3
|
||||
main::@3: scope:[main] from main::@2 main::@4
|
||||
[10] (void()*) main::f#3 ← phi( main::@2/&(void()) fn1() main::@4/&(void()) fn2() )
|
||||
[11] call *((void()*) main::f#3)
|
||||
to:main::@1
|
||||
fn2: scope:[fn2] from
|
||||
[12] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0)
|
||||
to:fn2::@return
|
||||
fn2::@return: scope:[fn2] from fn2
|
||||
[13] return
|
||||
to:@return
|
||||
fn1: scope:[fn1] from
|
||||
[14] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0)
|
||||
to:fn1::@return
|
||||
fn1::@return: scope:[fn1] from fn1
|
||||
[15] return
|
||||
to:@return
|
587
src/test/ref/function-pointer-noarg-call-2.log
Normal file
587
src/test/ref/function-pointer-noarg-call-2.log
Normal file
@ -0,0 +1,587 @@
|
||||
Resolved forward reference fn1 to (void()) fn1()
|
||||
Resolved forward reference fn2 to (void()) fn2()
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
@begin: scope:[] from
|
||||
to:@3
|
||||
main: scope:[main] from @3
|
||||
(byte*) main::SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400
|
||||
(void()*) main::f#0 ← (void()*) 0
|
||||
(byte) main::i#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@5
|
||||
(byte) main::i#3 ← phi( main/(byte) main::i#0 main::@5/(byte) main::i#4 )
|
||||
if(true) goto main::@2
|
||||
to:main::@return
|
||||
main::@2: scope:[main] from main::@1
|
||||
(byte) main::i#2 ← phi( main::@1/(byte) main::i#3 )
|
||||
(byte) main::i#1 ← ++ (byte) main::i#2
|
||||
(byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1
|
||||
(bool~) main::$1 ← (byte~) main::$0 == (byte/signed byte/word/signed word/dword/signed dword) 0
|
||||
if((bool~) main::$1) goto main::@4
|
||||
to:main::@8
|
||||
main::@4: scope:[main] from main::@2
|
||||
(byte) main::i#5 ← phi( main::@2/(byte) main::i#1 )
|
||||
(void()*~) main::$3 ← & (void()) fn1()
|
||||
(void()*) main::f#1 ← (void()*~) main::$3
|
||||
to:main::@5
|
||||
main::@8: scope:[main] from main::@2
|
||||
(byte) main::i#6 ← phi( main::@2/(byte) main::i#1 )
|
||||
(void()*~) main::$2 ← & (void()) fn2()
|
||||
(void()*) main::f#2 ← (void()*~) main::$2
|
||||
to:main::@5
|
||||
main::@5: scope:[main] from main::@4 main::@8
|
||||
(byte) main::i#4 ← phi( main::@4/(byte) main::i#5 main::@8/(byte) main::i#6 )
|
||||
(void()*) main::f#3 ← phi( main::@4/(void()*) main::f#1 main::@8/(void()*) main::f#2 )
|
||||
call *((void()*) main::f#3)
|
||||
to:main::@1
|
||||
main::@return: scope:[main] from main::@1
|
||||
return
|
||||
to:@return
|
||||
fn1: scope:[fn1] from
|
||||
(byte*) fn1::BORDERCOL#0 ← ((byte*)) (word/dword/signed dword) $d020
|
||||
*((byte*) fn1::BORDERCOL#0) ← ++ *((byte*) fn1::BORDERCOL#0)
|
||||
to:fn1::@return
|
||||
fn1::@return: scope:[fn1] from fn1
|
||||
return
|
||||
to:@return
|
||||
fn2: scope:[fn2] from
|
||||
(byte*) fn2::BGCOL#0 ← ((byte*)) (word/dword/signed dword) $d021
|
||||
*((byte*) fn2::BGCOL#0) ← ++ *((byte*) fn2::BGCOL#0)
|
||||
to:fn2::@return
|
||||
fn2::@return: scope:[fn2] from fn2
|
||||
return
|
||||
to:@return
|
||||
@3: scope:[] from @begin
|
||||
call main
|
||||
to:@4
|
||||
@4: scope:[] from @3
|
||||
to:@end
|
||||
@end: scope:[] from @4
|
||||
|
||||
SYMBOL TABLE SSA
|
||||
(label) @3
|
||||
(label) @4
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(void()) fn1()
|
||||
(label) fn1::@return
|
||||
(byte*) fn1::BORDERCOL
|
||||
(byte*) fn1::BORDERCOL#0
|
||||
(void()) fn2()
|
||||
(label) fn2::@return
|
||||
(byte*) fn2::BGCOL
|
||||
(byte*) fn2::BGCOL#0
|
||||
(void()) main()
|
||||
(byte~) main::$0
|
||||
(bool~) main::$1
|
||||
(void()*~) main::$2
|
||||
(void()*~) main::$3
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@4
|
||||
(label) main::@5
|
||||
(label) main::@8
|
||||
(label) main::@return
|
||||
(byte*) main::SCREEN
|
||||
(byte*) main::SCREEN#0
|
||||
(void()*) main::f
|
||||
(void()*) main::f#0
|
||||
(void()*) main::f#1
|
||||
(void()*) main::f#2
|
||||
(void()*) main::f#3
|
||||
(byte) main::i
|
||||
(byte) main::i#0
|
||||
(byte) main::i#1
|
||||
(byte) main::i#2
|
||||
(byte) main::i#3
|
||||
(byte) main::i#4
|
||||
(byte) main::i#5
|
||||
(byte) main::i#6
|
||||
|
||||
Culled Empty Block (label) @4
|
||||
Successful SSA optimization Pass2CullEmptyBlocks
|
||||
Alias (byte) main::i#2 = (byte) main::i#3
|
||||
Alias (byte) main::i#1 = (byte) main::i#5 (byte) main::i#6
|
||||
Alias (void()*) main::f#1 = (void()*~) main::$3
|
||||
Alias (void()*) main::f#2 = (void()*~) main::$2
|
||||
Successful SSA optimization Pass2AliasElimination
|
||||
Alias (byte) main::i#1 = (byte) main::i#4
|
||||
Successful SSA optimization Pass2AliasElimination
|
||||
Simple Condition (bool~) main::$1 [9] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@4
|
||||
Successful SSA optimization Pass2ConditionalJumpSimplification
|
||||
Constant (const byte*) main::SCREEN#0 = ((byte*))$400
|
||||
Constant (const void()*) main::f#0 = 0
|
||||
Constant (const byte) main::i#0 = 0
|
||||
Constant (const void()*) main::f#1 = &fn1
|
||||
Constant (const void()*) main::f#2 = &fn2
|
||||
Constant (const byte*) fn1::BORDERCOL#0 = ((byte*))$d020
|
||||
Constant (const byte*) fn2::BGCOL#0 = ((byte*))$d021
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
if() condition always true - replacing block destination [1] if(true) goto main::@2
|
||||
Successful SSA optimization Pass2ConstantIfs
|
||||
Successful SSA optimization PassNEliminateUnusedVars
|
||||
Removing unused block main::@return
|
||||
Successful SSA optimization Pass2EliminateUnusedBlocks
|
||||
Culled Empty Block (label) main::@4
|
||||
Successful SSA optimization Pass2CullEmptyBlocks
|
||||
Inlining constant with var siblings (const byte) main::i#0
|
||||
Inlining constant with var siblings (const void()*) main::f#1
|
||||
Inlining constant with var siblings (const void()*) main::f#2
|
||||
Constant inlined main::i#0 = (byte/signed byte/word/signed word/dword/signed dword) 0
|
||||
Constant inlined main::f#2 = &(void()) fn2()
|
||||
Constant inlined main::f#1 = &(void()) fn1()
|
||||
Successful SSA optimization Pass2ConstantInlining
|
||||
Adding NOP phi() at start of @begin
|
||||
Adding NOP phi() at start of @3
|
||||
Adding NOP phi() at start of @end
|
||||
Adding NOP phi() at start of main
|
||||
Adding NOP phi() at start of main::@8
|
||||
CALL GRAPH
|
||||
Calls in [] to main:2
|
||||
|
||||
Created 2 initial phi equivalence classes
|
||||
Coalesced [12] main::i#7 ← main::i#1
|
||||
Coalesced down to 2 phi equivalence classes
|
||||
Renumbering block @3 to @1
|
||||
Renumbering block main::@5 to main::@3
|
||||
Renumbering block main::@8 to main::@4
|
||||
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
|
||||
Adding NOP phi() at start of main::@4
|
||||
|
||||
FINAL CONTROL FLOW GRAPH
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
main: scope:[main] from @1
|
||||
[4] phi()
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@3
|
||||
[5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@3/(byte) main::i#1 )
|
||||
to:main::@2
|
||||
main::@2: scope:[main] from main::@1
|
||||
[6] (byte) main::i#1 ← ++ (byte) main::i#2
|
||||
[7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1
|
||||
[8] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3
|
||||
to:main::@4
|
||||
main::@4: scope:[main] from main::@2
|
||||
[9] phi()
|
||||
to:main::@3
|
||||
main::@3: scope:[main] from main::@2 main::@4
|
||||
[10] (void()*) main::f#3 ← phi( main::@2/&(void()) fn1() main::@4/&(void()) fn2() )
|
||||
[11] call *((void()*) main::f#3)
|
||||
to:main::@1
|
||||
fn2: scope:[fn2] from
|
||||
[12] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0)
|
||||
to:fn2::@return
|
||||
fn2::@return: scope:[fn2] from fn2
|
||||
[13] return
|
||||
to:@return
|
||||
fn1: scope:[fn1] from
|
||||
[14] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0)
|
||||
to:fn1::@return
|
||||
fn1::@return: scope:[fn1] from fn1
|
||||
[15] return
|
||||
to:@return
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
(void()) fn1()
|
||||
(byte*) fn1::BORDERCOL
|
||||
(void()) fn2()
|
||||
(byte*) fn2::BGCOL
|
||||
(void()) main()
|
||||
(byte~) main::$0 22.0
|
||||
(byte*) main::SCREEN
|
||||
(void()*) main::f
|
||||
(void()*) main::f#3
|
||||
(byte) main::i
|
||||
(byte) main::i#1 5.5
|
||||
(byte) main::i#2 22.0
|
||||
|
||||
Initial phi equivalence classes
|
||||
[ main::i#2 main::i#1 ]
|
||||
[ main::f#3 ]
|
||||
Added variable main::$0 to zero page equivalence class [ main::$0 ]
|
||||
Complete equivalence classes
|
||||
[ main::i#2 main::i#1 ]
|
||||
[ main::f#3 ]
|
||||
[ main::$0 ]
|
||||
Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
|
||||
Allocated zp ZP_WORD:3 [ main::f#3 ]
|
||||
Allocated zp ZP_BYTE:5 [ main::$0 ]
|
||||
|
||||
INITIAL ASM
|
||||
//SEG0 File Comments
|
||||
// Tests creating, assigning and calling pointers to non-args no-return functions
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
//SEG3 @begin
|
||||
bbegin:
|
||||
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
b1_from_bbegin:
|
||||
jmp b1
|
||||
//SEG5 @1
|
||||
b1:
|
||||
//SEG6 [2] call main
|
||||
//SEG7 [4] phi from @1 to main [phi:@1->main]
|
||||
main_from_b1:
|
||||
jsr main
|
||||
//SEG8 [3] phi from @1 to @end [phi:@1->@end]
|
||||
bend_from_b1:
|
||||
jmp bend
|
||||
//SEG9 @end
|
||||
bend:
|
||||
//SEG10 main
|
||||
main: {
|
||||
.label _0 = 5
|
||||
.label i = 2
|
||||
.label f = 3
|
||||
//SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
|
||||
b1_from_main:
|
||||
//SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1
|
||||
lda #0
|
||||
sta i
|
||||
jmp b1
|
||||
//SEG13 main::@1
|
||||
b1:
|
||||
jmp b2
|
||||
//SEG14 main::@2
|
||||
b2:
|
||||
//SEG15 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1
|
||||
inc i
|
||||
//SEG16 [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuz1=vbuz2_band_vbuc1
|
||||
lda #1
|
||||
and i
|
||||
sta _0
|
||||
//SEG17 [8] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3 -- vbuz1_eq_0_then_la1
|
||||
lda _0
|
||||
cmp #0
|
||||
beq b3_from_b2
|
||||
//SEG18 [9] phi from main::@2 to main::@4 [phi:main::@2->main::@4]
|
||||
b4_from_b2:
|
||||
jmp b4
|
||||
//SEG19 main::@4
|
||||
b4:
|
||||
//SEG20 [10] phi from main::@4 to main::@3 [phi:main::@4->main::@3]
|
||||
b3_from_b4:
|
||||
//SEG21 [10] phi (void()*) main::f#3 = &(void()) fn2() [phi:main::@4->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn2
|
||||
sta f
|
||||
lda #>fn2
|
||||
sta f+1
|
||||
jmp b3
|
||||
//SEG22 [10] phi from main::@2 to main::@3 [phi:main::@2->main::@3]
|
||||
b3_from_b2:
|
||||
//SEG23 [10] phi (void()*) main::f#3 = &(void()) fn1() [phi:main::@2->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn1
|
||||
sta f
|
||||
lda #>fn1
|
||||
sta f+1
|
||||
jmp b3
|
||||
//SEG24 main::@3
|
||||
b3:
|
||||
//SEG25 [11] call *((void()*) main::f#3)
|
||||
jsr bi_f
|
||||
//SEG26 [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1]
|
||||
b1_from_b3:
|
||||
//SEG27 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy
|
||||
jmp b1
|
||||
bi_f:
|
||||
jmp (f)
|
||||
}
|
||||
//SEG28 fn2
|
||||
fn2: {
|
||||
.label BGCOL = $d021
|
||||
//SEG29 [12] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BGCOL
|
||||
jmp breturn
|
||||
//SEG30 fn2::@return
|
||||
breturn:
|
||||
//SEG31 [13] return
|
||||
rts
|
||||
}
|
||||
//SEG32 fn1
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
//SEG33 [14] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BORDERCOL
|
||||
jmp breturn
|
||||
//SEG34 fn1::@return
|
||||
breturn:
|
||||
//SEG35 [15] return
|
||||
rts
|
||||
}
|
||||
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y ,
|
||||
Potential registers zp ZP_WORD:3 [ main::f#3 ] : zp ZP_WORD:3 ,
|
||||
Potential registers zp ZP_BYTE:5 [ main::$0 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y ,
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main] 27.5: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 22: zp ZP_BYTE:5 [ main::$0 ] 0: zp ZP_WORD:3 [ main::f#3 ]
|
||||
Uplift Scope [fn1]
|
||||
Uplift Scope [fn2]
|
||||
Uplift Scope []
|
||||
|
||||
Uplifting [main] best 657 combination reg byte x [ main::i#2 main::i#1 ] reg byte a [ main::$0 ] zp ZP_WORD:3 [ main::f#3 ]
|
||||
Uplifting [fn1] best 657 combination
|
||||
Uplifting [fn2] best 657 combination
|
||||
Uplifting [] best 657 combination
|
||||
Allocated (was zp ZP_WORD:3) zp ZP_WORD:2 [ main::f#3 ]
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
//SEG0 File Comments
|
||||
// Tests creating, assigning and calling pointers to non-args no-return functions
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
//SEG3 @begin
|
||||
bbegin:
|
||||
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
b1_from_bbegin:
|
||||
jmp b1
|
||||
//SEG5 @1
|
||||
b1:
|
||||
//SEG6 [2] call main
|
||||
//SEG7 [4] phi from @1 to main [phi:@1->main]
|
||||
main_from_b1:
|
||||
jsr main
|
||||
//SEG8 [3] phi from @1 to @end [phi:@1->@end]
|
||||
bend_from_b1:
|
||||
jmp bend
|
||||
//SEG9 @end
|
||||
bend:
|
||||
//SEG10 main
|
||||
main: {
|
||||
.label f = 2
|
||||
//SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
|
||||
b1_from_main:
|
||||
//SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1
|
||||
ldx #0
|
||||
jmp b1
|
||||
//SEG13 main::@1
|
||||
b1:
|
||||
jmp b2
|
||||
//SEG14 main::@2
|
||||
b2:
|
||||
//SEG15 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx
|
||||
inx
|
||||
//SEG16 [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuxx_band_vbuc1
|
||||
txa
|
||||
and #1
|
||||
//SEG17 [8] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3 -- vbuaa_eq_0_then_la1
|
||||
cmp #0
|
||||
beq b3_from_b2
|
||||
//SEG18 [9] phi from main::@2 to main::@4 [phi:main::@2->main::@4]
|
||||
b4_from_b2:
|
||||
jmp b4
|
||||
//SEG19 main::@4
|
||||
b4:
|
||||
//SEG20 [10] phi from main::@4 to main::@3 [phi:main::@4->main::@3]
|
||||
b3_from_b4:
|
||||
//SEG21 [10] phi (void()*) main::f#3 = &(void()) fn2() [phi:main::@4->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn2
|
||||
sta f
|
||||
lda #>fn2
|
||||
sta f+1
|
||||
jmp b3
|
||||
//SEG22 [10] phi from main::@2 to main::@3 [phi:main::@2->main::@3]
|
||||
b3_from_b2:
|
||||
//SEG23 [10] phi (void()*) main::f#3 = &(void()) fn1() [phi:main::@2->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn1
|
||||
sta f
|
||||
lda #>fn1
|
||||
sta f+1
|
||||
jmp b3
|
||||
//SEG24 main::@3
|
||||
b3:
|
||||
//SEG25 [11] call *((void()*) main::f#3)
|
||||
jsr bi_f
|
||||
//SEG26 [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1]
|
||||
b1_from_b3:
|
||||
//SEG27 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy
|
||||
jmp b1
|
||||
bi_f:
|
||||
jmp (f)
|
||||
}
|
||||
//SEG28 fn2
|
||||
fn2: {
|
||||
.label BGCOL = $d021
|
||||
//SEG29 [12] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BGCOL
|
||||
jmp breturn
|
||||
//SEG30 fn2::@return
|
||||
breturn:
|
||||
//SEG31 [13] return
|
||||
rts
|
||||
}
|
||||
//SEG32 fn1
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
//SEG33 [14] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BORDERCOL
|
||||
jmp breturn
|
||||
//SEG34 fn1::@return
|
||||
breturn:
|
||||
//SEG35 [15] return
|
||||
rts
|
||||
}
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp b1
|
||||
Removing instruction jmp bend
|
||||
Removing instruction jmp b1
|
||||
Removing instruction jmp b2
|
||||
Removing instruction jmp b4
|
||||
Removing instruction jmp b3
|
||||
Removing instruction jmp breturn
|
||||
Removing instruction jmp breturn
|
||||
Succesful ASM optimization Pass5NextJumpElimination
|
||||
Replacing label b1 with b2
|
||||
Removing instruction b1_from_bbegin:
|
||||
Removing instruction b1:
|
||||
Removing instruction main_from_b1:
|
||||
Removing instruction bend_from_b1:
|
||||
Removing instruction b1:
|
||||
Removing instruction b4_from_b2:
|
||||
Removing instruction b3_from_b4:
|
||||
Succesful ASM optimization Pass5RedundantLabelElimination
|
||||
Removing instruction bend:
|
||||
Removing instruction b1_from_main:
|
||||
Removing instruction b4:
|
||||
Removing instruction b1_from_b3:
|
||||
Removing instruction breturn:
|
||||
Removing instruction breturn:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
Updating BasicUpstart to call main directly
|
||||
Removing instruction jsr main
|
||||
Succesful ASM optimization Pass5SkipBegin
|
||||
Relabelling long label b3_from_b2 to b1
|
||||
Succesful ASM optimization Pass5RelabelLongLabels
|
||||
Removing instruction bbegin:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
|
||||
FINAL SYMBOL TABLE
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(void()) fn1()
|
||||
(label) fn1::@return
|
||||
(byte*) fn1::BORDERCOL
|
||||
(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020
|
||||
(void()) fn2()
|
||||
(label) fn2::@return
|
||||
(byte*) fn2::BGCOL
|
||||
(const byte*) fn2::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) $d021
|
||||
(void()) main()
|
||||
(byte~) main::$0 reg byte a 22.0
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@3
|
||||
(label) main::@4
|
||||
(byte*) main::SCREEN
|
||||
(void()*) main::f
|
||||
(void()*) main::f#3 f zp ZP_WORD:2
|
||||
(byte) main::i
|
||||
(byte) main::i#1 reg byte x 5.5
|
||||
(byte) main::i#2 reg byte x 22.0
|
||||
|
||||
reg byte x [ main::i#2 main::i#1 ]
|
||||
zp ZP_WORD:2 [ main::f#3 ]
|
||||
reg byte a [ main::$0 ]
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 519
|
||||
|
||||
//SEG0 File Comments
|
||||
// Tests creating, assigning and calling pointers to non-args no-return functions
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
//SEG3 @begin
|
||||
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
//SEG5 @1
|
||||
//SEG6 [2] call main
|
||||
//SEG7 [4] phi from @1 to main [phi:@1->main]
|
||||
//SEG8 [3] phi from @1 to @end [phi:@1->@end]
|
||||
//SEG9 @end
|
||||
//SEG10 main
|
||||
main: {
|
||||
.label f = 2
|
||||
//SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
|
||||
//SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1
|
||||
ldx #0
|
||||
//SEG13 main::@1
|
||||
//SEG14 main::@2
|
||||
b2:
|
||||
//SEG15 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx
|
||||
inx
|
||||
//SEG16 [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuxx_band_vbuc1
|
||||
txa
|
||||
and #1
|
||||
//SEG17 [8] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3 -- vbuaa_eq_0_then_la1
|
||||
cmp #0
|
||||
beq b1
|
||||
//SEG18 [9] phi from main::@2 to main::@4 [phi:main::@2->main::@4]
|
||||
//SEG19 main::@4
|
||||
//SEG20 [10] phi from main::@4 to main::@3 [phi:main::@4->main::@3]
|
||||
//SEG21 [10] phi (void()*) main::f#3 = &(void()) fn2() [phi:main::@4->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn2
|
||||
sta f
|
||||
lda #>fn2
|
||||
sta f+1
|
||||
jmp b3
|
||||
//SEG22 [10] phi from main::@2 to main::@3 [phi:main::@2->main::@3]
|
||||
b1:
|
||||
//SEG23 [10] phi (void()*) main::f#3 = &(void()) fn1() [phi:main::@2->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn1
|
||||
sta f
|
||||
lda #>fn1
|
||||
sta f+1
|
||||
//SEG24 main::@3
|
||||
b3:
|
||||
//SEG25 [11] call *((void()*) main::f#3)
|
||||
jsr bi_f
|
||||
//SEG26 [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1]
|
||||
//SEG27 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy
|
||||
jmp b2
|
||||
bi_f:
|
||||
jmp (f)
|
||||
}
|
||||
//SEG28 fn2
|
||||
fn2: {
|
||||
.label BGCOL = $d021
|
||||
//SEG29 [12] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BGCOL
|
||||
//SEG30 fn2::@return
|
||||
//SEG31 [13] return
|
||||
rts
|
||||
}
|
||||
//SEG32 fn1
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
//SEG33 [14] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BORDERCOL
|
||||
//SEG34 fn1::@return
|
||||
//SEG35 [15] return
|
||||
rts
|
||||
}
|
||||
|
27
src/test/ref/function-pointer-noarg-call-2.sym
Normal file
27
src/test/ref/function-pointer-noarg-call-2.sym
Normal file
@ -0,0 +1,27 @@
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(void()) fn1()
|
||||
(label) fn1::@return
|
||||
(byte*) fn1::BORDERCOL
|
||||
(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020
|
||||
(void()) fn2()
|
||||
(label) fn2::@return
|
||||
(byte*) fn2::BGCOL
|
||||
(const byte*) fn2::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) $d021
|
||||
(void()) main()
|
||||
(byte~) main::$0 reg byte a 22.0
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@3
|
||||
(label) main::@4
|
||||
(byte*) main::SCREEN
|
||||
(void()*) main::f
|
||||
(void()*) main::f#3 f zp ZP_WORD:2
|
||||
(byte) main::i
|
||||
(byte) main::i#1 reg byte x 5.5
|
||||
(byte) main::i#2 reg byte x 22.0
|
||||
|
||||
reg byte x [ main::i#2 main::i#1 ]
|
||||
zp ZP_WORD:2 [ main::f#3 ]
|
||||
reg byte a [ main::$0 ]
|
14
src/test/ref/function-pointer-noarg-call.asm
Normal file
14
src/test/ref/function-pointer-noarg-call.asm
Normal file
@ -0,0 +1,14 @@
|
||||
// Tests creating, assigning and calling pointers to non-args no-return functions
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
main: {
|
||||
.label f = fn1
|
||||
jsr f
|
||||
rts
|
||||
}
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
inc BORDERCOL
|
||||
rts
|
||||
}
|
21
src/test/ref/function-pointer-noarg-call.cfg
Normal file
21
src/test/ref/function-pointer-noarg-call.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
main: scope:[main] from @1
|
||||
[4] call *((const void()*) main::f#0)
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[5] return
|
||||
to:@return
|
||||
fn1: scope:[fn1] from
|
||||
[6] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0)
|
||||
to:fn1::@return
|
||||
fn1::@return: scope:[fn1] from fn1
|
||||
[7] return
|
||||
to:@return
|
266
src/test/ref/function-pointer-noarg-call.log
Normal file
266
src/test/ref/function-pointer-noarg-call.log
Normal file
@ -0,0 +1,266 @@
|
||||
Resolved forward reference fn1 to (void()) fn1()
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
@begin: scope:[] from
|
||||
to:@2
|
||||
main: scope:[main] from @2
|
||||
(void()*~) main::$0 ← & (void()) fn1()
|
||||
(void()*) main::f#0 ← (void()*~) main::$0
|
||||
call *((void()*) main::f#0)
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
return
|
||||
to:@return
|
||||
fn1: scope:[fn1] from
|
||||
(byte*) fn1::BORDERCOL#0 ← ((byte*)) (word/dword/signed dword) $d020
|
||||
*((byte*) fn1::BORDERCOL#0) ← ++ *((byte*) fn1::BORDERCOL#0)
|
||||
to:fn1::@return
|
||||
fn1::@return: scope:[fn1] from fn1
|
||||
return
|
||||
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
|
||||
(void()) fn1()
|
||||
(label) fn1::@return
|
||||
(byte*) fn1::BORDERCOL
|
||||
(byte*) fn1::BORDERCOL#0
|
||||
(void()) main()
|
||||
(void()*~) main::$0
|
||||
(label) main::@return
|
||||
(void()*) main::f
|
||||
(void()*) main::f#0
|
||||
|
||||
Culled Empty Block (label) @3
|
||||
Successful SSA optimization Pass2CullEmptyBlocks
|
||||
Alias (void()*) main::f#0 = (void()*~) main::$0
|
||||
Successful SSA optimization Pass2AliasElimination
|
||||
Constant (const void()*) main::f#0 = &fn1
|
||||
Constant (const byte*) fn1::BORDERCOL#0 = ((byte*))$d020
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Adding NOP phi() at start of @begin
|
||||
Adding NOP phi() at start of @2
|
||||
Adding NOP phi() at start of @end
|
||||
CALL GRAPH
|
||||
Calls in [] to main:2
|
||||
|
||||
Created 0 initial phi equivalence classes
|
||||
Coalesced down to 0 phi equivalence classes
|
||||
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
|
||||
|
||||
FINAL CONTROL FLOW GRAPH
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
main: scope:[main] from @1
|
||||
[4] call *((const void()*) main::f#0)
|
||||
to:main::@return
|
||||
main::@return: scope:[main] from main
|
||||
[5] return
|
||||
to:@return
|
||||
fn1: scope:[fn1] from
|
||||
[6] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0)
|
||||
to:fn1::@return
|
||||
fn1::@return: scope:[fn1] from fn1
|
||||
[7] return
|
||||
to:@return
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
(void()) fn1()
|
||||
(byte*) fn1::BORDERCOL
|
||||
(void()) main()
|
||||
(void()*) main::f
|
||||
|
||||
Initial phi equivalence classes
|
||||
Complete equivalence classes
|
||||
|
||||
INITIAL ASM
|
||||
//SEG0 File Comments
|
||||
// Tests creating, assigning and calling pointers to non-args no-return functions
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
//SEG3 @begin
|
||||
bbegin:
|
||||
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
b1_from_bbegin:
|
||||
jmp b1
|
||||
//SEG5 @1
|
||||
b1:
|
||||
//SEG6 [2] call main
|
||||
jsr main
|
||||
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
|
||||
bend_from_b1:
|
||||
jmp bend
|
||||
//SEG8 @end
|
||||
bend:
|
||||
//SEG9 main
|
||||
main: {
|
||||
.label f = fn1
|
||||
//SEG10 [4] call *((const void()*) main::f#0)
|
||||
jsr f
|
||||
jmp breturn
|
||||
//SEG11 main::@return
|
||||
breturn:
|
||||
//SEG12 [5] return
|
||||
rts
|
||||
}
|
||||
//SEG13 fn1
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
//SEG14 [6] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BORDERCOL
|
||||
jmp breturn
|
||||
//SEG15 fn1::@return
|
||||
breturn:
|
||||
//SEG16 [7] return
|
||||
rts
|
||||
}
|
||||
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main]
|
||||
Uplift Scope [fn1]
|
||||
Uplift Scope []
|
||||
|
||||
Uplifting [main] best 42 combination
|
||||
Uplifting [fn1] best 42 combination
|
||||
Uplifting [] best 42 combination
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
//SEG0 File Comments
|
||||
// Tests creating, assigning and calling pointers to non-args no-return functions
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
//SEG3 @begin
|
||||
bbegin:
|
||||
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
b1_from_bbegin:
|
||||
jmp b1
|
||||
//SEG5 @1
|
||||
b1:
|
||||
//SEG6 [2] call main
|
||||
jsr main
|
||||
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
|
||||
bend_from_b1:
|
||||
jmp bend
|
||||
//SEG8 @end
|
||||
bend:
|
||||
//SEG9 main
|
||||
main: {
|
||||
.label f = fn1
|
||||
//SEG10 [4] call *((const void()*) main::f#0)
|
||||
jsr f
|
||||
jmp breturn
|
||||
//SEG11 main::@return
|
||||
breturn:
|
||||
//SEG12 [5] return
|
||||
rts
|
||||
}
|
||||
//SEG13 fn1
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
//SEG14 [6] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BORDERCOL
|
||||
jmp breturn
|
||||
//SEG15 fn1::@return
|
||||
breturn:
|
||||
//SEG16 [7] return
|
||||
rts
|
||||
}
|
||||
|
||||
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 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
|
||||
(void()) fn1()
|
||||
(label) fn1::@return
|
||||
(byte*) fn1::BORDERCOL
|
||||
(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020
|
||||
(void()) main()
|
||||
(label) main::@return
|
||||
(void()*) main::f
|
||||
(const void()*) main::f#0 f = &(void()) fn1()
|
||||
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 24
|
||||
|
||||
//SEG0 File Comments
|
||||
// Tests creating, assigning and calling pointers to non-args no-return functions
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
//SEG3 @begin
|
||||
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
//SEG5 @1
|
||||
//SEG6 [2] call main
|
||||
//SEG7 [3] phi from @1 to @end [phi:@1->@end]
|
||||
//SEG8 @end
|
||||
//SEG9 main
|
||||
main: {
|
||||
.label f = fn1
|
||||
//SEG10 [4] call *((const void()*) main::f#0)
|
||||
jsr f
|
||||
//SEG11 main::@return
|
||||
//SEG12 [5] return
|
||||
rts
|
||||
}
|
||||
//SEG13 fn1
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
//SEG14 [6] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BORDERCOL
|
||||
//SEG15 fn1::@return
|
||||
//SEG16 [7] return
|
||||
rts
|
||||
}
|
||||
|
12
src/test/ref/function-pointer-noarg-call.sym
Normal file
12
src/test/ref/function-pointer-noarg-call.sym
Normal file
@ -0,0 +1,12 @@
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(void()) fn1()
|
||||
(label) fn1::@return
|
||||
(byte*) fn1::BORDERCOL
|
||||
(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020
|
||||
(void()) main()
|
||||
(label) main::@return
|
||||
(void()*) main::f
|
||||
(const void()*) main::f#0 f = &(void()) fn1()
|
||||
|
41
src/test/ref/function-pointer-return.asm
Normal file
41
src/test/ref/function-pointer-return.asm
Normal file
@ -0,0 +1,41 @@
|
||||
// Tests creating and assigning pointers to non-args return with function value
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
main: {
|
||||
.label SCREEN = $400
|
||||
.label f = 2
|
||||
ldx #0
|
||||
b2:
|
||||
inx
|
||||
txa
|
||||
and #1
|
||||
cmp #0
|
||||
beq b1
|
||||
lda #<fn2
|
||||
sta f
|
||||
lda #>fn2
|
||||
sta f+1
|
||||
jmp b3
|
||||
b1:
|
||||
lda #<fn1
|
||||
sta f
|
||||
lda #>fn1
|
||||
sta f+1
|
||||
b3:
|
||||
lda f
|
||||
sta SCREEN
|
||||
jmp b2
|
||||
}
|
||||
fn2: {
|
||||
.label BGCOL = $d021
|
||||
inc BGCOL
|
||||
lda BGCOL
|
||||
rts
|
||||
}
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
inc BORDERCOL
|
||||
lda BORDERCOL
|
||||
rts
|
||||
}
|
42
src/test/ref/function-pointer-return.cfg
Normal file
42
src/test/ref/function-pointer-return.cfg
Normal file
@ -0,0 +1,42 @@
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
main: scope:[main] from @1
|
||||
[4] phi()
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@3
|
||||
[5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@3/(byte) main::i#1 )
|
||||
to:main::@2
|
||||
main::@2: scope:[main] from main::@1
|
||||
[6] (byte) main::i#1 ← ++ (byte) main::i#2
|
||||
[7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1
|
||||
[8] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3
|
||||
to:main::@4
|
||||
main::@4: scope:[main] from main::@2
|
||||
[9] phi()
|
||||
to:main::@3
|
||||
main::@3: scope:[main] from main::@2 main::@4
|
||||
[10] (byte()*) main::f#3 ← phi( main::@2/&(byte()) fn1() main::@4/&(byte()) fn2() )
|
||||
[11] (byte~) main::$4 ← ((byte)) (byte()*) main::f#3
|
||||
[12] *((const byte*) main::SCREEN#0) ← (byte~) main::$4
|
||||
to:main::@1
|
||||
fn2: scope:[fn2] from
|
||||
[13] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0)
|
||||
[14] (byte) fn2::return#0 ← *((const byte*) fn2::BGCOL#0)
|
||||
to:fn2::@return
|
||||
fn2::@return: scope:[fn2] from fn2
|
||||
[15] return
|
||||
to:@return
|
||||
fn1: scope:[fn1] from
|
||||
[16] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0)
|
||||
[17] (byte) fn1::return#0 ← *((const byte*) fn1::BORDERCOL#0)
|
||||
to:fn1::@return
|
||||
fn1::@return: scope:[fn1] from fn1
|
||||
[18] return
|
||||
to:@return
|
663
src/test/ref/function-pointer-return.log
Normal file
663
src/test/ref/function-pointer-return.log
Normal file
@ -0,0 +1,663 @@
|
||||
Resolved forward reference fn1 to (byte()) fn1()
|
||||
Resolved forward reference fn2 to (byte()) fn2()
|
||||
|
||||
CONTROL FLOW GRAPH SSA
|
||||
@begin: scope:[] from
|
||||
to:@3
|
||||
main: scope:[main] from @3
|
||||
(byte*) main::SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400
|
||||
(byte()*) main::f#0 ← (byte()*) 0
|
||||
(byte) main::i#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@5
|
||||
(byte) main::i#3 ← phi( main/(byte) main::i#0 main::@5/(byte) main::i#4 )
|
||||
if(true) goto main::@2
|
||||
to:main::@return
|
||||
main::@2: scope:[main] from main::@1
|
||||
(byte) main::i#2 ← phi( main::@1/(byte) main::i#3 )
|
||||
(byte) main::i#1 ← ++ (byte) main::i#2
|
||||
(byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1
|
||||
(bool~) main::$1 ← (byte~) main::$0 == (byte/signed byte/word/signed word/dword/signed dword) 0
|
||||
if((bool~) main::$1) goto main::@4
|
||||
to:main::@8
|
||||
main::@4: scope:[main] from main::@2
|
||||
(byte) main::i#5 ← phi( main::@2/(byte) main::i#1 )
|
||||
(byte()*~) main::$3 ← & (byte()) fn1()
|
||||
(byte()*) main::f#1 ← (byte()*~) main::$3
|
||||
to:main::@5
|
||||
main::@8: scope:[main] from main::@2
|
||||
(byte) main::i#6 ← phi( main::@2/(byte) main::i#1 )
|
||||
(byte()*~) main::$2 ← & (byte()) fn2()
|
||||
(byte()*) main::f#2 ← (byte()*~) main::$2
|
||||
to:main::@5
|
||||
main::@5: scope:[main] from main::@4 main::@8
|
||||
(byte) main::i#4 ← phi( main::@4/(byte) main::i#5 main::@8/(byte) main::i#6 )
|
||||
(byte()*) main::f#3 ← phi( main::@4/(byte()*) main::f#1 main::@8/(byte()*) main::f#2 )
|
||||
(byte~) main::$4 ← ((byte)) (byte()*) main::f#3
|
||||
*((byte*) main::SCREEN#0 + (byte/signed byte/word/signed word/dword/signed dword) 0) ← (byte~) main::$4
|
||||
to:main::@1
|
||||
main::@return: scope:[main] from main::@1
|
||||
return
|
||||
to:@return
|
||||
fn1: scope:[fn1] from
|
||||
(byte*) fn1::BORDERCOL#0 ← ((byte*)) (word/dword/signed dword) $d020
|
||||
*((byte*) fn1::BORDERCOL#0) ← ++ *((byte*) fn1::BORDERCOL#0)
|
||||
(byte) fn1::return#0 ← *((byte*) fn1::BORDERCOL#0)
|
||||
to:fn1::@return
|
||||
fn1::@return: scope:[fn1] from fn1
|
||||
(byte) fn1::return#2 ← phi( fn1/(byte) fn1::return#0 )
|
||||
(byte) fn1::return#1 ← (byte) fn1::return#2
|
||||
return
|
||||
to:@return
|
||||
fn2: scope:[fn2] from
|
||||
(byte*) fn2::BGCOL#0 ← ((byte*)) (word/dword/signed dword) $d021
|
||||
*((byte*) fn2::BGCOL#0) ← ++ *((byte*) fn2::BGCOL#0)
|
||||
(byte) fn2::return#0 ← *((byte*) fn2::BGCOL#0)
|
||||
to:fn2::@return
|
||||
fn2::@return: scope:[fn2] from fn2
|
||||
(byte) fn2::return#2 ← phi( fn2/(byte) fn2::return#0 )
|
||||
(byte) fn2::return#1 ← (byte) fn2::return#2
|
||||
return
|
||||
to:@return
|
||||
@3: scope:[] from @begin
|
||||
call main
|
||||
to:@4
|
||||
@4: scope:[] from @3
|
||||
to:@end
|
||||
@end: scope:[] from @4
|
||||
|
||||
SYMBOL TABLE SSA
|
||||
(label) @3
|
||||
(label) @4
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(byte()) fn1()
|
||||
(label) fn1::@return
|
||||
(byte*) fn1::BORDERCOL
|
||||
(byte*) fn1::BORDERCOL#0
|
||||
(byte) fn1::return
|
||||
(byte) fn1::return#0
|
||||
(byte) fn1::return#1
|
||||
(byte) fn1::return#2
|
||||
(byte()) fn2()
|
||||
(label) fn2::@return
|
||||
(byte*) fn2::BGCOL
|
||||
(byte*) fn2::BGCOL#0
|
||||
(byte) fn2::return
|
||||
(byte) fn2::return#0
|
||||
(byte) fn2::return#1
|
||||
(byte) fn2::return#2
|
||||
(void()) main()
|
||||
(byte~) main::$0
|
||||
(bool~) main::$1
|
||||
(byte()*~) main::$2
|
||||
(byte()*~) main::$3
|
||||
(byte~) main::$4
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@4
|
||||
(label) main::@5
|
||||
(label) main::@8
|
||||
(label) main::@return
|
||||
(byte*) main::SCREEN
|
||||
(byte*) main::SCREEN#0
|
||||
(byte()*) main::f
|
||||
(byte()*) main::f#0
|
||||
(byte()*) main::f#1
|
||||
(byte()*) main::f#2
|
||||
(byte()*) main::f#3
|
||||
(byte) main::i
|
||||
(byte) main::i#0
|
||||
(byte) main::i#1
|
||||
(byte) main::i#2
|
||||
(byte) main::i#3
|
||||
(byte) main::i#4
|
||||
(byte) main::i#5
|
||||
(byte) main::i#6
|
||||
|
||||
Culled Empty Block (label) @4
|
||||
Successful SSA optimization Pass2CullEmptyBlocks
|
||||
Alias (byte) main::i#2 = (byte) main::i#3
|
||||
Alias (byte) main::i#1 = (byte) main::i#5 (byte) main::i#6
|
||||
Alias (byte()*) main::f#1 = (byte()*~) main::$3
|
||||
Alias (byte()*) main::f#2 = (byte()*~) main::$2
|
||||
Alias (byte) fn1::return#0 = (byte) fn1::return#2 (byte) fn1::return#1
|
||||
Alias (byte) fn2::return#0 = (byte) fn2::return#2 (byte) fn2::return#1
|
||||
Successful SSA optimization Pass2AliasElimination
|
||||
Alias (byte) main::i#1 = (byte) main::i#4
|
||||
Successful SSA optimization Pass2AliasElimination
|
||||
Simple Condition (bool~) main::$1 [9] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@4
|
||||
Successful SSA optimization Pass2ConditionalJumpSimplification
|
||||
Constant (const byte*) main::SCREEN#0 = ((byte*))$400
|
||||
Constant (const byte()*) main::f#0 = 0
|
||||
Constant (const byte) main::i#0 = 0
|
||||
Constant (const byte()*) main::f#1 = &fn1
|
||||
Constant (const byte()*) main::f#2 = &fn2
|
||||
Constant (const byte*) fn1::BORDERCOL#0 = ((byte*))$d020
|
||||
Constant (const byte*) fn2::BGCOL#0 = ((byte*))$d021
|
||||
Successful SSA optimization Pass2ConstantIdentification
|
||||
Consolidated array index constant in *(main::SCREEN#0+0)
|
||||
Successful SSA optimization Pass2ConstantAdditionElimination
|
||||
if() condition always true - replacing block destination [1] if(true) goto main::@2
|
||||
Successful SSA optimization Pass2ConstantIfs
|
||||
Successful SSA optimization PassNEliminateUnusedVars
|
||||
Removing unused block main::@return
|
||||
Successful SSA optimization Pass2EliminateUnusedBlocks
|
||||
Culled Empty Block (label) main::@4
|
||||
Successful SSA optimization Pass2CullEmptyBlocks
|
||||
Inlining constant with var siblings (const byte) main::i#0
|
||||
Inlining constant with var siblings (const byte()*) main::f#1
|
||||
Inlining constant with var siblings (const byte()*) main::f#2
|
||||
Constant inlined main::i#0 = (byte/signed byte/word/signed word/dword/signed dword) 0
|
||||
Constant inlined main::f#2 = &(byte()) fn2()
|
||||
Constant inlined main::f#1 = &(byte()) fn1()
|
||||
Successful SSA optimization Pass2ConstantInlining
|
||||
Simplifying constant plus zero main::SCREEN#0+0
|
||||
Adding NOP phi() at start of @begin
|
||||
Adding NOP phi() at start of @3
|
||||
Adding NOP phi() at start of @end
|
||||
Adding NOP phi() at start of main
|
||||
Adding NOP phi() at start of main::@8
|
||||
CALL GRAPH
|
||||
Calls in [] to main:2
|
||||
|
||||
Created 2 initial phi equivalence classes
|
||||
Coalesced [13] main::i#7 ← main::i#1
|
||||
Coalesced down to 2 phi equivalence classes
|
||||
Renumbering block @3 to @1
|
||||
Renumbering block main::@5 to main::@3
|
||||
Renumbering block main::@8 to main::@4
|
||||
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
|
||||
Adding NOP phi() at start of main::@4
|
||||
|
||||
FINAL CONTROL FLOW GRAPH
|
||||
@begin: scope:[] from
|
||||
[0] phi()
|
||||
to:@1
|
||||
@1: scope:[] from @begin
|
||||
[1] phi()
|
||||
[2] call main
|
||||
to:@end
|
||||
@end: scope:[] from @1
|
||||
[3] phi()
|
||||
main: scope:[main] from @1
|
||||
[4] phi()
|
||||
to:main::@1
|
||||
main::@1: scope:[main] from main main::@3
|
||||
[5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@3/(byte) main::i#1 )
|
||||
to:main::@2
|
||||
main::@2: scope:[main] from main::@1
|
||||
[6] (byte) main::i#1 ← ++ (byte) main::i#2
|
||||
[7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1
|
||||
[8] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3
|
||||
to:main::@4
|
||||
main::@4: scope:[main] from main::@2
|
||||
[9] phi()
|
||||
to:main::@3
|
||||
main::@3: scope:[main] from main::@2 main::@4
|
||||
[10] (byte()*) main::f#3 ← phi( main::@2/&(byte()) fn1() main::@4/&(byte()) fn2() )
|
||||
[11] (byte~) main::$4 ← ((byte)) (byte()*) main::f#3
|
||||
[12] *((const byte*) main::SCREEN#0) ← (byte~) main::$4
|
||||
to:main::@1
|
||||
fn2: scope:[fn2] from
|
||||
[13] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0)
|
||||
[14] (byte) fn2::return#0 ← *((const byte*) fn2::BGCOL#0)
|
||||
to:fn2::@return
|
||||
fn2::@return: scope:[fn2] from fn2
|
||||
[15] return
|
||||
to:@return
|
||||
fn1: scope:[fn1] from
|
||||
[16] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0)
|
||||
[17] (byte) fn1::return#0 ← *((const byte*) fn1::BORDERCOL#0)
|
||||
to:fn1::@return
|
||||
fn1::@return: scope:[fn1] from fn1
|
||||
[18] return
|
||||
to:@return
|
||||
|
||||
|
||||
VARIABLE REGISTER WEIGHTS
|
||||
(byte()) fn1()
|
||||
(byte*) fn1::BORDERCOL
|
||||
(byte) fn1::return
|
||||
(byte) fn1::return#0 20.0
|
||||
(byte()) fn2()
|
||||
(byte*) fn2::BGCOL
|
||||
(byte) fn2::return
|
||||
(byte) fn2::return#0 20.0
|
||||
(void()) main()
|
||||
(byte~) main::$0 22.0
|
||||
(byte~) main::$4 22.0
|
||||
(byte*) main::SCREEN
|
||||
(byte()*) main::f
|
||||
(byte()*) main::f#3 11.0
|
||||
(byte) main::i
|
||||
(byte) main::i#1 4.714285714285714
|
||||
(byte) main::i#2 22.0
|
||||
|
||||
Initial phi equivalence classes
|
||||
[ main::i#2 main::i#1 ]
|
||||
[ main::f#3 ]
|
||||
Added variable main::$0 to zero page equivalence class [ main::$0 ]
|
||||
Added variable main::$4 to zero page equivalence class [ main::$4 ]
|
||||
Added variable fn2::return#0 to zero page equivalence class [ fn2::return#0 ]
|
||||
Added variable fn1::return#0 to zero page equivalence class [ fn1::return#0 ]
|
||||
Complete equivalence classes
|
||||
[ main::i#2 main::i#1 ]
|
||||
[ main::f#3 ]
|
||||
[ main::$0 ]
|
||||
[ main::$4 ]
|
||||
[ fn2::return#0 ]
|
||||
[ fn1::return#0 ]
|
||||
Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
|
||||
Allocated zp ZP_WORD:3 [ main::f#3 ]
|
||||
Allocated zp ZP_BYTE:5 [ main::$0 ]
|
||||
Allocated zp ZP_BYTE:6 [ main::$4 ]
|
||||
Allocated zp ZP_BYTE:7 [ fn2::return#0 ]
|
||||
Allocated zp ZP_BYTE:8 [ fn1::return#0 ]
|
||||
|
||||
INITIAL ASM
|
||||
//SEG0 File Comments
|
||||
// Tests creating and assigning pointers to non-args return with function value
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
//SEG3 @begin
|
||||
bbegin:
|
||||
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
b1_from_bbegin:
|
||||
jmp b1
|
||||
//SEG5 @1
|
||||
b1:
|
||||
//SEG6 [2] call main
|
||||
//SEG7 [4] phi from @1 to main [phi:@1->main]
|
||||
main_from_b1:
|
||||
jsr main
|
||||
//SEG8 [3] phi from @1 to @end [phi:@1->@end]
|
||||
bend_from_b1:
|
||||
jmp bend
|
||||
//SEG9 @end
|
||||
bend:
|
||||
//SEG10 main
|
||||
main: {
|
||||
.label SCREEN = $400
|
||||
.label _0 = 5
|
||||
.label _4 = 6
|
||||
.label i = 2
|
||||
.label f = 3
|
||||
//SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
|
||||
b1_from_main:
|
||||
//SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1
|
||||
lda #0
|
||||
sta i
|
||||
jmp b1
|
||||
//SEG13 main::@1
|
||||
b1:
|
||||
jmp b2
|
||||
//SEG14 main::@2
|
||||
b2:
|
||||
//SEG15 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1
|
||||
inc i
|
||||
//SEG16 [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuz1=vbuz2_band_vbuc1
|
||||
lda #1
|
||||
and i
|
||||
sta _0
|
||||
//SEG17 [8] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3 -- vbuz1_eq_0_then_la1
|
||||
lda _0
|
||||
cmp #0
|
||||
beq b3_from_b2
|
||||
//SEG18 [9] phi from main::@2 to main::@4 [phi:main::@2->main::@4]
|
||||
b4_from_b2:
|
||||
jmp b4
|
||||
//SEG19 main::@4
|
||||
b4:
|
||||
//SEG20 [10] phi from main::@4 to main::@3 [phi:main::@4->main::@3]
|
||||
b3_from_b4:
|
||||
//SEG21 [10] phi (byte()*) main::f#3 = &(byte()) fn2() [phi:main::@4->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn2
|
||||
sta f
|
||||
lda #>fn2
|
||||
sta f+1
|
||||
jmp b3
|
||||
//SEG22 [10] phi from main::@2 to main::@3 [phi:main::@2->main::@3]
|
||||
b3_from_b2:
|
||||
//SEG23 [10] phi (byte()*) main::f#3 = &(byte()) fn1() [phi:main::@2->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn1
|
||||
sta f
|
||||
lda #>fn1
|
||||
sta f+1
|
||||
jmp b3
|
||||
//SEG24 main::@3
|
||||
b3:
|
||||
//SEG25 [11] (byte~) main::$4 ← ((byte)) (byte()*) main::f#3 -- vbuz1=_byte_pprz2
|
||||
lda f
|
||||
sta _4
|
||||
//SEG26 [12] *((const byte*) main::SCREEN#0) ← (byte~) main::$4 -- _deref_pbuc1=vbuz1
|
||||
lda _4
|
||||
sta SCREEN
|
||||
//SEG27 [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1]
|
||||
b1_from_b3:
|
||||
//SEG28 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy
|
||||
jmp b1
|
||||
}
|
||||
//SEG29 fn2
|
||||
fn2: {
|
||||
.label BGCOL = $d021
|
||||
.label return = 7
|
||||
//SEG30 [13] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BGCOL
|
||||
//SEG31 [14] (byte) fn2::return#0 ← *((const byte*) fn2::BGCOL#0) -- vbuz1=_deref_pbuc1
|
||||
lda BGCOL
|
||||
sta return
|
||||
jmp breturn
|
||||
//SEG32 fn2::@return
|
||||
breturn:
|
||||
//SEG33 [15] return
|
||||
rts
|
||||
}
|
||||
//SEG34 fn1
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
.label return = 8
|
||||
//SEG35 [16] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BORDERCOL
|
||||
//SEG36 [17] (byte) fn1::return#0 ← *((const byte*) fn1::BORDERCOL#0) -- vbuz1=_deref_pbuc1
|
||||
lda BORDERCOL
|
||||
sta return
|
||||
jmp breturn
|
||||
//SEG37 fn1::@return
|
||||
breturn:
|
||||
//SEG38 [18] return
|
||||
rts
|
||||
}
|
||||
|
||||
REGISTER UPLIFT POTENTIAL REGISTERS
|
||||
Statement [11] (byte~) main::$4 ← ((byte)) (byte()*) main::f#3 [ main::i#1 main::$4 ] ( main:2 [ main::i#1 main::$4 ] ) always clobbers reg byte a
|
||||
Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ]
|
||||
Statement [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 [ main::i#1 main::$0 ] ( main:2 [ main::i#1 main::$0 ] ) always clobbers reg byte a
|
||||
Statement [11] (byte~) main::$4 ← ((byte)) (byte()*) main::f#3 [ main::i#1 main::$4 ] ( main:2 [ main::i#1 main::$4 ] ) always clobbers reg byte a
|
||||
Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y ,
|
||||
Potential registers zp ZP_WORD:3 [ main::f#3 ] : zp ZP_WORD:3 ,
|
||||
Potential registers zp ZP_BYTE:5 [ main::$0 ] : zp ZP_BYTE:5 , reg byte a , reg byte x , reg byte y ,
|
||||
Potential registers zp ZP_BYTE:6 [ main::$4 ] : zp ZP_BYTE:6 , reg byte a , reg byte x , reg byte y ,
|
||||
Potential registers zp ZP_BYTE:7 [ fn2::return#0 ] : zp ZP_BYTE:7 , reg byte a , reg byte x , reg byte y ,
|
||||
Potential registers zp ZP_BYTE:8 [ fn1::return#0 ] : zp ZP_BYTE:8 , reg byte a , reg byte x , reg byte y ,
|
||||
|
||||
REGISTER UPLIFT SCOPES
|
||||
Uplift Scope [main] 26.71: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] 22: zp ZP_BYTE:5 [ main::$0 ] 22: zp ZP_BYTE:6 [ main::$4 ] 11: zp ZP_WORD:3 [ main::f#3 ]
|
||||
Uplift Scope [fn1] 20: zp ZP_BYTE:8 [ fn1::return#0 ]
|
||||
Uplift Scope [fn2] 20: zp ZP_BYTE:7 [ fn2::return#0 ]
|
||||
Uplift Scope []
|
||||
|
||||
Uplifting [main] best 631 combination reg byte x [ main::i#2 main::i#1 ] reg byte a [ main::$0 ] reg byte a [ main::$4 ] zp ZP_WORD:3 [ main::f#3 ]
|
||||
Uplifting [fn1] best 628 combination reg byte a [ fn1::return#0 ]
|
||||
Uplifting [fn2] best 625 combination reg byte a [ fn2::return#0 ]
|
||||
Uplifting [] best 625 combination
|
||||
Allocated (was zp ZP_WORD:3) zp ZP_WORD:2 [ main::f#3 ]
|
||||
|
||||
ASSEMBLER BEFORE OPTIMIZATION
|
||||
//SEG0 File Comments
|
||||
// Tests creating and assigning pointers to non-args return with function value
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(bbegin)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
//SEG3 @begin
|
||||
bbegin:
|
||||
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
b1_from_bbegin:
|
||||
jmp b1
|
||||
//SEG5 @1
|
||||
b1:
|
||||
//SEG6 [2] call main
|
||||
//SEG7 [4] phi from @1 to main [phi:@1->main]
|
||||
main_from_b1:
|
||||
jsr main
|
||||
//SEG8 [3] phi from @1 to @end [phi:@1->@end]
|
||||
bend_from_b1:
|
||||
jmp bend
|
||||
//SEG9 @end
|
||||
bend:
|
||||
//SEG10 main
|
||||
main: {
|
||||
.label SCREEN = $400
|
||||
.label f = 2
|
||||
//SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
|
||||
b1_from_main:
|
||||
//SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1
|
||||
ldx #0
|
||||
jmp b1
|
||||
//SEG13 main::@1
|
||||
b1:
|
||||
jmp b2
|
||||
//SEG14 main::@2
|
||||
b2:
|
||||
//SEG15 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx
|
||||
inx
|
||||
//SEG16 [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuxx_band_vbuc1
|
||||
txa
|
||||
and #1
|
||||
//SEG17 [8] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3 -- vbuaa_eq_0_then_la1
|
||||
cmp #0
|
||||
beq b3_from_b2
|
||||
//SEG18 [9] phi from main::@2 to main::@4 [phi:main::@2->main::@4]
|
||||
b4_from_b2:
|
||||
jmp b4
|
||||
//SEG19 main::@4
|
||||
b4:
|
||||
//SEG20 [10] phi from main::@4 to main::@3 [phi:main::@4->main::@3]
|
||||
b3_from_b4:
|
||||
//SEG21 [10] phi (byte()*) main::f#3 = &(byte()) fn2() [phi:main::@4->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn2
|
||||
sta f
|
||||
lda #>fn2
|
||||
sta f+1
|
||||
jmp b3
|
||||
//SEG22 [10] phi from main::@2 to main::@3 [phi:main::@2->main::@3]
|
||||
b3_from_b2:
|
||||
//SEG23 [10] phi (byte()*) main::f#3 = &(byte()) fn1() [phi:main::@2->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn1
|
||||
sta f
|
||||
lda #>fn1
|
||||
sta f+1
|
||||
jmp b3
|
||||
//SEG24 main::@3
|
||||
b3:
|
||||
//SEG25 [11] (byte~) main::$4 ← ((byte)) (byte()*) main::f#3 -- vbuaa=_byte_pprz1
|
||||
lda f
|
||||
//SEG26 [12] *((const byte*) main::SCREEN#0) ← (byte~) main::$4 -- _deref_pbuc1=vbuaa
|
||||
sta SCREEN
|
||||
//SEG27 [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1]
|
||||
b1_from_b3:
|
||||
//SEG28 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy
|
||||
jmp b1
|
||||
}
|
||||
//SEG29 fn2
|
||||
fn2: {
|
||||
.label BGCOL = $d021
|
||||
//SEG30 [13] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BGCOL
|
||||
//SEG31 [14] (byte) fn2::return#0 ← *((const byte*) fn2::BGCOL#0) -- vbuaa=_deref_pbuc1
|
||||
lda BGCOL
|
||||
jmp breturn
|
||||
//SEG32 fn2::@return
|
||||
breturn:
|
||||
//SEG33 [15] return
|
||||
rts
|
||||
}
|
||||
//SEG34 fn1
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
//SEG35 [16] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BORDERCOL
|
||||
//SEG36 [17] (byte) fn1::return#0 ← *((const byte*) fn1::BORDERCOL#0) -- vbuaa=_deref_pbuc1
|
||||
lda BORDERCOL
|
||||
jmp breturn
|
||||
//SEG37 fn1::@return
|
||||
breturn:
|
||||
//SEG38 [18] return
|
||||
rts
|
||||
}
|
||||
|
||||
ASSEMBLER OPTIMIZATIONS
|
||||
Removing instruction jmp b1
|
||||
Removing instruction jmp bend
|
||||
Removing instruction jmp b1
|
||||
Removing instruction jmp b2
|
||||
Removing instruction jmp b4
|
||||
Removing instruction jmp b3
|
||||
Removing instruction jmp breturn
|
||||
Removing instruction jmp breturn
|
||||
Succesful ASM optimization Pass5NextJumpElimination
|
||||
Replacing label b1 with b2
|
||||
Removing instruction b1_from_bbegin:
|
||||
Removing instruction b1:
|
||||
Removing instruction main_from_b1:
|
||||
Removing instruction bend_from_b1:
|
||||
Removing instruction b1:
|
||||
Removing instruction b4_from_b2:
|
||||
Removing instruction b3_from_b4:
|
||||
Succesful ASM optimization Pass5RedundantLabelElimination
|
||||
Removing instruction bend:
|
||||
Removing instruction b1_from_main:
|
||||
Removing instruction b4:
|
||||
Removing instruction b1_from_b3:
|
||||
Removing instruction breturn:
|
||||
Removing instruction breturn:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
Updating BasicUpstart to call main directly
|
||||
Removing instruction jsr main
|
||||
Succesful ASM optimization Pass5SkipBegin
|
||||
Relabelling long label b3_from_b2 to b1
|
||||
Succesful ASM optimization Pass5RelabelLongLabels
|
||||
Removing instruction bbegin:
|
||||
Succesful ASM optimization Pass5UnusedLabelElimination
|
||||
|
||||
FINAL SYMBOL TABLE
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(byte()) fn1()
|
||||
(label) fn1::@return
|
||||
(byte*) fn1::BORDERCOL
|
||||
(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020
|
||||
(byte) fn1::return
|
||||
(byte) fn1::return#0 reg byte a 20.0
|
||||
(byte()) fn2()
|
||||
(label) fn2::@return
|
||||
(byte*) fn2::BGCOL
|
||||
(const byte*) fn2::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) $d021
|
||||
(byte) fn2::return
|
||||
(byte) fn2::return#0 reg byte a 20.0
|
||||
(void()) main()
|
||||
(byte~) main::$0 reg byte a 22.0
|
||||
(byte~) main::$4 reg byte a 22.0
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@3
|
||||
(label) main::@4
|
||||
(byte*) main::SCREEN
|
||||
(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400
|
||||
(byte()*) main::f
|
||||
(byte()*) main::f#3 f zp ZP_WORD:2 11.0
|
||||
(byte) main::i
|
||||
(byte) main::i#1 reg byte x 4.714285714285714
|
||||
(byte) main::i#2 reg byte x 22.0
|
||||
|
||||
reg byte x [ main::i#2 main::i#1 ]
|
||||
zp ZP_WORD:2 [ main::f#3 ]
|
||||
reg byte a [ main::$0 ]
|
||||
reg byte a [ main::$4 ]
|
||||
reg byte a [ fn2::return#0 ]
|
||||
reg byte a [ fn1::return#0 ]
|
||||
|
||||
|
||||
FINAL ASSEMBLER
|
||||
Score: 487
|
||||
|
||||
//SEG0 File Comments
|
||||
// Tests creating and assigning pointers to non-args return with function value
|
||||
//SEG1 Basic Upstart
|
||||
.pc = $801 "Basic"
|
||||
:BasicUpstart(main)
|
||||
.pc = $80d "Program"
|
||||
//SEG2 Global Constants & labels
|
||||
//SEG3 @begin
|
||||
//SEG4 [1] phi from @begin to @1 [phi:@begin->@1]
|
||||
//SEG5 @1
|
||||
//SEG6 [2] call main
|
||||
//SEG7 [4] phi from @1 to main [phi:@1->main]
|
||||
//SEG8 [3] phi from @1 to @end [phi:@1->@end]
|
||||
//SEG9 @end
|
||||
//SEG10 main
|
||||
main: {
|
||||
.label SCREEN = $400
|
||||
.label f = 2
|
||||
//SEG11 [5] phi from main to main::@1 [phi:main->main::@1]
|
||||
//SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1
|
||||
ldx #0
|
||||
//SEG13 main::@1
|
||||
//SEG14 main::@2
|
||||
b2:
|
||||
//SEG15 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx
|
||||
inx
|
||||
//SEG16 [7] (byte~) main::$0 ← (byte) main::i#1 & (byte/signed byte/word/signed word/dword/signed dword) 1 -- vbuaa=vbuxx_band_vbuc1
|
||||
txa
|
||||
and #1
|
||||
//SEG17 [8] if((byte~) main::$0==(byte/signed byte/word/signed word/dword/signed dword) 0) goto main::@3 -- vbuaa_eq_0_then_la1
|
||||
cmp #0
|
||||
beq b1
|
||||
//SEG18 [9] phi from main::@2 to main::@4 [phi:main::@2->main::@4]
|
||||
//SEG19 main::@4
|
||||
//SEG20 [10] phi from main::@4 to main::@3 [phi:main::@4->main::@3]
|
||||
//SEG21 [10] phi (byte()*) main::f#3 = &(byte()) fn2() [phi:main::@4->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn2
|
||||
sta f
|
||||
lda #>fn2
|
||||
sta f+1
|
||||
jmp b3
|
||||
//SEG22 [10] phi from main::@2 to main::@3 [phi:main::@2->main::@3]
|
||||
b1:
|
||||
//SEG23 [10] phi (byte()*) main::f#3 = &(byte()) fn1() [phi:main::@2->main::@3#0] -- pprz1=pprc1
|
||||
lda #<fn1
|
||||
sta f
|
||||
lda #>fn1
|
||||
sta f+1
|
||||
//SEG24 main::@3
|
||||
b3:
|
||||
//SEG25 [11] (byte~) main::$4 ← ((byte)) (byte()*) main::f#3 -- vbuaa=_byte_pprz1
|
||||
lda f
|
||||
//SEG26 [12] *((const byte*) main::SCREEN#0) ← (byte~) main::$4 -- _deref_pbuc1=vbuaa
|
||||
sta SCREEN
|
||||
//SEG27 [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1]
|
||||
//SEG28 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy
|
||||
jmp b2
|
||||
}
|
||||
//SEG29 fn2
|
||||
fn2: {
|
||||
.label BGCOL = $d021
|
||||
//SEG30 [13] *((const byte*) fn2::BGCOL#0) ← ++ *((const byte*) fn2::BGCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BGCOL
|
||||
//SEG31 [14] (byte) fn2::return#0 ← *((const byte*) fn2::BGCOL#0) -- vbuaa=_deref_pbuc1
|
||||
lda BGCOL
|
||||
//SEG32 fn2::@return
|
||||
//SEG33 [15] return
|
||||
rts
|
||||
}
|
||||
//SEG34 fn1
|
||||
fn1: {
|
||||
.label BORDERCOL = $d020
|
||||
//SEG35 [16] *((const byte*) fn1::BORDERCOL#0) ← ++ *((const byte*) fn1::BORDERCOL#0) -- _deref_pbuc1=_inc__deref_pbuc1
|
||||
inc BORDERCOL
|
||||
//SEG36 [17] (byte) fn1::return#0 ← *((const byte*) fn1::BORDERCOL#0) -- vbuaa=_deref_pbuc1
|
||||
lda BORDERCOL
|
||||
//SEG37 fn1::@return
|
||||
//SEG38 [18] return
|
||||
rts
|
||||
}
|
||||
|
36
src/test/ref/function-pointer-return.sym
Normal file
36
src/test/ref/function-pointer-return.sym
Normal file
@ -0,0 +1,36 @@
|
||||
(label) @1
|
||||
(label) @begin
|
||||
(label) @end
|
||||
(byte()) fn1()
|
||||
(label) fn1::@return
|
||||
(byte*) fn1::BORDERCOL
|
||||
(const byte*) fn1::BORDERCOL#0 BORDERCOL = ((byte*))(word/dword/signed dword) $d020
|
||||
(byte) fn1::return
|
||||
(byte) fn1::return#0 reg byte a 20.0
|
||||
(byte()) fn2()
|
||||
(label) fn2::@return
|
||||
(byte*) fn2::BGCOL
|
||||
(const byte*) fn2::BGCOL#0 BGCOL = ((byte*))(word/dword/signed dword) $d021
|
||||
(byte) fn2::return
|
||||
(byte) fn2::return#0 reg byte a 20.0
|
||||
(void()) main()
|
||||
(byte~) main::$0 reg byte a 22.0
|
||||
(byte~) main::$4 reg byte a 22.0
|
||||
(label) main::@1
|
||||
(label) main::@2
|
||||
(label) main::@3
|
||||
(label) main::@4
|
||||
(byte*) main::SCREEN
|
||||
(const byte*) main::SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400
|
||||
(byte()*) main::f
|
||||
(byte()*) main::f#3 f zp ZP_WORD:2 11.0
|
||||
(byte) main::i
|
||||
(byte) main::i#1 reg byte x 4.714285714285714
|
||||
(byte) main::i#2 reg byte x 22.0
|
||||
|
||||
reg byte x [ main::i#2 main::i#1 ]
|
||||
zp ZP_WORD:2 [ main::f#3 ]
|
||||
reg byte a [ main::$0 ]
|
||||
reg byte a [ main::$4 ]
|
||||
reg byte a [ fn2::return#0 ]
|
||||
reg byte a [ fn1::return#0 ]
|
Loading…
Reference in New Issue
Block a user