diff --git a/src/main/fragment/vbuaa=_byte_pprz1.asm b/src/main/fragment/vbuaa=_byte_pprz1.asm new file mode 100644 index 000000000..89049a799 --- /dev/null +++ b/src/main/fragment/vbuaa=_byte_pprz1.asm @@ -0,0 +1 @@ +lda {z1} \ No newline at end of file diff --git a/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java b/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java index 9aa91c71e..9788060fd 100644 --- a/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java +++ b/src/main/java/dk/camelot64/kickc/fragment/AsmFormat.java @@ -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); diff --git a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraphBaseVisitor.java b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraphBaseVisitor.java index f371e8841..40e9167f7 100644 --- a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraphBaseVisitor.java +++ b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraphBaseVisitor.java @@ -33,6 +33,8 @@ public class ControlFlowGraphBaseVisitor { 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 { return null; } + public T visitCallPointer(StatementCallPointer call) { + return null; + } + public T visitAsm(StatementAsm asm) { return null; } diff --git a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraphCopyVisitor.java b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraphCopyVisitor.java index ed1cd7e9f..3be8c3abc 100644 --- a/src/main/java/dk/camelot64/kickc/model/ControlFlowGraphCopyVisitor.java +++ b/src/main/java/dk/camelot64/kickc/model/ControlFlowGraphCopyVisitor.java @@ -173,6 +173,14 @@ public class ControlFlowGraphCopyVisitor extends ControlFlowGraphBaseVisitor 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()); diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java index a6499b3d9..4d7a753b3 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValue.java @@ -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; diff --git a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java index cbfe5bc53..7ca0243de 100644 --- a/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java +++ b/src/main/java/dk/camelot64/kickc/model/iterator/ProgramValueIterator.java @@ -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); diff --git a/src/main/java/dk/camelot64/kickc/model/statements/StatementCall.java b/src/main/java/dk/camelot64/kickc/model/statements/StatementCall.java index 364f82321..66d29cdf7 100644 --- a/src/main/java/dk/camelot64/kickc/model/statements/StatementCall.java +++ b/src/main/java/dk/camelot64/kickc/model/statements/StatementCall.java @@ -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 parameters; public StatementCall(LValue lValue, String procedureName, List parameters, StatementSource source, List comments) { diff --git a/src/main/java/dk/camelot64/kickc/model/statements/StatementCallPointer.java b/src/main/java/dk/camelot64/kickc/model/statements/StatementCallPointer.java new file mode 100644 index 000000000..9afbc49c1 --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/statements/StatementCallPointer.java @@ -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 parameters; + + public StatementCallPointer(LValue lValue, RValue procedure, List parameters, StatementSource source, List 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 getParameters() { + return parameters; + } + + public void setParameters(List 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); + } +} diff --git a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java index c7067812b..603b275e8 100644 --- a/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java +++ b/src/main/java/dk/camelot64/kickc/model/types/SymbolTypeInference.java @@ -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); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 09c1368fa..78b55c696 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -1120,13 +1120,6 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { @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 parameters; KickCParser.ParameterListContext parameterList = ctx.parameterList(); if(parameterList != null) { @@ -1136,7 +1129,15 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { } 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; } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1TypeInference.java b/src/main/java/dk/camelot64/kickc/passes/Pass1TypeInference.java index b497c570c..67eaa8d0a 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1TypeInference.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1TypeInference.java @@ -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; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertSymbols.java b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertSymbols.java index 17c64d44a..5996be8db 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2AssertSymbols.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2AssertSymbols.java @@ -19,7 +19,7 @@ public class Pass2AssertSymbols extends Pass2SsaAssertion { SymbolFinder symbolFinder = new SymbolFinder(getScope()); symbolFinder.visitGraph(getGraph()); HashSet 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()) { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 210656012..2b484228c 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -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 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); } diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java index 1e1c2355b..35565c966 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNEliminateUnusedVars.java @@ -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 phiVarIt = statementPhi.getPhiVariables().listIterator(); diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNVariableReferenceInfos.java b/src/main/java/dk/camelot64/kickc/passes/PassNVariableReferenceInfos.java index 2fa3a0139..cdb7bdd3e 100644 --- a/src/main/java/dk/camelot64/kickc/passes/PassNVariableReferenceInfos.java +++ b/src/main/java/dk/camelot64/kickc/passes/PassNVariableReferenceInfos.java @@ -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 defined = new ArrayList<>(); + if(((StatementCallPointer) stmt).getlValue() instanceof VariableRef) { + defined.add((VariableRef) ((StatementCallPointer) stmt).getlValue()); + } + return defined; } return new ArrayList<>(); } diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 6f9e7accc..e79f9fca7 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -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"); diff --git a/src/test/kc/function-pointer-noarg-call-2.kc b/src/test/kc/function-pointer-noarg-call-2.kc new file mode 100644 index 000000000..a22dcd6f3 --- /dev/null +++ b/src/test/kc/function-pointer-noarg-call-2.kc @@ -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)++; +} \ No newline at end of file diff --git a/src/test/kc/function-pointer-noarg-call.kc b/src/test/kc/function-pointer-noarg-call.kc new file mode 100644 index 000000000..a4e65cc21 --- /dev/null +++ b/src/test/kc/function-pointer-noarg-call.kc @@ -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)++; +} diff --git a/src/test/kc/function-pointer-return.kc b/src/test/kc/function-pointer-return.kc new file mode 100644 index 000000000..af2d5ff84 --- /dev/null +++ b/src/test/kc/function-pointer-return.kc @@ -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; +} \ No newline at end of file diff --git a/src/test/ref/function-pointer-noarg-call-2.asm b/src/test/ref/function-pointer-noarg-call-2.asm new file mode 100644 index 000000000..a18cff166 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-2.asm @@ -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+1 + jmp b3 + b1: + 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 +} diff --git a/src/test/ref/function-pointer-noarg-call-2.cfg b/src/test/ref/function-pointer-noarg-call-2.cfg new file mode 100644 index 000000000..9bcac2181 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-2.cfg @@ -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 diff --git a/src/test/ref/function-pointer-noarg-call-2.log b/src/test/ref/function-pointer-noarg-call-2.log new file mode 100644 index 000000000..9563bb99a --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-2.log @@ -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+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+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+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+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+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+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 +} + diff --git a/src/test/ref/function-pointer-noarg-call-2.sym b/src/test/ref/function-pointer-noarg-call-2.sym new file mode 100644 index 000000000..023184446 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call-2.sym @@ -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 ] diff --git a/src/test/ref/function-pointer-noarg-call.asm b/src/test/ref/function-pointer-noarg-call.asm new file mode 100644 index 000000000..d63b2fceb --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call.asm @@ -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 +} diff --git a/src/test/ref/function-pointer-noarg-call.cfg b/src/test/ref/function-pointer-noarg-call.cfg new file mode 100644 index 000000000..aceeabe29 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call.cfg @@ -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 diff --git a/src/test/ref/function-pointer-noarg-call.log b/src/test/ref/function-pointer-noarg-call.log new file mode 100644 index 000000000..8dc49bcc9 --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call.log @@ -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 +} + diff --git a/src/test/ref/function-pointer-noarg-call.sym b/src/test/ref/function-pointer-noarg-call.sym new file mode 100644 index 000000000..818f86d4b --- /dev/null +++ b/src/test/ref/function-pointer-noarg-call.sym @@ -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() + diff --git a/src/test/ref/function-pointer-return.asm b/src/test/ref/function-pointer-return.asm new file mode 100644 index 000000000..575f1cc9a --- /dev/null +++ b/src/test/ref/function-pointer-return.asm @@ -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+1 + jmp b3 + b1: + 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 +} diff --git a/src/test/ref/function-pointer-return.cfg b/src/test/ref/function-pointer-return.cfg new file mode 100644 index 000000000..d6bc05b9a --- /dev/null +++ b/src/test/ref/function-pointer-return.cfg @@ -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 diff --git a/src/test/ref/function-pointer-return.log b/src/test/ref/function-pointer-return.log new file mode 100644 index 000000000..28593cc21 --- /dev/null +++ b/src/test/ref/function-pointer-return.log @@ -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+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+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+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+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+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+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 +} + diff --git a/src/test/ref/function-pointer-return.sym b/src/test/ref/function-pointer-return.sym new file mode 100644 index 000000000..cf785061e --- /dev/null +++ b/src/test/ref/function-pointer-return.sym @@ -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 ]