diff --git a/pom.xml b/pom.xml index 446cc4bd1..e4abd072c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,8 +5,8 @@ 4.0.0 - 8 - 8 + 17 + 17 UTF-8 -Xmx2048m @@ -47,24 +47,24 @@ org.antlr antlr4 - 4.9.3 + 4.12.0 provided org.antlr antlr4-runtime - 4.9.3 + 4.12.0 org.junit.jupiter junit-jupiter-engine - 5.8.2 + 5.9.2 test info.picocli picocli - 4.6.2 + 4.7.1 javax.json @@ -124,7 +124,7 @@ org.antlr antlr4-maven-plugin - 4.9.3 + 4.12.0 antlr @@ -152,7 +152,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + 3.0.0-M9 **/TestFragments.java @@ -167,7 +167,7 @@ org.jacoco jacoco-maven-plugin - 0.8.8 + 0.8.9 pre-unit-test @@ -187,7 +187,7 @@ org.apache.maven.plugins maven-jar-plugin - 3.2.0 + 3.3.0 @@ -201,7 +201,7 @@ org.apache.felix maven-bundle-plugin - 3.5.0 + 5.1.8 bundle-manifest @@ -214,7 +214,7 @@ maven-assembly-plugin - 3.3.0 + 3.5.0 kickc posix diff --git a/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 b/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 index 0bfba0d7d..9dcea6f49 100644 --- a/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 +++ b/src/main/antlr4/dk/camelot64/kickc/parser/KickCParser.g4 @@ -136,8 +136,7 @@ parameterDecl ; pragma - : PRAGMA NAME - | PRAGMA NAME (PAR_BEGIN pragmaParam (COMMA pragmaParam)* PAR_END)? + : PRAGMA NAME PAR_BEGIN (pragmaParam (COMMA pragmaParam)* )? PAR_END ; pragmaParam @@ -245,9 +244,10 @@ expr | expr ( '&&' ) expr #exprBinary | expr ( '||' ) expr #exprBinary | expr '?' expr COLON expr #exprTernary - | expr '=' expr #exprAssignment + | expr ASSIGN expr #exprAssignment | expr ASSIGN_COMPOUND expr #exprAssignmentCompound | CURLY_BEGIN expr (COMMA expr )* COMMA? CURLY_END #initList + | CURLY_BEGIN DOT NAME ASSIGN expr CURLY_END #initUnion | NAME #exprId | NUMBER #exprNumber | STRING+ #exprString diff --git a/src/main/fragment/mos6502-common/pbuz2_derefidx_vbuyy_lt_pbuz1_derefidx_vbuyy_then_la1.asm b/src/main/fragment/mos6502-common/pbuz2_derefidx_vbuyy_lt_pbuz1_derefidx_vbuyy_then_la1.asm new file mode 100644 index 000000000..298782bba --- /dev/null +++ b/src/main/fragment/mos6502-common/pbuz2_derefidx_vbuyy_lt_pbuz1_derefidx_vbuyy_then_la1.asm @@ -0,0 +1,3 @@ +lda ({z1}),y +cmp ({z2}),y +bcc {la1} diff --git a/src/main/java/dk/camelot64/kickc/model/Initializers.java b/src/main/java/dk/camelot64/kickc/model/Initializers.java index 6db8334eb..31f5cc2fe 100644 --- a/src/main/java/dk/camelot64/kickc/model/Initializers.java +++ b/src/main/java/dk/camelot64/kickc/model/Initializers.java @@ -89,6 +89,8 @@ public class Initializers { return new ConstantCastValue(toType, (ConstantValue) constantSub); } } + } else if(initValue instanceof UnionDesignator) { + initValue = constantifyUnion((UnionDesignator) initValue, (SymbolTypeStruct) typeSpec.getType(), program, source); } else if(initValue instanceof ValueList) { ValueList initList = (ValueList) initValue; if(typeSpec.getType() instanceof SymbolTypePointer && ((SymbolTypePointer) typeSpec.getType()).getArraySpec() != null) { @@ -96,7 +98,7 @@ public class Initializers { initValue = constantifyArray(initList, (SymbolTypePointer) typeSpec.getType(), program, source); } else if(typeSpec.getType() instanceof SymbolTypeStruct) { // Type is a struct - initValue = constantifyStruct(initList, (SymbolTypeStruct) typeSpec.getType(), program, source); + initValue = constantifyStructOrUnion(initList, (SymbolTypeStruct) typeSpec.getType(), program, source); } else { throw new CompileError("Value list cannot initialize type " + typeSpec.getType(), source); } @@ -127,6 +129,41 @@ public class Initializers { return initValue; } + /** + * Convert a union designator initializer to a constant. + * + * @param unionInit The union initializer + * @param structType The union type + * @param program The program + * @param source The source line + * @return The constantified value + */ + private static RValue constantifyUnion(UnionDesignator unionInit, SymbolTypeStruct structType, Program program, StatementSource source) { + StructDefinition structDefinition = structType.getStructDefinition(program.getScope()); + Collection memberDefinitions = structDefinition.getAllVars(false); + + final String memberName = unionInit.getMemberName(); + final RValue initValue = unionInit.getMemberValue(); + + Variable memberDef = null; + for(Variable definition : memberDefinitions) { + if(definition.getLocalName().equals(memberName)) { + memberDef = definition; + } + } + if(memberDef==null) + throw new CompileError( "Union member not found", source); + + RValue constantifiedMemberValue = constantify(initValue, new ValueTypeSpec(memberDef.getType()), program, source); + if(constantifiedMemberValue instanceof ConstantValue) { + LinkedHashMap constMemberMap = new LinkedHashMap<>(); + constMemberMap.put(memberDef.getRef(), (ConstantValue) constantifiedMemberValue); + return new ConstantStructValue(structType, constMemberMap); + } else { + throw new CompileError( "Union initializer is not constant", source); + } + } + /** * Convert as much as possible of a struct to constants. * @@ -136,7 +173,7 @@ public class Initializers { * @param source The source line * @return The constantified value */ - private static RValue constantifyStruct(ValueList valueList, SymbolTypeStruct structType, Program program, StatementSource source) { + private static RValue constantifyStructOrUnion(ValueList valueList, SymbolTypeStruct structType, Program program, StatementSource source) { // Recursively cast all sub-elements StructDefinition structDefinition = structType.getStructDefinition(program.getScope()); Collection memberDefinitions = structDefinition.getAllVars(false); @@ -144,7 +181,7 @@ public class Initializers { if(structInitNeedSize != valueList.getList().size()) { if(structDefinition.isUnion()) { throw new CompileError( - "Union initializer has too many values, since only one is allowed.\n" + + "Union initializer has wrong size. One value is required.\n" + " Union initializer: " + valueList.toString(program), source); } else { diff --git a/src/main/java/dk/camelot64/kickc/model/statements/StatementPhiBlock.java b/src/main/java/dk/camelot64/kickc/model/statements/StatementPhiBlock.java index 11cdbb587..bb1d018ee 100644 --- a/src/main/java/dk/camelot64/kickc/model/statements/StatementPhiBlock.java +++ b/src/main/java/dk/camelot64/kickc/model/statements/StatementPhiBlock.java @@ -1,13 +1,11 @@ package dk.camelot64.kickc.model.statements; import dk.camelot64.kickc.model.Comment; -import dk.camelot64.kickc.model.values.LabelRef; import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.values.LabelRef; import dk.camelot64.kickc.model.values.RValue; import dk.camelot64.kickc.model.values.VariableRef; -import org.antlr.v4.runtime.RuleContext; -import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -24,7 +22,7 @@ public class StatementPhiBlock extends StatementBase { private List phiVariables; public StatementPhiBlock(List comments) { - super(new StatementSource(RuleContext.EMPTY), comments); + super(StatementSource.NONE, comments); this.phiVariables = new ArrayList<>(); } diff --git a/src/main/java/dk/camelot64/kickc/model/values/UnionDesignator.java b/src/main/java/dk/camelot64/kickc/model/values/UnionDesignator.java new file mode 100644 index 000000000..6811766db --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/model/values/UnionDesignator.java @@ -0,0 +1,40 @@ +package dk.camelot64.kickc.model.values; + +import dk.camelot64.kickc.model.Program; + +/** + * A union designator initializer. + */ +public class UnionDesignator implements RValue { + + private final String memberName; + + private final RValue rValue; + + public UnionDesignator(String memberName, RValue rValue) { + this.memberName = memberName; + this.rValue = rValue; + } + + public String getMemberName() { return memberName; } + + public RValue getMemberValue() { + return rValue; + } + + @Override + public String toString(Program program) { + StringBuilder out = new StringBuilder(); + out.append("{ "); + out.append(memberName); + out.append("="); + out.append(rValue.toString(program)); + out.append(" }"); + return out.toString(); + } + + @Override + public String toString() { + return toString(null); + } +} diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 4a3dcaf4b..b1a939feb 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -136,7 +136,7 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor()); program.getScope().add(initProc); program.createProcedureCompilation(initProc.getRef()); - program.getProcedureCompilation(initProc.getRef()).getStatementSequence().addStatement(new StatementProcedureBegin(initProc.getRef(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); + program.getProcedureCompilation(initProc.getRef()).getStatementSequence().addStatement(new StatementProcedureBegin(initProc.getRef(), StatementSource.NONE, Comment.NO_COMMENTS)); } return initProc; } @@ -180,9 +180,9 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); + startSequence.addStatement(new StatementCall(null, SymbolRef.INIT_PROC_NAME, new ArrayList<>(), StatementSource.NONE, Comment.NO_COMMENTS)); final Procedure mainProc = program.getScope().getLocalProcedure(SymbolRef.MAIN_PROC_NAME); if(mainProc == null) throw new CompileError("Required main() not defined in program."); if(!SymbolType.VOID.equals(mainProc.getReturnType()) && !SymbolType.SWORD.equals(mainProc.getReturnType())) throw new CompileError("return of main() must be 'void' or of type 'int'.", mainProc.getDefinitionSource()); if(mainProc.getParameterNames().size() == 0) { - startSequence.addStatement(new StatementCall(null, SymbolRef.MAIN_PROC_NAME, new ArrayList<>(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); + startSequence.addStatement(new StatementCall(null, SymbolRef.MAIN_PROC_NAME, new ArrayList<>(), StatementSource.NONE, Comment.NO_COMMENTS)); } else if(mainProc.getParameterNames().size() == 2) { final List parameters = mainProc.getParameters(); final Variable argc = parameters.get(0); @@ -214,15 +214,15 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor params = new ArrayList<>(); params.add(new ConstantInteger(0L, SymbolType.SWORD)); params.add(new ConstantPointer(0L, new SymbolTypePointer(SymbolType.BYTE))); - startSequence.addStatement(new StatementCall(null, SymbolRef.MAIN_PROC_NAME, params, new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); + startSequence.addStatement(new StatementCall(null, SymbolRef.MAIN_PROC_NAME, params, StatementSource.NONE, Comment.NO_COMMENTS)); } else throw new CompileError("main() has wrong number of parameters. It must have zero or 2 parameters.", mainProc.getDefinitionSource()); final Label startReturnLabel = startProcedure.addLabel(SymbolRef.PROCEXIT_BLOCK_NAME); - startSequence.addStatement(new StatementLabel(startReturnLabel.getRef(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); - startSequence.addStatement(new StatementReturn(null, new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); - startSequence.addStatement(new StatementProcedureEnd(startProcedure.getRef(), new StatementSource(RuleContext.EMPTY), Comment.NO_COMMENTS)); + startSequence.addStatement(new StatementLabel(startReturnLabel.getRef(), StatementSource.NONE, Comment.NO_COMMENTS)); + startSequence.addStatement(new StatementReturn(null, StatementSource.NONE, Comment.NO_COMMENTS)); + startSequence.addStatement(new StatementProcedureEnd(startProcedure.getRef(), StatementSource.NONE, Comment.NO_COMMENTS)); } } @@ -1963,13 +1963,20 @@ public class Pass0GenerateStatementSequence extends KickCParserBaseVisitor initValues = new ArrayList<>(); for(KickCParser.ExprContext initializer : ctx.expr()) { RValue rValue = (RValue) visit(initializer); - initValues.add(rValue); - } + initValues.add(rValue); + } return new ValueList(initValues); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1CallVar.java b/src/main/java/dk/camelot64/kickc/passes/Pass1CallVar.java index fcf013ba0..62aa6513b 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1CallVar.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1CallVar.java @@ -62,9 +62,7 @@ public class Pass1CallVar extends Pass2SsaOptimization { final LValue lValue = call.getlValue(); if(lValue!=null) { Variable returnVar = procedure.getLocalVariable("return"); - stmtIt.previous(); generateCallFinalize(lValue, returnVar, source, comments, stmtIt, statement); - stmtIt.next(); } stmtIt.remove(); } @@ -105,7 +103,9 @@ public class Pass1CallVar extends Pass2SsaOptimization { if(!(lValue instanceof ValueList) || !(returnType instanceof SymbolTypeStruct)) { // A simple value - add simple assignment final StatementAssignment stackPull = new StatementAssignment(lValue, returnVar.getRef(), false, source, comments); + stmtIt.previous(); stmtIt.add(stackPull); + stmtIt.next(); getLog().append("Calling convention " + Procedure.CallingConvention.VAR_CALL + " adding return value assignment " + stackPull); } else { final CastValue structLValue = new CastValue(returnType, lValue); @@ -113,21 +113,6 @@ public class Pass1CallVar extends Pass2SsaOptimization { final ValueSource lValueSource = ValueSourceFactory.getValueSource(structLValue, getProgram(), getScope(), currentStmt, stmtIt, null); final ValueSource rValueSource = ValueSourceFactory.getValueSource(returnVar.getRef(), getProgram(), getScope(), currentStmt, stmtIt, null); Pass1UnwindStructValues.copyValues(lValueSource, rValueSource, null, false, currentStmt, null, stmtIt, getProgram()); - - - /* - final List memberLValues = ((ValueList) lValue).getList(); - final StructVariableMemberUnwinding structVariableMemberUnwinding = getProgram().getStructVariableMemberUnwinding(); - final StructVariableMemberUnwinding.VariableUnwinding returnVarUnwinding = structVariableMemberUnwinding.getVariableUnwinding(returnVar.getRef()); - for(RValue memberLValue : memberLValues) { - - } - for(int i = 0; i < structMemberVars.size(); i++) { - final Variable memberVar = structMemberVars.get(i); - final RValue memberValue = memberLValues.get(i); - generateCallFinalize(memberValue, memberVar.getType(), source, comments, stmtIt); - } - */ } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java index df78a6bb4..bde8b4b30 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConstantInlining.java @@ -3,9 +3,7 @@ package dk.camelot64.kickc.passes; import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.iterator.ProgramValue; import dk.camelot64.kickc.model.iterator.ProgramValueIterator; -import dk.camelot64.kickc.model.symbols.ProgramScope; -import dk.camelot64.kickc.model.symbols.Symbol; -import dk.camelot64.kickc.model.symbols.Variable; +import dk.camelot64.kickc.model.symbols.*; import dk.camelot64.kickc.model.values.*; import dk.camelot64.kickc.passes.utils.AliasReplacer; @@ -55,10 +53,14 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization { replaceInValues(inline); // Replace all usages of the constants in the control flow graph or symbol table replaceVariables(inline); + // Remove parameters + final Set inlineRefs = inline.keySet(); + removeParameters(inlineRefs); // Remove from symbol table - deleteSymbols(getScope(), inline.keySet()); + deleteSymbols(getScope(), inlineRefs); - for(ConstantRef constantRef : inline.keySet()) { + + for(ConstantRef constantRef : inlineRefs) { getLog().append("Constant inlined " + constantRef.toString() + " = " + inline.get(constantRef).toString(getProgram())); } @@ -66,6 +68,30 @@ public class Pass2ConstantInlining extends Pass2SsaOptimization { } + private void removeParameters(Set inlineRefs) { + for(ConstantRef inlineRef : inlineRefs) { + final Scope scope = getScope().getConstant(inlineRef).getScope(); + final Procedure procedure = getProcedure(scope); + if(procedure!=null) { + final List parameters = procedure.getParameters(); + final boolean modified = parameters.removeIf(param -> param.getRef().equals(inlineRef)); + if(modified) { + procedure.setParameters(parameters); + getLog().append("Parameter inlined " + inlineRef.toString()); + } + } + } + } + + private static Procedure getProcedure(Scope scope) { + if(scope instanceof Procedure) + return (Procedure) scope; + else if(scope instanceof ProgramScope) + return null; + else + return getProcedure(scope.getScope()); + } + /** * Replace any aliases within the constant values themselves * diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegistersFinalize.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegistersFinalize.java index 545eb23e9..59c1a1480 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegistersFinalize.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegistersFinalize.java @@ -212,7 +212,7 @@ public class Pass4RegistersFinalize extends Pass2Base { reserved = false; int candidateZp = currentZp; for(int i=0;i pragmaBody = readBody(cTokenSource); - if(pragmaBody.size()>0) { - final Token pragmaBodyStart = pragmaBody.get(0); - // Convert space-based pragma to parenthesis-based for easier parsing - // #pragma NAME XXX YYY \n => #pragma NAME ( XXX , YYY ) \n - if (pragmaBodyStart.getType() != KickCLexer.PAR_BEGIN) { - ArrayList parenthesizedBody = new ArrayList<>(); - parenthesizedBody.add(new CommonToken(KickCLexer.PAR_BEGIN, "(")); - // Parenthesize the parameter list - boolean first = true; - for (Token token : pragmaBody) { - if (token.getChannel() != CParser.CHANNEL_WHITESPACE && !first) { - parenthesizedBody.add(new CommonToken(KickCLexer.COMMA, ",")); - } - parenthesizedBody.add(token); - first = false; + // Convert space-based pragma to parenthesis-based for easier parsing + // #pragma NAME XXX YYY \n => #pragma NAME ( XXX , YYY ) \n + if(pragmaBody.isEmpty() || pragmaBody.get(0).getType() != KickCLexer.PAR_BEGIN) { + ArrayList parenthesizedBody = new ArrayList<>(); + parenthesizedBody.add(new CommonToken(KickCLexer.PAR_BEGIN, "(")); + // Parenthesize the parameter list + boolean first = true; + for(Token token : pragmaBody) { + if(token.getChannel() != CParser.CHANNEL_WHITESPACE && !first) { + parenthesizedBody.add(new CommonToken(KickCLexer.COMMA, ",")); } - parenthesizedBody.add(new CommonToken(KickCLexer.PAR_END, ")")); - pragmaBody = parenthesizedBody; + parenthesizedBody.add(token); + first = false; } - pragmaTokens.addAll(pragmaBody); - // Pass on the #pragma to the parser - and mark it as already handled - cTokenSource.addSourceFirst(new ListTokenSource(pragmaTokens)); + parenthesizedBody.add(new CommonToken(KickCLexer.PAR_END, ")")); + pragmaBody = parenthesizedBody; } + pragmaTokens.addAll(pragmaBody); + // Pass on the #pragma to the parser - and mark it as already handled + cTokenSource.addSourceFirst(new ListTokenSource(pragmaTokens)); parserPragmas.add(inputToken); return true; } diff --git a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java index 4420f7bfc..95ae83112 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java +++ b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java @@ -374,17 +374,33 @@ public class TestProgramsFast extends TestPrograms { public void testStructUnwinding2() throws IOException { compileAndCompare("struct-unwinding-2.c"); } - @Test public void testStructUnwinding1() throws IOException { compileAndCompare("struct-unwinding-1.c"); } - //@Test - //public void testVarCall5() throws IOException { - // compileAndCompare("varcall-5.c", log().verboseCreateSsa().verboseStructUnwind()); - //} + @Test + public void testVarCall9() throws IOException { + compileAndCompare("varcall-9.c"); + } + @Test + public void testVarCall8() throws IOException { + compileAndCompare("varcall-8.c"); + } + + @Test + public void testVarCall7() throws IOException { + compileAndCompare("varcall-7.c"); + } + @Test + public void testVarCall6() throws IOException { + compileAndCompare("varcall-6.c"); + } + @Test + public void testVarCall5() throws IOException { + compileAndCompare("varcall-5.c"); + } @Test public void testVarCall4() throws IOException { compileAndCompare("varcall-4.c"); @@ -530,6 +546,11 @@ public class TestProgramsFast extends TestPrograms { compileAndCompare("pragma-unknown.c"); } + @Test + public void testPragmaNoParametersNoParenthesis() throws IOException { + compileAndCompare("pragma-noparam-noparen.c"); + } + @Test public void testErrorFormatter() throws IOException { // Error on a char @@ -2612,6 +2633,31 @@ public class TestProgramsFast extends TestPrograms { compileAndCompare("weeip-bbslist.c"); } + @Test + public void testUnion13() throws IOException { + compileAndCompare("union-13.c"); + } + + @Test + public void testUnion12() throws IOException { + compileAndCompare("union-12.c"); + } + + @Test + public void testUnion11() throws IOException { + compileAndCompare("union-11.c"); + } + + @Test + public void testUnion10() throws IOException { + compileAndCompare("union-10.c"); + } + + @Test + public void testUnion9() throws IOException { + compileAndCompare("union-9.c"); + } + @Test public void testUnion8() throws IOException { compileAndCompare("union-8.c"); diff --git a/src/test/kc/pragma-noparam-noparen.c b/src/test/kc/pragma-noparam-noparen.c new file mode 100644 index 000000000..2d9fe466b --- /dev/null +++ b/src/test/kc/pragma-noparam-noparen.c @@ -0,0 +1,8 @@ +// Test that #pragma works with no parenthesis and no parameters + +#pragma nobank + +void main() { + char * const SCREEN = (char*) 0x0400; + *SCREEN = 'a'; +} \ No newline at end of file diff --git a/src/test/kc/union-10.c b/src/test/kc/union-10.c new file mode 100644 index 000000000..e05a9d461 --- /dev/null +++ b/src/test/kc/union-10.c @@ -0,0 +1,27 @@ +// More extensive union with C99 style designator initialization behaviour of the second element. + +struct Move { + char f; + char t; + char s; +}; + +struct Turn { + char t; + char s; + char r; + char d; +}; + +union Data { + struct Move m; + struct Turn t; +}; + +union Data data = { .t={1,2,3,4} }; + +char* const SCREEN = (char*)0x0400; + +void main() { + SCREEN[0] = data.m.f; +} \ No newline at end of file diff --git a/src/test/kc/union-11.c b/src/test/kc/union-11.c new file mode 100644 index 000000000..840f4c9a0 --- /dev/null +++ b/src/test/kc/union-11.c @@ -0,0 +1,27 @@ +// More extensive union with C99 style designator initialization behaviour of the first element. + +struct Move { + char f; + char t; + char s; +}; + +struct Turn { + char t; + char s; + char r; + char d; +}; + +union Data { + struct Move m; + struct Turn t; +}; + +union Data data = { .m={1,2,3} }; + +char* const SCREEN = (char*)0x0400; + +void main() { + SCREEN[0] = data.m.f; +} \ No newline at end of file diff --git a/src/test/kc/union-12.c b/src/test/kc/union-12.c new file mode 100644 index 000000000..a6e084637 --- /dev/null +++ b/src/test/kc/union-12.c @@ -0,0 +1,29 @@ +// More extensive union with C99 style designator initialization behaviour using const expressions. + +struct Move { + char f; + char t; + char s; +}; + +struct Turn { + char t; + char s; + char r; + char d; +}; + +union Data { + struct Move m; + struct Turn t; +}; + +const struct Move move = {1,2,3}; + +union Data data = { .m=move }; + +char* const SCREEN = (char*)0x0400; + +void main() { + SCREEN[0] = data.m.f; +} \ No newline at end of file diff --git a/src/test/kc/union-13.c b/src/test/kc/union-13.c new file mode 100644 index 000000000..fbfb6d8f4 --- /dev/null +++ b/src/test/kc/union-13.c @@ -0,0 +1,20 @@ +// More extensive union with C99 style designator initialization behaviour using const expressions. + +union A { + unsigned char b; + unsigned int w; +}; + +union B { + union A a; + char b[4]; +}; + +union B b1 = { .a={ .b=1 } }; + + +char* const SCREEN = (char*)0x0400; + +void main() { + SCREEN[0] = b1.b[0]; +} \ No newline at end of file diff --git a/src/test/kc/union-9.c b/src/test/kc/union-9.c new file mode 100644 index 000000000..bf7a6dd30 --- /dev/null +++ b/src/test/kc/union-9.c @@ -0,0 +1,14 @@ +// Minimal union with C99 style designator initialization behaviour. + +union Data { + char b; + unsigned w; +}; + +union Data data = { .w=1234 }; + +char* const SCREEN = (char*)0x0400; + +void main() { + SCREEN[0] = data.b; +} \ No newline at end of file diff --git a/src/test/kc/varcall-5.c b/src/test/kc/varcall-5.c index 0e0b1cd1a..e6b7f18d1 100644 --- a/src/test/kc/varcall-5.c +++ b/src/test/kc/varcall-5.c @@ -6,10 +6,17 @@ struct Cols { char bg; }; -struct Cols * const COLS = (struct Cols *)0xd020; +struct Cols * const COLS = (struct Cols * const)0xd020; struct Cols a; +__varcall struct Cols make(char v) { + struct Cols c; + c.border = v; + c.bg = v+v; + return c; +} + void main() { a = make(1); *COLS = a; @@ -17,6 +24,3 @@ void main() { *COLS = a; } -__varcall struct Cols make(char v) { - return { v, v+v }; -} diff --git a/src/test/kc/varcall-6.c b/src/test/kc/varcall-6.c index f5e00cc96..41c23d687 100644 --- a/src/test/kc/varcall-6.c +++ b/src/test/kc/varcall-6.c @@ -8,15 +8,15 @@ struct Cols { struct Cols * const COLS = (struct Cols *)0xd020; -void main() { - struct Cols a = { 1, 2 }; - //*COLS = a; - a = plus(a, { 2, 3 } ); - *COLS = a; - //a = plus(a, a); - //*COLS = a; -} - __varcall struct Cols plus(struct Cols a, struct Cols b) { return { a.border+b.border, a.bg+b.bg }; -} \ No newline at end of file +} + + +void main() { + struct Cols a = { 1, 2 }; + struct Cols c = plus(a, { 2, 3 }); + *COLS = c; + c = plus(c, a); + *COLS = c; +} diff --git a/src/test/kc/varcall-7.c b/src/test/kc/varcall-7.c new file mode 100644 index 000000000..83d418bee --- /dev/null +++ b/src/test/kc/varcall-7.c @@ -0,0 +1,21 @@ +// Test __varcall calling convention +// Struct parameter & return value - only a single call + +struct Cols { + char border; + char bg; +}; + +struct Cols * const COLS = (struct Cols *)0xd020; + +__varcall struct Cols plus(struct Cols a, struct Cols b) { + return { a.border+b.border, a.bg+b.bg }; +} + + +void main() { + struct Cols a = { 1, 2 }; + struct Cols b = { 2, 3 }; + struct Cols c = plus(a, b); + *COLS = c; +} diff --git a/src/test/kc/varcall-8.c b/src/test/kc/varcall-8.c new file mode 100644 index 000000000..be744f1b3 --- /dev/null +++ b/src/test/kc/varcall-8.c @@ -0,0 +1,28 @@ +// Test __varcall calling convention +// Pointer to Struct parameter & return value + +struct Cols { + char border; + char bg; +}; + +struct Cols * const COLS = (struct Cols *)0xd020; + +__varcall struct Cols * min(struct Cols * a, struct Cols * b) { + if(a->bg < b->bg) + return a; + else + return b; + +} + + +void main() { + struct Cols a = { 1, 7 }; + struct Cols b = { 2, 6 }; + struct Cols c = { 3, 5 }; + struct Cols *m = min(&a,&b); + *COLS = *m; + m = min(m,&c); + *COLS = *m; +} diff --git a/src/test/kc/varcall-9.c b/src/test/kc/varcall-9.c new file mode 100644 index 000000000..b72b75bba --- /dev/null +++ b/src/test/kc/varcall-9.c @@ -0,0 +1,26 @@ +// Test __varcall calling convention +// Struct of struct parameter value + +struct Col { + char border; + char bg; +}; + +struct Cols { + struct Col normal; + struct Col error; +}; + +char * const COLS = (char*)0xd020; + +__varcall char plus(struct Cols a, struct Cols b) { + return a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + a.error.border + b.error.border + a.error.bg + b.error.bg; +} + +void main() { + struct Cols a = { { 1, 2 }, { 3, 4 }}; + struct Cols b = { { 5, 6 }, { 7, 8 }}; + struct Cols c = { { 9, 10 }, { 11, 12 }}; + *COLS = plus(a, b); + *COLS = plus(b, c); +} diff --git a/src/test/ref/pragma-noparam-noparen.asm b/src/test/ref/pragma-noparam-noparen.asm new file mode 100644 index 000000000..7039561db --- /dev/null +++ b/src/test/ref/pragma-noparam-noparen.asm @@ -0,0 +1,18 @@ +// Test that #pragma works with no parenthesis and no parameters + // Commodore 64 PRG executable file +.file [name="pragma-noparam-noparen.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) +.segment Code +main: { + .label SCREEN = $400 + // *SCREEN = 'a' + lda #'a' + sta SCREEN + // } + rts +} diff --git a/src/test/ref/pragma-noparam-noparen.cfg b/src/test/ref/pragma-noparam-noparen.cfg new file mode 100644 index 000000000..134ce4314 --- /dev/null +++ b/src/test/ref/pragma-noparam-noparen.cfg @@ -0,0 +1,8 @@ + +void main() +main: scope:[main] from + [0] *main::SCREEN = 'a' + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return diff --git a/src/test/ref/pragma-noparam-noparen.log b/src/test/ref/pragma-noparam-noparen.log new file mode 100644 index 000000000..68ccb3b9d --- /dev/null +++ b/src/test/ref/pragma-noparam-noparen.log @@ -0,0 +1,135 @@ +Warning! Unknown #pragma nobank + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + *main::SCREEN = 'a' + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +void __start() +void main() +__constant char * const main::SCREEN = (char *)$400 + +Simplifying constant pointer cast (char *) 1024 +Successful SSA optimization PassNCastSimplification +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *main::SCREEN = 'a' + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() + +Initial phi equivalence classes +Complete equivalence classes +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *main::SCREEN = 'a' [ ] ( [ ] { } ) always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [main] +Uplift Scope [] + +Uplifting [main] best 15 combination +Uplifting [] best 15 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test that #pragma works with no parenthesis and no parameters + // Upstart + // Commodore 64 PRG executable file +.file [name="pragma-noparam-noparen.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels +.segment Code + // main +main: { + .label SCREEN = $400 + // [0] *main::SCREEN = 'a' -- _deref_pbuc1=vbuc2 + lda #'a' + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [1] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +void main() +__constant char * const main::SCREEN = (char *) 1024 + + + +FINAL ASSEMBLER +Score: 12 + + // File Comments +// Test that #pragma works with no parenthesis and no parameters + // Upstart + // Commodore 64 PRG executable file +.file [name="pragma-noparam-noparen.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels +.segment Code + // main +main: { + .label SCREEN = $400 + // *SCREEN = 'a' + // [0] *main::SCREEN = 'a' -- _deref_pbuc1=vbuc2 + lda #'a' + sta SCREEN + // main::@return + // } + // [1] return + rts +} + // File Data + diff --git a/src/test/ref/pragma-noparam-noparen.sym b/src/test/ref/pragma-noparam-noparen.sym new file mode 100644 index 000000000..a2a8aef7f --- /dev/null +++ b/src/test/ref/pragma-noparam-noparen.sym @@ -0,0 +1,3 @@ +void main() +__constant char * const main::SCREEN = (char *) 1024 + diff --git a/src/test/ref/union-10.asm b/src/test/ref/union-10.asm new file mode 100644 index 000000000..312006261 --- /dev/null +++ b/src/test/ref/union-10.asm @@ -0,0 +1,20 @@ +// More extensive union with C99 style designator initialization behaviour of the second element. + // Commodore 64 PRG executable file +.file [name="union-10.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .label SCREEN = $400 +.segment Code +main: { + // SCREEN[0] = data.m.f + lda data + sta SCREEN + // } + rts +} +.segment Data + data: .byte 1, 2, 3, 4 diff --git a/src/test/ref/union-10.cfg b/src/test/ref/union-10.cfg new file mode 100644 index 000000000..16c9227d5 --- /dev/null +++ b/src/test/ref/union-10.cfg @@ -0,0 +1,8 @@ + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return diff --git a/src/test/ref/union-10.log b/src/test/ref/union-10.log new file mode 100644 index 000000000..f48c5715f --- /dev/null +++ b/src/test/ref/union-10.log @@ -0,0 +1,166 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char OFFSET_STRUCT_MOVE_F = 0 +__constant char OFFSET_UNION_DATA_M = 0 +__constant char * const SCREEN = (char *)$400 +void __start() +__loadstore union Data data = { t: { t: 1, s: 2, r: 3, d: 4 } } +void main() + +Adding number conversion cast (unumber) 0 in SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero (char *)(struct Move *)&data+OFFSET_UNION_DATA_M in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Simplifying expression containing zero (struct Move *)&data in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M) +Simplifying expression containing zero SCREEN in [0] SCREEN[0] = *((char *)(struct Move *)&data) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant OFFSET_UNION_DATA_M +Eliminating unused constant OFFSET_STRUCT_MOVE_F +Successful SSA optimization PassNEliminateUnusedVars +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__loadstore union Data data = { t: { t: 1, s: 2, r: 3, d: 4 } } +void main() + +Initial phi equivalence classes +Added variable data to live range equivalence class [ data ] +Complete equivalence classes +[ data ] +Allocated mem[4] [ data ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *SCREEN = *((char *)(struct Move *)&data) [ ] ( [ ] { } ) always clobbers reg byte a +Potential registers mem[4] [ data ] : mem[4] , + +REGISTER UPLIFT SCOPES +Uplift Scope [Move] +Uplift Scope [Turn] +Uplift Scope [Data] +Uplift Scope [main] +Uplift Scope [] 0: mem[4] [ data ] + +Uplifting [Move] best 17 combination +Uplifting [Turn] best 17 combination +Uplifting [Data] best 17 combination +Uplifting [main] best 17 combination +Uplifting [] best 17 combination mem[4] [ data ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// More extensive union with C99 style designator initialization behaviour of the second element. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-10.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [1] return + rts +} + // File Data +.segment Data + data: .byte 1, 2, 3, 4 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { t: { t: 1, s: 2, r: 3, d: 4 } } // mem[4] +void main() + +mem[4] [ data ] + + +FINAL ASSEMBLER +Score: 14 + + // File Comments +// More extensive union with C99 style designator initialization behaviour of the second element. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-10.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // SCREEN[0] = data.m.f + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + // main::@return + // } + // [1] return + rts +} + // File Data +.segment Data + data: .byte 1, 2, 3, 4 + diff --git a/src/test/ref/union-10.sym b/src/test/ref/union-10.sym new file mode 100644 index 000000000..74e440b35 --- /dev/null +++ b/src/test/ref/union-10.sym @@ -0,0 +1,5 @@ +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { t: { t: 1, s: 2, r: 3, d: 4 } } // mem[4] +void main() + +mem[4] [ data ] diff --git a/src/test/ref/union-11.asm b/src/test/ref/union-11.asm new file mode 100644 index 000000000..8cc1f3211 --- /dev/null +++ b/src/test/ref/union-11.asm @@ -0,0 +1,21 @@ +// More extensive union with C99 style designator initialization behaviour of the first element. + // Commodore 64 PRG executable file +.file [name="union-11.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .label SCREEN = $400 +.segment Code +main: { + // SCREEN[0] = data.m.f + lda data + sta SCREEN + // } + rts +} +.segment Data + data: .byte 1, 2, 3 + .fill 1, 0 diff --git a/src/test/ref/union-11.cfg b/src/test/ref/union-11.cfg new file mode 100644 index 000000000..16c9227d5 --- /dev/null +++ b/src/test/ref/union-11.cfg @@ -0,0 +1,8 @@ + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return diff --git a/src/test/ref/union-11.log b/src/test/ref/union-11.log new file mode 100644 index 000000000..a03a0dd9b --- /dev/null +++ b/src/test/ref/union-11.log @@ -0,0 +1,168 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char OFFSET_STRUCT_MOVE_F = 0 +__constant char OFFSET_UNION_DATA_M = 0 +__constant char * const SCREEN = (char *)$400 +void __start() +__loadstore union Data data = { m: { f: 1, t: 2, s: 3 } } +void main() + +Adding number conversion cast (unumber) 0 in SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero (char *)(struct Move *)&data+OFFSET_UNION_DATA_M in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Simplifying expression containing zero (struct Move *)&data in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M) +Simplifying expression containing zero SCREEN in [0] SCREEN[0] = *((char *)(struct Move *)&data) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant OFFSET_UNION_DATA_M +Eliminating unused constant OFFSET_STRUCT_MOVE_F +Successful SSA optimization PassNEliminateUnusedVars +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__loadstore union Data data = { m: { f: 1, t: 2, s: 3 } } +void main() + +Initial phi equivalence classes +Added variable data to live range equivalence class [ data ] +Complete equivalence classes +[ data ] +Allocated mem[4] [ data ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *SCREEN = *((char *)(struct Move *)&data) [ ] ( [ ] { } ) always clobbers reg byte a +Potential registers mem[4] [ data ] : mem[4] , + +REGISTER UPLIFT SCOPES +Uplift Scope [Move] +Uplift Scope [Turn] +Uplift Scope [Data] +Uplift Scope [main] +Uplift Scope [] 0: mem[4] [ data ] + +Uplifting [Move] best 17 combination +Uplifting [Turn] best 17 combination +Uplifting [Data] best 17 combination +Uplifting [main] best 17 combination +Uplifting [] best 17 combination mem[4] [ data ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// More extensive union with C99 style designator initialization behaviour of the first element. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-11.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [1] return + rts +} + // File Data +.segment Data + data: .byte 1, 2, 3 + .fill 1, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { m: { f: 1, t: 2, s: 3 } } // mem[4] +void main() + +mem[4] [ data ] + + +FINAL ASSEMBLER +Score: 14 + + // File Comments +// More extensive union with C99 style designator initialization behaviour of the first element. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-11.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // SCREEN[0] = data.m.f + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + // main::@return + // } + // [1] return + rts +} + // File Data +.segment Data + data: .byte 1, 2, 3 + .fill 1, 0 + diff --git a/src/test/ref/union-11.sym b/src/test/ref/union-11.sym new file mode 100644 index 000000000..65428d3de --- /dev/null +++ b/src/test/ref/union-11.sym @@ -0,0 +1,5 @@ +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { m: { f: 1, t: 2, s: 3 } } // mem[4] +void main() + +mem[4] [ data ] diff --git a/src/test/ref/union-12.asm b/src/test/ref/union-12.asm new file mode 100644 index 000000000..cd836e561 --- /dev/null +++ b/src/test/ref/union-12.asm @@ -0,0 +1,21 @@ +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Commodore 64 PRG executable file +.file [name="union-12.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .label SCREEN = $400 +.segment Code +main: { + // SCREEN[0] = data.m.f + lda data + sta SCREEN + // } + rts +} +.segment Data + move: .byte 1, 2, 3 + data: .fill 1, 0 diff --git a/src/test/ref/union-12.cfg b/src/test/ref/union-12.cfg new file mode 100644 index 000000000..16c9227d5 --- /dev/null +++ b/src/test/ref/union-12.cfg @@ -0,0 +1,8 @@ + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return diff --git a/src/test/ref/union-12.log b/src/test/ref/union-12.log new file mode 100644 index 000000000..6d08ee7e8 --- /dev/null +++ b/src/test/ref/union-12.log @@ -0,0 +1,170 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char OFFSET_STRUCT_MOVE_F = 0 +__constant char OFFSET_UNION_DATA_M = 0 +__constant char * const SCREEN = (char *)$400 +void __start() +__loadstore union Data data = { m: move } +void main() +__constant const struct Move move = { f: 1, t: 2, s: 3 } + +Adding number conversion cast (unumber) 0 in SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero (char *)(struct Move *)&data+OFFSET_UNION_DATA_M in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M+OFFSET_STRUCT_MOVE_F) +Simplifying expression containing zero (struct Move *)&data in [0] SCREEN[0] = *((char *)(struct Move *)&data+OFFSET_UNION_DATA_M) +Simplifying expression containing zero SCREEN in [0] SCREEN[0] = *((char *)(struct Move *)&data) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant OFFSET_UNION_DATA_M +Eliminating unused constant OFFSET_STRUCT_MOVE_F +Successful SSA optimization PassNEliminateUnusedVars +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)(struct Move *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__loadstore union Data data = { m: move } +void main() + +Initial phi equivalence classes +Added variable data to live range equivalence class [ data ] +Complete equivalence classes +[ data ] +Allocated mem[4] [ data ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *SCREEN = *((char *)(struct Move *)&data) [ ] ( [ ] { } ) always clobbers reg byte a +Potential registers mem[4] [ data ] : mem[4] , + +REGISTER UPLIFT SCOPES +Uplift Scope [Move] +Uplift Scope [Turn] +Uplift Scope [Data] +Uplift Scope [main] +Uplift Scope [] 0: mem[4] [ data ] + +Uplifting [Move] best 17 combination +Uplifting [Turn] best 17 combination +Uplifting [Data] best 17 combination +Uplifting [main] best 17 combination +Uplifting [] best 17 combination mem[4] [ data ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-12.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [1] return + rts +} + // File Data +.segment Data + move: .byte 1, 2, 3 + data: .fill 1, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { m: move } // mem[4] +void main() +__constant const struct Move move = { f: 1, t: 2, s: 3 } + +mem[4] [ data ] + + +FINAL ASSEMBLER +Score: 14 + + // File Comments +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-12.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // SCREEN[0] = data.m.f + // [0] *SCREEN = *((char *)(struct Move *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + // main::@return + // } + // [1] return + rts +} + // File Data +.segment Data + move: .byte 1, 2, 3 + data: .fill 1, 0 + diff --git a/src/test/ref/union-12.sym b/src/test/ref/union-12.sym new file mode 100644 index 000000000..262c82268 --- /dev/null +++ b/src/test/ref/union-12.sym @@ -0,0 +1,6 @@ +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { m: move } // mem[4] +void main() +__constant const struct Move move = { f: 1, t: 2, s: 3 } + +mem[4] [ data ] diff --git a/src/test/ref/union-13.asm b/src/test/ref/union-13.asm new file mode 100644 index 000000000..523f3d619 --- /dev/null +++ b/src/test/ref/union-13.asm @@ -0,0 +1,22 @@ +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Commodore 64 PRG executable file +.file [name="union-13.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .label SCREEN = $400 +.segment Code +main: { + // SCREEN[0] = b1.b[0] + lda b1 + sta SCREEN + // } + rts +} +.segment Data + b1: .byte 1 + .fill 1, 0 + .fill 2, 0 diff --git a/src/test/ref/union-13.cfg b/src/test/ref/union-13.cfg new file mode 100644 index 000000000..067c8eda8 --- /dev/null +++ b/src/test/ref/union-13.cfg @@ -0,0 +1,8 @@ + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)&b1) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return diff --git a/src/test/ref/union-13.log b/src/test/ref/union-13.log new file mode 100644 index 000000000..70ec969ae --- /dev/null +++ b/src/test/ref/union-13.log @@ -0,0 +1,176 @@ +Fixing struct type size union B to 4 +Fixing struct type size union B to 4 +Fixing struct type SIZE_OF union B to 4 +Fixing struct type SIZE_OF union B to 4 + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + SCREEN[0] = ((char *)&b1+OFFSET_UNION_B_B)[0] + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char OFFSET_UNION_B_B = 0 +__constant char * const SCREEN = (char *)$400 +void __start() +__loadstore union B b1 = { a: { b: 1 } } +void main() + +Adding number conversion cast (unumber) 0 in SCREEN[0] = ((char *)&b1+OFFSET_UNION_B_B)[0] +Adding number conversion cast (unumber) 0 in SCREEN[0] = ((char *)&b1+OFFSET_UNION_B_B)[(unumber)0] +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 0 +Finalized unsigned number type (char) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero (char *)&b1+OFFSET_UNION_B_B in [0] SCREEN[0] = ((char *)&b1+OFFSET_UNION_B_B)[0] +Simplifying expression containing zero (char *)&b1 in [0] SCREEN[0] = *((char *)&b1+OFFSET_UNION_B_B) +Simplifying expression containing zero SCREEN in [0] SCREEN[0] = *((char *)&b1) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant OFFSET_UNION_B_B +Successful SSA optimization PassNEliminateUnusedVars +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Finalized unsigned number type (char) 4 +Finalized unsigned number type (char) 4 +Successful SSA optimization PassNFinalizeNumberTypeConversions +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)&b1) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__loadstore union B b1 = { a: { b: 1 } } +void main() + +Initial phi equivalence classes +Added variable b1 to live range equivalence class [ b1 ] +Complete equivalence classes +[ b1 ] +Allocated mem[4] [ b1 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *SCREEN = *((char *)&b1) [ ] ( [ ] { } ) always clobbers reg byte a +Potential registers mem[4] [ b1 ] : mem[4] , + +REGISTER UPLIFT SCOPES +Uplift Scope [A] +Uplift Scope [B] +Uplift Scope [main] +Uplift Scope [] 0: mem[4] [ b1 ] + +Uplifting [A] best 17 combination +Uplifting [B] best 17 combination +Uplifting [main] best 17 combination +Uplifting [] best 17 combination mem[4] [ b1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-13.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [0] *SCREEN = *((char *)&b1) -- _deref_pbuc1=_deref_pbuc2 + lda b1 + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [1] return + rts +} + // File Data +.segment Data + b1: .byte 1 + .fill 1, 0 + .fill 2, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__loadstore union B b1 = { a: { b: 1 } } // mem[4] +void main() + +mem[4] [ b1 ] + + +FINAL ASSEMBLER +Score: 14 + + // File Comments +// More extensive union with C99 style designator initialization behaviour using const expressions. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-13.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // SCREEN[0] = b1.b[0] + // [0] *SCREEN = *((char *)&b1) -- _deref_pbuc1=_deref_pbuc2 + lda b1 + sta SCREEN + // main::@return + // } + // [1] return + rts +} + // File Data +.segment Data + b1: .byte 1 + .fill 1, 0 + .fill 2, 0 + diff --git a/src/test/ref/union-13.sym b/src/test/ref/union-13.sym new file mode 100644 index 000000000..12c6996e2 --- /dev/null +++ b/src/test/ref/union-13.sym @@ -0,0 +1,5 @@ +__constant char * const SCREEN = (char *) 1024 +__loadstore union B b1 = { a: { b: 1 } } // mem[4] +void main() + +mem[4] [ b1 ] diff --git a/src/test/ref/union-9.asm b/src/test/ref/union-9.asm new file mode 100644 index 000000000..7963534cd --- /dev/null +++ b/src/test/ref/union-9.asm @@ -0,0 +1,20 @@ +// Minimal union with C99 style designator initialization behaviour. + // Commodore 64 PRG executable file +.file [name="union-9.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .label SCREEN = $400 +.segment Code +main: { + // SCREEN[0] = data.b + lda data + sta SCREEN + // } + rts +} +.segment Data + data: .word $4d2 diff --git a/src/test/ref/union-9.cfg b/src/test/ref/union-9.cfg new file mode 100644 index 000000000..dcb6487cf --- /dev/null +++ b/src/test/ref/union-9.cfg @@ -0,0 +1,8 @@ + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return diff --git a/src/test/ref/union-9.log b/src/test/ref/union-9.log new file mode 100644 index 000000000..7f4e6a7d1 --- /dev/null +++ b/src/test/ref/union-9.log @@ -0,0 +1,159 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + SCREEN[0] = *((char *)&data+OFFSET_UNION_DATA_B) + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char OFFSET_UNION_DATA_B = 0 +__constant char * const SCREEN = (char *)$400 +void __start() +__loadstore union Data data = { w: $4d2 } +void main() + +Adding number conversion cast (unumber) 0 in SCREEN[0] = *((char *)&data+OFFSET_UNION_DATA_B) +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (char *) 1024 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simplifying expression containing zero (char *)&data in [0] SCREEN[0] = *((char *)&data+OFFSET_UNION_DATA_B) +Simplifying expression containing zero SCREEN in [0] SCREEN[0] = *((char *)&data) +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused constant OFFSET_UNION_DATA_B +Successful SSA optimization PassNEliminateUnusedVars +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +CALL GRAPH + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *SCREEN = *((char *)&data) + to:main::@return +main::@return: scope:[main] from main + [1] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__loadstore union Data data = { w: $4d2 } +void main() + +Initial phi equivalence classes +Added variable data to live range equivalence class [ data ] +Complete equivalence classes +[ data ] +Allocated mem[2] [ data ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *SCREEN = *((char *)&data) [ ] ( [ ] { } ) always clobbers reg byte a +Potential registers mem[2] [ data ] : mem[2] , + +REGISTER UPLIFT SCOPES +Uplift Scope [Data] +Uplift Scope [main] +Uplift Scope [] 0: mem[2] [ data ] + +Uplifting [Data] best 17 combination +Uplifting [main] best 17 combination +Uplifting [] best 17 combination mem[2] [ data ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Minimal union with C99 style designator initialization behaviour. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-9.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // [0] *SCREEN = *((char *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + jmp __breturn + // main::@return + __breturn: + // [1] return + rts +} + // File Data +.segment Data + data: .word $4d2 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { w: $4d2 } // mem[2] +void main() + +mem[2] [ data ] + + +FINAL ASSEMBLER +Score: 14 + + // File Comments +// Minimal union with C99 style designator initialization behaviour. + // Upstart + // Commodore 64 PRG executable file +.file [name="union-9.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label SCREEN = $400 +.segment Code + // main +main: { + // SCREEN[0] = data.b + // [0] *SCREEN = *((char *)&data) -- _deref_pbuc1=_deref_pbuc2 + lda data + sta SCREEN + // main::@return + // } + // [1] return + rts +} + // File Data +.segment Data + data: .word $4d2 + diff --git a/src/test/ref/union-9.sym b/src/test/ref/union-9.sym new file mode 100644 index 000000000..3f15e7831 --- /dev/null +++ b/src/test/ref/union-9.sym @@ -0,0 +1,5 @@ +__constant char * const SCREEN = (char *) 1024 +__loadstore union Data data = { w: $4d2 } // mem[2] +void main() + +mem[2] [ data ] diff --git a/src/test/ref/varcall-5.asm b/src/test/ref/varcall-5.asm new file mode 100644 index 000000000..22b209990 --- /dev/null +++ b/src/test/ref/varcall-5.asm @@ -0,0 +1,69 @@ +// Test __varcall calling convention +// Struct return value + // Commodore 64 PRG executable file +.file [name="varcall-5.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .const OFFSET_STRUCT_COLS_BG = 1 + .const SIZEOF_STRUCT_COLS = 2 + .label COLS = $d020 +.segment Code +main: { + // make(1) + lda #1 + sta.z make.v + jsr make + ldx.z make.return_border + lda.z make.return_bg + // a = make(1) + stx a + sta a+OFFSET_STRUCT_COLS_BG + // *COLS = a + ldy #SIZEOF_STRUCT_COLS + !: + lda a-1,y + sta COLS-1,y + dey + bne !- + // make(2) + lda #2 + sta.z make.v + jsr make + ldx.z make.return_border + lda.z make.return_bg + // a = make(2) + stx a + sta a+OFFSET_STRUCT_COLS_BG + // *COLS = a + ldy #SIZEOF_STRUCT_COLS + !: + lda a-1,y + sta COLS-1,y + dey + bne !- + // } + rts +} +// struct Cols make(__zp(2) char v) +make: { + .label v = 2 + .label return_border = 3 + .label return_bg = 4 + // c.border = v + ldx.z v + // v+v + txa + asl + // c.bg = v+v + // return c; + stx.z return_border + sta.z return_bg + // } + rts +} +.segment Data + a: .fill SIZEOF_STRUCT_COLS, 0 diff --git a/src/test/ref/varcall-5.cfg b/src/test/ref/varcall-5.cfg new file mode 100644 index 000000000..c0e651751 --- /dev/null +++ b/src/test/ref/varcall-5.cfg @@ -0,0 +1,33 @@ + +void main() +main: scope:[main] from + [0] make::v = 1 + [1] callexecute make + [2] main::$0_border = make::return_border + [3] main::$0_bg = make::return_bg + [4] *((char *)&a) = main::$0_border + [5] *((char *)&a+OFFSET_STRUCT_COLS_BG) = main::$0_bg + [6] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) + [7] make::v = 2 + [8] callexecute make + [9] main::$1_border = make::return_border + [10] main::$1_bg = make::return_bg + [11] *((char *)&a) = main::$1_border + [12] *((char *)&a+OFFSET_STRUCT_COLS_BG) = main::$1_bg + [13] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) + to:main::@return +main::@return: scope:[main] from main + [14] return + to:@return + +__varcall struct Cols make(char v) +make: scope:[make] from + [15] make::c_border#1 = make::v + [16] make::$0 = make::v + make::v + [17] make::c_bg#1 = make::$0 + [18] make::return_border = make::c_border#1 + [19] make::return_bg = make::c_bg#1 + to:make::@return +make::@return: scope:[make] from make + [20] return + to:@return diff --git a/src/test/ref/varcall-5.log b/src/test/ref/varcall-5.log new file mode 100644 index 000000000..0d1cb0dde --- /dev/null +++ b/src/test/ref/varcall-5.log @@ -0,0 +1,496 @@ +Converting parameter in __varcall procedure to load/store make::v +Converting return in __varcall procedure to load/store make::return +Eliminating unused variable with no statement main::$0 +Eliminating unused variable with no statement main::$1 +Calling convention __varcall adding prepare/execute/finalize for { main::$0_border, main::$0_bg } = call make(1) +Calling convention __varcall adding prepare/execute/finalize for { main::$1_border, main::$1_bg } = call make(2) +Removing C-classic struct-unwound assignment a = struct-unwound {*((char *)&a+OFFSET_STRUCT_COLS_BORDER), *((char *)&a+OFFSET_STRUCT_COLS_BG)} +Removing C-classic struct-unwound assignment a = struct-unwound {*((char *)&a+OFFSET_STRUCT_COLS_BORDER), *((char *)&a+OFFSET_STRUCT_COLS_BG)} + +CONTROL FLOW GRAPH SSA + +__varcall struct Cols make(char v) +make: scope:[make] from + make::c_border#0 = 0 + make::c_bg#0 = 0 + make::c_border#1 = make::v + make::$0 = make::v + make::v + make::c_bg#1 = make::$0 + make::return_border = make::c_border#1 + make::return_bg = make::c_bg#1 + make::return = struct-unwound {make::return_border, make::return_bg} + to:make::@return +make::@return: scope:[make] from make + return + to:@return + +void main() +main: scope:[main] from __start + make::v = 1 + callexecute make + main::$0_border = make::return_border + main::$0_bg = make::return_bg + *((char *)&a+OFFSET_STRUCT_COLS_BORDER) = main::$0_border + *((char *)&a+OFFSET_STRUCT_COLS_BG) = main::$0_bg + *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) + make::v = 2 + callexecute make + main::$1_border = make::return_border + main::$1_bg = make::return_bg + *((char *)&a+OFFSET_STRUCT_COLS_BORDER) = main::$1_border + *((char *)&a+OFFSET_STRUCT_COLS_BG) = main::$1_bg + *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant struct Cols * const COLS = (struct Cols * const )$d020 +__constant char OFFSET_STRUCT_COLS_BG = 1 +__constant char OFFSET_STRUCT_COLS_BORDER = 0 +__constant char SIZEOF_STRUCT_COLS = 2 +void __start() +__loadstore struct Cols a = {} +void main() +char main::$0_bg +char main::$0_border +char main::$1_bg +char main::$1_border +__varcall struct Cols make(char v) +char make::$0 +char make::c_bg +char make::c_bg#0 +char make::c_bg#1 +char make::c_border +char make::c_border#0 +char make::c_border#1 +__loadstore struct Cols make::return +__loadstore char make::return_bg +__loadstore char make::return_border +__loadstore char make::v + +Adding number conversion cast (unumber) 1 in make::v = 1 +Adding number conversion cast (unumber) 2 in make::v = 2 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast make::v = (unumber)1 +Inlining cast make::v = (unumber)2 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (struct Cols *) 53280 +Simplifying constant integer cast 1 +Simplifying constant integer cast 2 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 1 +Finalized unsigned number type (char) 2 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias candidate removed (volatile)make::c_bg#1 = make::$0 make::return_bg +Alias candidate removed (volatile)make::c_border#1 = make::return_border +Constant make::c_border#0 = 0 +Constant make::c_bg#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero (char *)&a in [13] *((char *)&a+OFFSET_STRUCT_COLS_BORDER) = main::$0_border +Simplifying expression containing zero (char *)&a in [20] *((char *)&a+OFFSET_STRUCT_COLS_BORDER) = main::$1_border +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable make::return and assignment [5] make::return = struct-unwound {make::return_border, make::return_bg} +Eliminating unused constant make::c_border#0 +Eliminating unused constant make::c_bg#0 +Eliminating unused constant OFFSET_STRUCT_COLS_BORDER +Successful SSA optimization PassNEliminateUnusedVars +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Alias candidate removed (volatile)make::c_bg#1 = make::$0 make::return_bg +Alias candidate removed (volatile)make::c_border#1 = make::return_border +Alias candidate removed (volatile)make::c_bg#1 = make::$0 make::return_bg +Alias candidate removed (volatile)make::c_border#1 = make::return_border +Alias candidate removed (volatile)make::c_bg#1 = make::$0 make::return_bg +Alias candidate removed (volatile)make::c_border#1 = make::return_border +CALL GRAPH +Calls in [main] to make:1 make:8 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] make::v = 1 + [1] callexecute make + [2] main::$0_border = make::return_border + [3] main::$0_bg = make::return_bg + [4] *((char *)&a) = main::$0_border + [5] *((char *)&a+OFFSET_STRUCT_COLS_BG) = main::$0_bg + [6] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) + [7] make::v = 2 + [8] callexecute make + [9] main::$1_border = make::return_border + [10] main::$1_bg = make::return_bg + [11] *((char *)&a) = main::$1_border + [12] *((char *)&a+OFFSET_STRUCT_COLS_BG) = main::$1_bg + [13] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) + to:main::@return +main::@return: scope:[main] from main + [14] return + to:@return + +__varcall struct Cols make(char v) +make: scope:[make] from + [15] make::c_border#1 = make::v + [16] make::$0 = make::v + make::v + [17] make::c_bg#1 = make::$0 + [18] make::return_border = make::c_border#1 + [19] make::return_bg = make::c_bg#1 + to:make::@return +make::@return: scope:[make] from make + [20] return + to:@return + + +VARIABLE REGISTER WEIGHTS +__loadstore struct Cols a = {} +void main() +char main::$0_bg // 2.0 +char main::$0_border // 2.0 +char main::$1_bg // 2.0 +char main::$1_border // 2.0 +__varcall struct Cols make(char v) +char make::$0 // 22.0 +char make::c_bg +char make::c_bg#1 // 11.0 +char make::c_border +char make::c_border#1 // 7.333333333333333 +__loadstore char make::return_bg // 2.5 +__loadstore char make::return_border // 3.0 +__loadstore char make::v // 12.333333333333332 + +Initial phi equivalence classes +Added variable make::v to live range equivalence class [ make::v ] +Added variable main::$0_border to live range equivalence class [ main::$0_border ] +Added variable main::$0_bg to live range equivalence class [ main::$0_bg ] +Added variable main::$1_border to live range equivalence class [ main::$1_border ] +Added variable main::$1_bg to live range equivalence class [ main::$1_bg ] +Added variable make::c_border#1 to live range equivalence class [ make::c_border#1 ] +Added variable make::$0 to live range equivalence class [ make::$0 ] +Added variable make::c_bg#1 to live range equivalence class [ make::c_bg#1 ] +Added variable make::return_border to live range equivalence class [ make::return_border ] +Added variable make::return_bg to live range equivalence class [ make::return_bg ] +Added variable a to live range equivalence class [ a ] +Complete equivalence classes +[ make::v ] +[ main::$0_border ] +[ main::$0_bg ] +[ main::$1_border ] +[ main::$1_bg ] +[ make::c_border#1 ] +[ make::$0 ] +[ make::c_bg#1 ] +[ make::return_border ] +[ make::return_bg ] +[ a ] +Allocated zp[1]:2 [ make::$0 ] +Allocated zp[1]:3 [ make::v ] +Allocated zp[1]:4 [ make::c_bg#1 ] +Allocated zp[1]:5 [ make::c_border#1 ] +Allocated zp[1]:6 [ make::return_border ] +Allocated zp[1]:7 [ make::return_bg ] +Allocated zp[1]:8 [ main::$0_border ] +Allocated zp[1]:9 [ main::$0_bg ] +Allocated zp[1]:10 [ main::$1_border ] +Allocated zp[1]:11 [ main::$1_bg ] +Allocated mem[2] [ a ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] make::v = 1 [ make::v a ] ( [ make::v a ] { } ) always clobbers reg byte a +Statement [6] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) [ a ] ( [ a ] { } ) always clobbers reg byte a reg byte y +Statement [7] make::v = 2 [ make::v a ] ( [ make::v a ] { } ) always clobbers reg byte a +Statement [13] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) [ ] ( [ ] { } ) always clobbers reg byte a reg byte y +Statement [16] make::$0 = make::v + make::v [ make::c_border#1 make::$0 ] ( make:1 [ a make::c_border#1 make::$0 ] { } make:8 [ a make::c_border#1 make::$0 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:5 [ make::c_border#1 ] +Statement [0] make::v = 1 [ make::v a ] ( [ make::v a ] { } ) always clobbers reg byte a +Statement [6] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) [ a ] ( [ a ] { } ) always clobbers reg byte a reg byte y +Statement [7] make::v = 2 [ make::v a ] ( [ make::v a ] { } ) always clobbers reg byte a +Statement [13] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) [ ] ( [ ] { } ) always clobbers reg byte a reg byte y +Statement [16] make::$0 = make::v + make::v [ make::c_border#1 make::$0 ] ( make:1 [ a make::c_border#1 make::$0 ] { } make:8 [ a make::c_border#1 make::$0 ] { } ) always clobbers reg byte a +Potential registers zp[1]:3 [ make::v ] : zp[1]:3 , +Potential registers zp[1]:8 [ main::$0_border ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:9 [ main::$0_bg ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ main::$1_border ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:11 [ main::$1_bg ] : zp[1]:11 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ make::c_border#1 ] : zp[1]:5 , reg byte x , reg byte y , +Potential registers zp[1]:2 [ make::$0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:4 [ make::c_bg#1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:6 [ make::return_border ] : zp[1]:6 , +Potential registers zp[1]:7 [ make::return_bg ] : zp[1]:7 , +Potential registers mem[2] [ a ] : mem[2] , + +REGISTER UPLIFT SCOPES +Uplift Scope [make] 22: zp[1]:2 [ make::$0 ] 12.33: zp[1]:3 [ make::v ] 11: zp[1]:4 [ make::c_bg#1 ] 7.33: zp[1]:5 [ make::c_border#1 ] 3: zp[1]:6 [ make::return_border ] 2.5: zp[1]:7 [ make::return_bg ] +Uplift Scope [main] 2: zp[1]:8 [ main::$0_border ] 2: zp[1]:9 [ main::$0_bg ] 2: zp[1]:10 [ main::$1_border ] 2: zp[1]:11 [ main::$1_bg ] +Uplift Scope [Cols] +Uplift Scope [] 0: mem[2] [ a ] + +Uplifting [make] best 138 combination reg byte a [ make::$0 ] zp[1]:3 [ make::v ] reg byte a [ make::c_bg#1 ] reg byte x [ make::c_border#1 ] zp[1]:6 [ make::return_border ] zp[1]:7 [ make::return_bg ] +Uplifting [main] best 120 combination reg byte x [ main::$0_border ] reg byte a [ main::$0_bg ] reg byte x [ main::$1_border ] zp[1]:11 [ main::$1_bg ] +Limited combination testing to 100 combinations of 256 possible. +Uplifting [Cols] best 120 combination +Uplifting [] best 120 combination mem[2] [ a ] +Attempting to uplift remaining variables inzp[1]:3 [ make::v ] +Uplifting [make] best 120 combination zp[1]:3 [ make::v ] +Attempting to uplift remaining variables inzp[1]:6 [ make::return_border ] +Uplifting [make] best 120 combination zp[1]:6 [ make::return_border ] +Attempting to uplift remaining variables inzp[1]:7 [ make::return_bg ] +Uplifting [make] best 120 combination zp[1]:7 [ make::return_bg ] +Attempting to uplift remaining variables inzp[1]:11 [ main::$1_bg ] +Uplifting [main] best 114 combination reg byte a [ main::$1_bg ] +Allocated (was zp[1]:3) zp[1]:2 [ make::v ] +Allocated (was zp[1]:6) zp[1]:3 [ make::return_border ] +Allocated (was zp[1]:7) zp[1]:4 [ make::return_bg ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test __varcall calling convention +// Struct return value + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-5.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const OFFSET_STRUCT_COLS_BG = 1 + .const SIZEOF_STRUCT_COLS = 2 + .label COLS = $d020 +.segment Code + // main +main: { + // [0] make::v = 1 -- vbuz1=vbuc1 + lda #1 + sta.z make.v + // [1] callexecute make -- call_vprc1 + jsr make + // [2] main::$0_border = make::return_border -- vbuxx=vbuz1 + ldx.z make.return_border + // [3] main::$0_bg = make::return_bg -- vbuaa=vbuz1 + lda.z make.return_bg + // [4] *((char *)&a) = main::$0_border -- _deref_pbuc1=vbuxx + stx a + // [5] *((char *)&a+OFFSET_STRUCT_COLS_BG) = main::$0_bg -- _deref_pbuc1=vbuaa + sta a+OFFSET_STRUCT_COLS_BG + // [6] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda a-1,y + sta COLS-1,y + dey + bne !- + // [7] make::v = 2 -- vbuz1=vbuc1 + lda #2 + sta.z make.v + // [8] callexecute make -- call_vprc1 + jsr make + // [9] main::$1_border = make::return_border -- vbuxx=vbuz1 + ldx.z make.return_border + // [10] main::$1_bg = make::return_bg -- vbuaa=vbuz1 + lda.z make.return_bg + // [11] *((char *)&a) = main::$1_border -- _deref_pbuc1=vbuxx + stx a + // [12] *((char *)&a+OFFSET_STRUCT_COLS_BG) = main::$1_bg -- _deref_pbuc1=vbuaa + sta a+OFFSET_STRUCT_COLS_BG + // [13] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda a-1,y + sta COLS-1,y + dey + bne !- + jmp __breturn + // main::@return + __breturn: + // [14] return + rts +} + // make +// struct Cols make(__zp(2) char v) +make: { + .label v = 2 + .label return_border = 3 + .label return_bg = 4 + // [15] make::c_border#1 = make::v -- vbuxx=vbuz1 + ldx.z v + // [16] make::$0 = make::v + make::v -- vbuaa=vbuz1_plus_vbuz1 + lda.z v + asl + // [17] make::c_bg#1 = make::$0 + // [18] make::return_border = make::c_border#1 -- vbuz1=vbuxx + stx.z return_border + // [19] make::return_bg = make::c_bg#1 -- vbuz1=vbuaa + sta.z return_bg + jmp __breturn + // make::@return + __breturn: + // [20] return + rts +} + // File Data +.segment Data + a: .fill SIZEOF_STRUCT_COLS, 0 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing instruction lda.z v with TXA +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant struct Cols * const COLS = (struct Cols *) 53280 +__constant char OFFSET_STRUCT_COLS_BG = 1 +__constant char SIZEOF_STRUCT_COLS = 2 +__loadstore struct Cols a = {} // mem[2] +void main() +char main::$0_bg // reg byte a 2.0 +char main::$0_border // reg byte x 2.0 +char main::$1_bg // reg byte a 2.0 +char main::$1_border // reg byte x 2.0 +__varcall struct Cols make(char v) +char make::$0 // reg byte a 22.0 +char make::c_bg +char make::c_bg#1 // reg byte a 11.0 +char make::c_border +char make::c_border#1 // reg byte x 7.333333333333333 +__loadstore char make::return_bg // zp[1]:4 2.5 +__loadstore char make::return_border // zp[1]:3 3.0 +__loadstore char make::v // zp[1]:2 12.333333333333332 + +zp[1]:2 [ make::v ] +reg byte x [ main::$0_border ] +reg byte a [ main::$0_bg ] +reg byte x [ main::$1_border ] +reg byte a [ main::$1_bg ] +reg byte x [ make::c_border#1 ] +reg byte a [ make::$0 ] +reg byte a [ make::c_bg#1 ] +zp[1]:3 [ make::return_border ] +zp[1]:4 [ make::return_bg ] +mem[2] [ a ] + + +FINAL ASSEMBLER +Score: 107 + + // File Comments +// Test __varcall calling convention +// Struct return value + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-5.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const OFFSET_STRUCT_COLS_BG = 1 + .const SIZEOF_STRUCT_COLS = 2 + .label COLS = $d020 +.segment Code + // main +main: { + // make(1) + // [0] make::v = 1 -- vbuz1=vbuc1 + lda #1 + sta.z make.v + // [1] callexecute make -- call_vprc1 + jsr make + // [2] main::$0_border = make::return_border -- vbuxx=vbuz1 + ldx.z make.return_border + // [3] main::$0_bg = make::return_bg -- vbuaa=vbuz1 + lda.z make.return_bg + // a = make(1) + // [4] *((char *)&a) = main::$0_border -- _deref_pbuc1=vbuxx + stx a + // [5] *((char *)&a+OFFSET_STRUCT_COLS_BG) = main::$0_bg -- _deref_pbuc1=vbuaa + sta a+OFFSET_STRUCT_COLS_BG + // *COLS = a + // [6] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda a-1,y + sta COLS-1,y + dey + bne !- + // make(2) + // [7] make::v = 2 -- vbuz1=vbuc1 + lda #2 + sta.z make.v + // [8] callexecute make -- call_vprc1 + jsr make + // [9] main::$1_border = make::return_border -- vbuxx=vbuz1 + ldx.z make.return_border + // [10] main::$1_bg = make::return_bg -- vbuaa=vbuz1 + lda.z make.return_bg + // a = make(2) + // [11] *((char *)&a) = main::$1_border -- _deref_pbuc1=vbuxx + stx a + // [12] *((char *)&a+OFFSET_STRUCT_COLS_BG) = main::$1_bg -- _deref_pbuc1=vbuaa + sta a+OFFSET_STRUCT_COLS_BG + // *COLS = a + // [13] *COLS = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda a-1,y + sta COLS-1,y + dey + bne !- + // main::@return + // } + // [14] return + rts +} + // make +// struct Cols make(__zp(2) char v) +make: { + .label v = 2 + .label return_border = 3 + .label return_bg = 4 + // c.border = v + // [15] make::c_border#1 = make::v -- vbuxx=vbuz1 + ldx.z v + // v+v + // [16] make::$0 = make::v + make::v -- vbuaa=vbuz1_plus_vbuz1 + txa + asl + // c.bg = v+v + // [17] make::c_bg#1 = make::$0 + // return c; + // [18] make::return_border = make::c_border#1 -- vbuz1=vbuxx + stx.z return_border + // [19] make::return_bg = make::c_bg#1 -- vbuz1=vbuaa + sta.z return_bg + // make::@return + // } + // [20] return + rts +} + // File Data +.segment Data + a: .fill SIZEOF_STRUCT_COLS, 0 + diff --git a/src/test/ref/varcall-5.sym b/src/test/ref/varcall-5.sym new file mode 100644 index 000000000..42d6b78ee --- /dev/null +++ b/src/test/ref/varcall-5.sym @@ -0,0 +1,30 @@ +__constant struct Cols * const COLS = (struct Cols *) 53280 +__constant char OFFSET_STRUCT_COLS_BG = 1 +__constant char SIZEOF_STRUCT_COLS = 2 +__loadstore struct Cols a = {} // mem[2] +void main() +char main::$0_bg // reg byte a 2.0 +char main::$0_border // reg byte x 2.0 +char main::$1_bg // reg byte a 2.0 +char main::$1_border // reg byte x 2.0 +__varcall struct Cols make(char v) +char make::$0 // reg byte a 22.0 +char make::c_bg +char make::c_bg#1 // reg byte a 11.0 +char make::c_border +char make::c_border#1 // reg byte x 7.333333333333333 +__loadstore char make::return_bg // zp[1]:4 2.5 +__loadstore char make::return_border // zp[1]:3 3.0 +__loadstore char make::v // zp[1]:2 12.333333333333332 + +zp[1]:2 [ make::v ] +reg byte x [ main::$0_border ] +reg byte a [ main::$0_bg ] +reg byte x [ main::$1_border ] +reg byte a [ main::$1_bg ] +reg byte x [ make::c_border#1 ] +reg byte a [ make::$0 ] +reg byte a [ make::c_bg#1 ] +zp[1]:3 [ make::return_border ] +zp[1]:4 [ make::return_bg ] +mem[2] [ a ] diff --git a/src/test/ref/varcall-6.asm b/src/test/ref/varcall-6.asm new file mode 100644 index 000000000..f66a117ec --- /dev/null +++ b/src/test/ref/varcall-6.asm @@ -0,0 +1,71 @@ +// Test __varcall calling convention +// Struct parameter & return value + // Commodore 64 PRG executable file +.file [name="varcall-6.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .const OFFSET_STRUCT_COLS_BG = 1 + .label COLS = $d020 +.segment Code +main: { + .const a_border = 1 + .const a_bg = 2 + // struct Cols c = plus(a, { 2, 3 }) + lda #a_border + sta.z plus.a_border + lda #a_bg + sta.z plus.a_bg + lda #2 + sta.z plus.b_border + lda #3 + sta.z plus.b_bg + jsr plus + ldx.z plus.return_border + lda.z plus.return_bg + // *COLS = c + stx COLS + sta COLS+OFFSET_STRUCT_COLS_BG + // plus(c, a) + stx.z plus.a_border + sta.z plus.a_bg + lda #a_border + sta.z plus.b_border + lda #a_bg + sta.z plus.b_bg + jsr plus + // c = plus(c, a) + ldx.z plus.return_border + lda.z plus.return_bg + // *COLS = c + stx COLS + sta COLS+OFFSET_STRUCT_COLS_BG + // } + rts +} +// struct Cols plus(__zp(7) char a_border, __zp(6) char a_bg, __zp(3) char b_border, __zp(2) char b_bg) +plus: { + .label a_border = 7 + .label a_bg = 6 + .label b_border = 3 + .label b_bg = 2 + .label return_border = 4 + .label return_bg = 5 + // a.border+b.border + lda.z a_border + clc + adc.z b_border + tax + // a.bg+b.bg + lda.z a_bg + clc + adc.z b_bg + // return { a.border+b.border, a.bg+b.bg }; + stx.z return_border + sta.z return_bg + // } + rts +} diff --git a/src/test/ref/varcall-6.cfg b/src/test/ref/varcall-6.cfg new file mode 100644 index 000000000..d1c58c825 --- /dev/null +++ b/src/test/ref/varcall-6.cfg @@ -0,0 +1,36 @@ + +void main() +main: scope:[main] from + [0] plus::a_border = main::a_border + [1] plus::a_bg = main::a_bg + [2] plus::b_border = 2 + [3] plus::b_bg = 3 + [4] callexecute plus + [5] main::c_border#0 = plus::return_border + [6] main::c_bg#0 = plus::return_bg + [7] *((char *)COLS) = main::c_border#0 + [8] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = main::c_bg#0 + [9] plus::a_border = main::c_border#0 + [10] plus::a_bg = main::c_bg#0 + [11] plus::b_border = main::a_border + [12] plus::b_bg = main::a_bg + [13] callexecute plus + [14] main::c_border#1 = plus::return_border + [15] main::c_bg#1 = plus::return_bg + [16] *((char *)COLS) = main::c_border#1 + [17] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = main::c_bg#1 + to:main::@return +main::@return: scope:[main] from main + [18] return + to:@return + +__varcall struct Cols plus(char a_border , char a_bg , char b_border , char b_bg) +plus: scope:[plus] from + [19] plus::$0 = plus::a_border + plus::b_border + [20] plus::$1 = plus::a_bg + plus::b_bg + [21] plus::return_border = plus::$0 + [22] plus::return_bg = plus::$1 + to:plus::@return +plus::@return: scope:[plus] from plus + [23] return + to:@return diff --git a/src/test/ref/varcall-6.log b/src/test/ref/varcall-6.log new file mode 100644 index 000000000..c2f9e0b26 --- /dev/null +++ b/src/test/ref/varcall-6.log @@ -0,0 +1,541 @@ +Converting parameter in __varcall procedure to load/store plus::a +Converting parameter in __varcall procedure to load/store plus::b +Converting return in __varcall procedure to load/store plus::return +Constantified RValue plus::return = (struct Cols){ plus::$0, plus::$1 } +Added struct type cast to parameter value list main::c = call plus(main::a, (struct Cols){ 2, 3 }) +Eliminating unused variable with no statement plus::a +Eliminating unused variable with no statement plus::b +Eliminating unused variable with no statement main::$0 +Eliminating unused variable with no statement main::$1 +Calling convention __varcall adding prepare/execute/finalize for { main::c_border, main::c_bg } = call plus(main::a_border, main::a_bg, 2, 3) +Calling convention __varcall adding prepare/execute/finalize for { main::$1_border, main::$1_bg } = call plus(main::c_border, main::c_bg, main::a_border, main::a_bg) + +CONTROL FLOW GRAPH SSA + +__varcall struct Cols plus(char a_border , char a_bg , char b_border , char b_bg) +plus: scope:[plus] from + plus::$0 = plus::a_border + plus::b_border + plus::$1 = plus::a_bg + plus::b_bg + plus::return_border = plus::$0 + plus::return_bg = plus::$1 + plus::return = struct-unwound {plus::return_border, plus::return_bg} + to:plus::@return +plus::@return: scope:[plus] from plus + return + to:@return + +void main() +main: scope:[main] from __start + plus::a_border = main::a_border + plus::a_bg = main::a_bg + plus::b_border = 2 + plus::b_bg = 3 + callexecute plus + main::c_border#0 = plus::return_border + main::c_bg#0 = plus::return_bg + *((char *)COLS+OFFSET_STRUCT_COLS_BORDER) = main::c_border#0 + *((char *)COLS+OFFSET_STRUCT_COLS_BG) = main::c_bg#0 + plus::a_border = main::c_border#0 + plus::a_bg = main::c_bg#0 + plus::b_border = main::a_border + plus::b_bg = main::a_bg + callexecute plus + main::$1_border = plus::return_border + main::$1_bg = plus::return_bg + main::c_border#1 = main::$1_border + main::c_bg#1 = main::$1_bg + *((char *)COLS+OFFSET_STRUCT_COLS_BORDER) = main::c_border#1 + *((char *)COLS+OFFSET_STRUCT_COLS_BG) = main::c_bg#1 + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant struct Cols * const COLS = (struct Cols *)$d020 +__constant char OFFSET_STRUCT_COLS_BG = 1 +__constant char OFFSET_STRUCT_COLS_BORDER = 0 +void __start() +void main() +char main::$1_bg +char main::$1_border +__constant char main::a_bg = 2 +__constant char main::a_border = 1 +char main::c_bg +char main::c_bg#0 +char main::c_bg#1 +char main::c_border +char main::c_border#0 +char main::c_border#1 +__varcall struct Cols plus(char a_border , char a_bg , char b_border , char b_bg) +char plus::$0 +char plus::$1 +__loadstore char plus::a_bg +__loadstore char plus::a_border +__loadstore char plus::b_bg +__loadstore char plus::b_border +__loadstore struct Cols plus::return +__loadstore char plus::return_bg +__loadstore char plus::return_border + +Adding number conversion cast (unumber) 2 in plus::b_border = 2 +Adding number conversion cast (unumber) 3 in plus::b_bg = 3 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast plus::b_border = (unumber)2 +Inlining cast plus::b_bg = (unumber)3 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (struct Cols *) 53280 +Simplifying constant integer cast 2 +Simplifying constant integer cast 3 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) 2 +Finalized unsigned number type (char) 3 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias candidate removed (volatile)plus::return_border = plus::$0 +Alias candidate removed (volatile)plus::return_bg = plus::$1 +Alias main::c_border#1 = main::$1_border +Alias main::c_bg#1 = main::$1_bg +Successful SSA optimization Pass2AliasElimination +Alias candidate removed (volatile)plus::return_border = plus::$0 +Alias candidate removed (volatile)plus::return_bg = plus::$1 +Simplifying expression containing zero (char *)COLS in [13] *((char *)COLS+OFFSET_STRUCT_COLS_BORDER) = main::c_border#0 +Simplifying expression containing zero (char *)COLS in [22] *((char *)COLS+OFFSET_STRUCT_COLS_BORDER) = main::c_border#1 +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable plus::return and assignment [4] plus::return = struct-unwound {plus::return_border, plus::return_bg} +Eliminating unused constant OFFSET_STRUCT_COLS_BORDER +Successful SSA optimization PassNEliminateUnusedVars +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Alias candidate removed (volatile)plus::return_border = plus::$0 +Alias candidate removed (volatile)plus::return_bg = plus::$1 +Alias candidate removed (volatile)plus::return_border = plus::$0 +Alias candidate removed (volatile)plus::return_bg = plus::$1 +Alias candidate removed (volatile)plus::return_border = plus::$0 +Alias candidate removed (volatile)plus::return_bg = plus::$1 +CALL GRAPH +Calls in [main] to plus:4 plus:13 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] plus::a_border = main::a_border + [1] plus::a_bg = main::a_bg + [2] plus::b_border = 2 + [3] plus::b_bg = 3 + [4] callexecute plus + [5] main::c_border#0 = plus::return_border + [6] main::c_bg#0 = plus::return_bg + [7] *((char *)COLS) = main::c_border#0 + [8] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = main::c_bg#0 + [9] plus::a_border = main::c_border#0 + [10] plus::a_bg = main::c_bg#0 + [11] plus::b_border = main::a_border + [12] plus::b_bg = main::a_bg + [13] callexecute plus + [14] main::c_border#1 = plus::return_border + [15] main::c_bg#1 = plus::return_bg + [16] *((char *)COLS) = main::c_border#1 + [17] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = main::c_bg#1 + to:main::@return +main::@return: scope:[main] from main + [18] return + to:@return + +__varcall struct Cols plus(char a_border , char a_bg , char b_border , char b_bg) +plus: scope:[plus] from + [19] plus::$0 = plus::a_border + plus::b_border + [20] plus::$1 = plus::a_bg + plus::b_bg + [21] plus::return_border = plus::$0 + [22] plus::return_bg = plus::$1 + to:plus::@return +plus::@return: scope:[plus] from plus + [23] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +char main::c_bg +char main::c_bg#0 // 1.5 +char main::c_bg#1 // 2.0 +char main::c_border +char main::c_border#0 // 1.5 +char main::c_border#1 // 2.0 +__varcall struct Cols plus(char a_border , char a_bg , char b_border , char b_bg) +char plus::$0 // 11.0 +char plus::$1 // 11.0 +__loadstore char plus::a_bg // 2.142857142857143 +__loadstore char plus::a_border // 1.875 +__loadstore char plus::b_bg // 5.0 +__loadstore char plus::b_border // 3.75 +__loadstore char plus::return_bg // 2.5 +__loadstore char plus::return_border // 3.0 + +Initial phi equivalence classes +Added variable plus::a_border to live range equivalence class [ plus::a_border ] +Added variable plus::a_bg to live range equivalence class [ plus::a_bg ] +Added variable plus::b_border to live range equivalence class [ plus::b_border ] +Added variable plus::b_bg to live range equivalence class [ plus::b_bg ] +Added variable main::c_border#0 to live range equivalence class [ main::c_border#0 ] +Added variable main::c_bg#0 to live range equivalence class [ main::c_bg#0 ] +Added variable main::c_border#1 to live range equivalence class [ main::c_border#1 ] +Added variable main::c_bg#1 to live range equivalence class [ main::c_bg#1 ] +Added variable plus::$0 to live range equivalence class [ plus::$0 ] +Added variable plus::$1 to live range equivalence class [ plus::$1 ] +Added variable plus::return_border to live range equivalence class [ plus::return_border ] +Added variable plus::return_bg to live range equivalence class [ plus::return_bg ] +Complete equivalence classes +[ plus::a_border ] +[ plus::a_bg ] +[ plus::b_border ] +[ plus::b_bg ] +[ main::c_border#0 ] +[ main::c_bg#0 ] +[ main::c_border#1 ] +[ main::c_bg#1 ] +[ plus::$0 ] +[ plus::$1 ] +[ plus::return_border ] +[ plus::return_bg ] +Allocated zp[1]:2 [ plus::$0 ] +Allocated zp[1]:3 [ plus::$1 ] +Allocated zp[1]:4 [ plus::b_bg ] +Allocated zp[1]:5 [ plus::b_border ] +Allocated zp[1]:6 [ plus::return_border ] +Allocated zp[1]:7 [ plus::return_bg ] +Allocated zp[1]:8 [ plus::a_bg ] +Allocated zp[1]:9 [ main::c_border#1 ] +Allocated zp[1]:10 [ main::c_bg#1 ] +Allocated zp[1]:11 [ plus::a_border ] +Allocated zp[1]:12 [ main::c_border#0 ] +Allocated zp[1]:13 [ main::c_bg#0 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] plus::a_border = main::a_border [ plus::a_border ] ( [ plus::a_border ] { } ) always clobbers reg byte a +Statement [1] plus::a_bg = main::a_bg [ plus::a_border plus::a_bg ] ( [ plus::a_border plus::a_bg ] { } ) always clobbers reg byte a +Statement [2] plus::b_border = 2 [ plus::a_border plus::a_bg plus::b_border ] ( [ plus::a_border plus::a_bg plus::b_border ] { } ) always clobbers reg byte a +Statement [3] plus::b_bg = 3 [ plus::a_border plus::a_bg plus::b_border plus::b_bg ] ( [ plus::a_border plus::a_bg plus::b_border plus::b_bg ] { } ) always clobbers reg byte a +Statement [11] plus::b_border = main::a_border [ plus::a_border plus::a_bg plus::b_border ] ( [ plus::a_border plus::a_bg plus::b_border ] { } ) always clobbers reg byte a +Statement [12] plus::b_bg = main::a_bg [ plus::a_border plus::a_bg plus::b_border plus::b_bg ] ( [ plus::a_border plus::a_bg plus::b_border plus::b_bg ] { } ) always clobbers reg byte a +Statement [19] plus::$0 = plus::a_border + plus::b_border [ plus::a_bg plus::b_bg plus::$0 ] ( plus:4 [ plus::a_bg plus::b_bg plus::$0 ] { { main::c_border#0 = plus::a_border } { main::c_bg#0 = plus::a_bg } } plus:13 [ plus::a_bg plus::b_bg plus::$0 ] { { main::c_border#0 = plus::a_border } { main::c_bg#0 = plus::a_bg } } ) always clobbers reg byte a +Statement [20] plus::$1 = plus::a_bg + plus::b_bg [ plus::$0 plus::$1 ] ( plus:4 [ plus::$0 plus::$1 ] { { main::c_border#0 = plus::a_border } { main::c_bg#0 = plus::a_bg } } plus:13 [ plus::$0 plus::$1 ] { { main::c_border#0 = plus::a_border } { main::c_bg#0 = plus::a_bg } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:2 [ plus::$0 ] +Statement [0] plus::a_border = main::a_border [ plus::a_border ] ( [ plus::a_border ] { } ) always clobbers reg byte a +Statement [1] plus::a_bg = main::a_bg [ plus::a_border plus::a_bg ] ( [ plus::a_border plus::a_bg ] { } ) always clobbers reg byte a +Statement [2] plus::b_border = 2 [ plus::a_border plus::a_bg plus::b_border ] ( [ plus::a_border plus::a_bg plus::b_border ] { } ) always clobbers reg byte a +Statement [3] plus::b_bg = 3 [ plus::a_border plus::a_bg plus::b_border plus::b_bg ] ( [ plus::a_border plus::a_bg plus::b_border plus::b_bg ] { } ) always clobbers reg byte a +Statement [11] plus::b_border = main::a_border [ plus::a_border plus::a_bg plus::b_border ] ( [ plus::a_border plus::a_bg plus::b_border ] { } ) always clobbers reg byte a +Statement [12] plus::b_bg = main::a_bg [ plus::a_border plus::a_bg plus::b_border plus::b_bg ] ( [ plus::a_border plus::a_bg plus::b_border plus::b_bg ] { } ) always clobbers reg byte a +Statement [19] plus::$0 = plus::a_border + plus::b_border [ plus::a_bg plus::b_bg plus::$0 ] ( plus:4 [ plus::a_bg plus::b_bg plus::$0 ] { { main::c_border#0 = plus::a_border } { main::c_bg#0 = plus::a_bg } } plus:13 [ plus::a_bg plus::b_bg plus::$0 ] { { main::c_border#0 = plus::a_border } { main::c_bg#0 = plus::a_bg } } ) always clobbers reg byte a +Statement [20] plus::$1 = plus::a_bg + plus::b_bg [ plus::$0 plus::$1 ] ( plus:4 [ plus::$0 plus::$1 ] { { main::c_border#0 = plus::a_border } { main::c_bg#0 = plus::a_bg } } plus:13 [ plus::$0 plus::$1 ] { { main::c_border#0 = plus::a_border } { main::c_bg#0 = plus::a_bg } } ) always clobbers reg byte a +Potential registers zp[1]:11 [ plus::a_border ] : zp[1]:11 , +Potential registers zp[1]:8 [ plus::a_bg ] : zp[1]:8 , +Potential registers zp[1]:5 [ plus::b_border ] : zp[1]:5 , +Potential registers zp[1]:4 [ plus::b_bg ] : zp[1]:4 , +Potential registers zp[1]:12 [ main::c_border#0 ] : zp[1]:12 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:13 [ main::c_bg#0 ] : zp[1]:13 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:9 [ main::c_border#1 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ main::c_bg#1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ plus::$0 ] : zp[1]:2 , reg byte x , reg byte y , +Potential registers zp[1]:3 [ plus::$1 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:6 [ plus::return_border ] : zp[1]:6 , +Potential registers zp[1]:7 [ plus::return_bg ] : zp[1]:7 , + +REGISTER UPLIFT SCOPES +Uplift Scope [plus] 11: zp[1]:2 [ plus::$0 ] 11: zp[1]:3 [ plus::$1 ] 5: zp[1]:4 [ plus::b_bg ] 3.75: zp[1]:5 [ plus::b_border ] 3: zp[1]:6 [ plus::return_border ] 2.5: zp[1]:7 [ plus::return_bg ] 2.14: zp[1]:8 [ plus::a_bg ] 1.88: zp[1]:11 [ plus::a_border ] +Uplift Scope [main] 2: zp[1]:9 [ main::c_border#1 ] 2: zp[1]:10 [ main::c_bg#1 ] 1.5: zp[1]:12 [ main::c_border#0 ] 1.5: zp[1]:13 [ main::c_bg#0 ] +Uplift Scope [Cols] +Uplift Scope [] + +Uplifting [plus] best 148 combination reg byte x [ plus::$0 ] reg byte a [ plus::$1 ] zp[1]:4 [ plus::b_bg ] zp[1]:5 [ plus::b_border ] zp[1]:6 [ plus::return_border ] zp[1]:7 [ plus::return_bg ] zp[1]:8 [ plus::a_bg ] zp[1]:11 [ plus::a_border ] +Uplifting [main] best 124 combination reg byte x [ main::c_border#1 ] zp[1]:10 [ main::c_bg#1 ] reg byte x [ main::c_border#0 ] reg byte a [ main::c_bg#0 ] +Limited combination testing to 100 combinations of 256 possible. +Uplifting [Cols] best 124 combination +Uplifting [] best 124 combination +Attempting to uplift remaining variables inzp[1]:4 [ plus::b_bg ] +Uplifting [plus] best 124 combination zp[1]:4 [ plus::b_bg ] +Attempting to uplift remaining variables inzp[1]:5 [ plus::b_border ] +Uplifting [plus] best 124 combination zp[1]:5 [ plus::b_border ] +Attempting to uplift remaining variables inzp[1]:6 [ plus::return_border ] +Uplifting [plus] best 124 combination zp[1]:6 [ plus::return_border ] +Attempting to uplift remaining variables inzp[1]:7 [ plus::return_bg ] +Uplifting [plus] best 124 combination zp[1]:7 [ plus::return_bg ] +Attempting to uplift remaining variables inzp[1]:8 [ plus::a_bg ] +Uplifting [plus] best 124 combination zp[1]:8 [ plus::a_bg ] +Attempting to uplift remaining variables inzp[1]:10 [ main::c_bg#1 ] +Uplifting [main] best 118 combination reg byte a [ main::c_bg#1 ] +Attempting to uplift remaining variables inzp[1]:11 [ plus::a_border ] +Uplifting [plus] best 118 combination zp[1]:11 [ plus::a_border ] +Allocated (was zp[1]:4) zp[1]:2 [ plus::b_bg ] +Allocated (was zp[1]:5) zp[1]:3 [ plus::b_border ] +Allocated (was zp[1]:6) zp[1]:4 [ plus::return_border ] +Allocated (was zp[1]:7) zp[1]:5 [ plus::return_bg ] +Allocated (was zp[1]:8) zp[1]:6 [ plus::a_bg ] +Allocated (was zp[1]:11) zp[1]:7 [ plus::a_border ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test __varcall calling convention +// Struct parameter & return value + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-6.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const OFFSET_STRUCT_COLS_BG = 1 + .label COLS = $d020 +.segment Code + // main +main: { + .const a_border = 1 + .const a_bg = 2 + // [0] plus::a_border = main::a_border -- vbuz1=vbuc1 + lda #a_border + sta.z plus.a_border + // [1] plus::a_bg = main::a_bg -- vbuz1=vbuc1 + lda #a_bg + sta.z plus.a_bg + // [2] plus::b_border = 2 -- vbuz1=vbuc1 + lda #2 + sta.z plus.b_border + // [3] plus::b_bg = 3 -- vbuz1=vbuc1 + lda #3 + sta.z plus.b_bg + // [4] callexecute plus -- call_vprc1 + jsr plus + // [5] main::c_border#0 = plus::return_border -- vbuxx=vbuz1 + ldx.z plus.return_border + // [6] main::c_bg#0 = plus::return_bg -- vbuaa=vbuz1 + lda.z plus.return_bg + // [7] *((char *)COLS) = main::c_border#0 -- _deref_pbuc1=vbuxx + stx COLS + // [8] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = main::c_bg#0 -- _deref_pbuc1=vbuaa + sta COLS+OFFSET_STRUCT_COLS_BG + // [9] plus::a_border = main::c_border#0 -- vbuz1=vbuxx + stx.z plus.a_border + // [10] plus::a_bg = main::c_bg#0 -- vbuz1=vbuaa + sta.z plus.a_bg + // [11] plus::b_border = main::a_border -- vbuz1=vbuc1 + lda #a_border + sta.z plus.b_border + // [12] plus::b_bg = main::a_bg -- vbuz1=vbuc1 + lda #a_bg + sta.z plus.b_bg + // [13] callexecute plus -- call_vprc1 + jsr plus + // [14] main::c_border#1 = plus::return_border -- vbuxx=vbuz1 + ldx.z plus.return_border + // [15] main::c_bg#1 = plus::return_bg -- vbuaa=vbuz1 + lda.z plus.return_bg + // [16] *((char *)COLS) = main::c_border#1 -- _deref_pbuc1=vbuxx + stx COLS + // [17] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = main::c_bg#1 -- _deref_pbuc1=vbuaa + sta COLS+OFFSET_STRUCT_COLS_BG + jmp __breturn + // main::@return + __breturn: + // [18] return + rts +} + // plus +// struct Cols plus(__zp(7) char a_border, __zp(6) char a_bg, __zp(3) char b_border, __zp(2) char b_bg) +plus: { + .label a_border = 7 + .label a_bg = 6 + .label b_border = 3 + .label b_bg = 2 + .label return_border = 4 + .label return_bg = 5 + // [19] plus::$0 = plus::a_border + plus::b_border -- vbuxx=vbuz1_plus_vbuz2 + lda.z a_border + clc + adc.z b_border + tax + // [20] plus::$1 = plus::a_bg + plus::b_bg -- vbuaa=vbuz1_plus_vbuz2 + lda.z a_bg + clc + adc.z b_bg + // [21] plus::return_border = plus::$0 -- vbuz1=vbuxx + stx.z return_border + // [22] plus::return_bg = plus::$1 -- vbuz1=vbuaa + sta.z return_bg + jmp __breturn + // plus::@return + __breturn: + // [23] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant struct Cols * const COLS = (struct Cols *) 53280 +__constant char OFFSET_STRUCT_COLS_BG = 1 +void main() +__constant char main::a_bg = 2 +__constant char main::a_border = 1 +char main::c_bg +char main::c_bg#0 // reg byte a 1.5 +char main::c_bg#1 // reg byte a 2.0 +char main::c_border +char main::c_border#0 // reg byte x 1.5 +char main::c_border#1 // reg byte x 2.0 +__varcall struct Cols plus(char a_border , char a_bg , char b_border , char b_bg) +char plus::$0 // reg byte x 11.0 +char plus::$1 // reg byte a 11.0 +__loadstore char plus::a_bg // zp[1]:6 2.142857142857143 +__loadstore char plus::a_border // zp[1]:7 1.875 +__loadstore char plus::b_bg // zp[1]:2 5.0 +__loadstore char plus::b_border // zp[1]:3 3.75 +__loadstore char plus::return_bg // zp[1]:5 2.5 +__loadstore char plus::return_border // zp[1]:4 3.0 + +zp[1]:7 [ plus::a_border ] +zp[1]:6 [ plus::a_bg ] +zp[1]:3 [ plus::b_border ] +zp[1]:2 [ plus::b_bg ] +reg byte x [ main::c_border#0 ] +reg byte a [ main::c_bg#0 ] +reg byte x [ main::c_border#1 ] +reg byte a [ main::c_bg#1 ] +reg byte x [ plus::$0 ] +reg byte a [ plus::$1 ] +zp[1]:4 [ plus::return_border ] +zp[1]:5 [ plus::return_bg ] + + +FINAL ASSEMBLER +Score: 112 + + // File Comments +// Test __varcall calling convention +// Struct parameter & return value + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-6.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const OFFSET_STRUCT_COLS_BG = 1 + .label COLS = $d020 +.segment Code + // main +main: { + .const a_border = 1 + .const a_bg = 2 + // struct Cols c = plus(a, { 2, 3 }) + // [0] plus::a_border = main::a_border -- vbuz1=vbuc1 + lda #a_border + sta.z plus.a_border + // [1] plus::a_bg = main::a_bg -- vbuz1=vbuc1 + lda #a_bg + sta.z plus.a_bg + // [2] plus::b_border = 2 -- vbuz1=vbuc1 + lda #2 + sta.z plus.b_border + // [3] plus::b_bg = 3 -- vbuz1=vbuc1 + lda #3 + sta.z plus.b_bg + // [4] callexecute plus -- call_vprc1 + jsr plus + // [5] main::c_border#0 = plus::return_border -- vbuxx=vbuz1 + ldx.z plus.return_border + // [6] main::c_bg#0 = plus::return_bg -- vbuaa=vbuz1 + lda.z plus.return_bg + // *COLS = c + // [7] *((char *)COLS) = main::c_border#0 -- _deref_pbuc1=vbuxx + stx COLS + // [8] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = main::c_bg#0 -- _deref_pbuc1=vbuaa + sta COLS+OFFSET_STRUCT_COLS_BG + // plus(c, a) + // [9] plus::a_border = main::c_border#0 -- vbuz1=vbuxx + stx.z plus.a_border + // [10] plus::a_bg = main::c_bg#0 -- vbuz1=vbuaa + sta.z plus.a_bg + // [11] plus::b_border = main::a_border -- vbuz1=vbuc1 + lda #a_border + sta.z plus.b_border + // [12] plus::b_bg = main::a_bg -- vbuz1=vbuc1 + lda #a_bg + sta.z plus.b_bg + // [13] callexecute plus -- call_vprc1 + jsr plus + // c = plus(c, a) + // [14] main::c_border#1 = plus::return_border -- vbuxx=vbuz1 + ldx.z plus.return_border + // [15] main::c_bg#1 = plus::return_bg -- vbuaa=vbuz1 + lda.z plus.return_bg + // *COLS = c + // [16] *((char *)COLS) = main::c_border#1 -- _deref_pbuc1=vbuxx + stx COLS + // [17] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = main::c_bg#1 -- _deref_pbuc1=vbuaa + sta COLS+OFFSET_STRUCT_COLS_BG + // main::@return + // } + // [18] return + rts +} + // plus +// struct Cols plus(__zp(7) char a_border, __zp(6) char a_bg, __zp(3) char b_border, __zp(2) char b_bg) +plus: { + .label a_border = 7 + .label a_bg = 6 + .label b_border = 3 + .label b_bg = 2 + .label return_border = 4 + .label return_bg = 5 + // a.border+b.border + // [19] plus::$0 = plus::a_border + plus::b_border -- vbuxx=vbuz1_plus_vbuz2 + lda.z a_border + clc + adc.z b_border + tax + // a.bg+b.bg + // [20] plus::$1 = plus::a_bg + plus::b_bg -- vbuaa=vbuz1_plus_vbuz2 + lda.z a_bg + clc + adc.z b_bg + // return { a.border+b.border, a.bg+b.bg }; + // [21] plus::return_border = plus::$0 -- vbuz1=vbuxx + stx.z return_border + // [22] plus::return_bg = plus::$1 -- vbuz1=vbuaa + sta.z return_bg + // plus::@return + // } + // [23] return + rts +} + // File Data + diff --git a/src/test/ref/varcall-6.sym b/src/test/ref/varcall-6.sym new file mode 100644 index 000000000..e37f86116 --- /dev/null +++ b/src/test/ref/varcall-6.sym @@ -0,0 +1,33 @@ +__constant struct Cols * const COLS = (struct Cols *) 53280 +__constant char OFFSET_STRUCT_COLS_BG = 1 +void main() +__constant char main::a_bg = 2 +__constant char main::a_border = 1 +char main::c_bg +char main::c_bg#0 // reg byte a 1.5 +char main::c_bg#1 // reg byte a 2.0 +char main::c_border +char main::c_border#0 // reg byte x 1.5 +char main::c_border#1 // reg byte x 2.0 +__varcall struct Cols plus(char a_border , char a_bg , char b_border , char b_bg) +char plus::$0 // reg byte x 11.0 +char plus::$1 // reg byte a 11.0 +__loadstore char plus::a_bg // zp[1]:6 2.142857142857143 +__loadstore char plus::a_border // zp[1]:7 1.875 +__loadstore char plus::b_bg // zp[1]:2 5.0 +__loadstore char plus::b_border // zp[1]:3 3.75 +__loadstore char plus::return_bg // zp[1]:5 2.5 +__loadstore char plus::return_border // zp[1]:4 3.0 + +zp[1]:7 [ plus::a_border ] +zp[1]:6 [ plus::a_bg ] +zp[1]:3 [ plus::b_border ] +zp[1]:2 [ plus::b_bg ] +reg byte x [ main::c_border#0 ] +reg byte a [ main::c_bg#0 ] +reg byte x [ main::c_border#1 ] +reg byte a [ main::c_bg#1 ] +reg byte x [ plus::$0 ] +reg byte a [ plus::$1 ] +zp[1]:4 [ plus::return_border ] +zp[1]:5 [ plus::return_bg ] diff --git a/src/test/ref/varcall-7.asm b/src/test/ref/varcall-7.asm new file mode 100644 index 000000000..86486cc8b --- /dev/null +++ b/src/test/ref/varcall-7.asm @@ -0,0 +1,33 @@ +// Test __varcall calling convention +// Struct parameter & return value - only a single call + // Commodore 64 PRG executable file +.file [name="varcall-7.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .const OFFSET_STRUCT_COLS_BG = 1 + .label COLS = $d020 +.segment Code +main: { + .label a_border = 1 + .label a_bg = 2 + .label b_border = 2 + .label b_bg = 3 + // struct Cols c = plus(a, b) + jsr plus + // *COLS = c + lda #plus.return_border + sta COLS + lda #plus.return_bg + sta COLS+OFFSET_STRUCT_COLS_BG + // } + rts +} +plus: { + .label return_border = main.a_border+main.b_border + .label return_bg = main.a_bg+main.b_bg + rts +} diff --git a/src/test/ref/varcall-7.cfg b/src/test/ref/varcall-7.cfg new file mode 100644 index 000000000..9cec35c4c --- /dev/null +++ b/src/test/ref/varcall-7.cfg @@ -0,0 +1,19 @@ + +void main() +main: scope:[main] from + [0] phi() + [1] callexecute plus + [2] *((char *)COLS) = plus::return_border + [3] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = plus::return_bg + to:main::@return +main::@return: scope:[main] from main + [4] return + to:@return + +__varcall struct Cols plus() +plus: scope:[plus] from + [5] phi() + to:plus::@return +plus::@return: scope:[plus] from plus + [6] return + to:@return diff --git a/src/test/ref/varcall-7.log b/src/test/ref/varcall-7.log new file mode 100644 index 000000000..f9db3a4eb --- /dev/null +++ b/src/test/ref/varcall-7.log @@ -0,0 +1,301 @@ +Converting parameter in __varcall procedure to load/store plus::a +Converting parameter in __varcall procedure to load/store plus::b +Converting return in __varcall procedure to load/store plus::return +Constantified RValue plus::return = (struct Cols){ plus::$0, plus::$1 } +Eliminating unused variable with no statement plus::a +Eliminating unused variable with no statement plus::b +Eliminating unused variable with no statement main::$0 +Calling convention __varcall adding prepare/execute/finalize for { main::c_border, main::c_bg } = call plus(main::a_border, main::a_bg, main::b_border, main::b_bg) + +CONTROL FLOW GRAPH SSA + +__varcall struct Cols plus(char a_border , char a_bg , char b_border , char b_bg) +plus: scope:[plus] from + plus::$0 = plus::a_border + plus::b_border + plus::$1 = plus::a_bg + plus::b_bg + plus::return_border = plus::$0 + plus::return_bg = plus::$1 + plus::return = struct-unwound {plus::return_border, plus::return_bg} + to:plus::@return +plus::@return: scope:[plus] from plus + return + to:@return + +void main() +main: scope:[main] from __start + plus::a_border = main::a_border + plus::a_bg = main::a_bg + plus::b_border = main::b_border + plus::b_bg = main::b_bg + callexecute plus + main::c_border#0 = plus::return_border + main::c_bg#0 = plus::return_bg + *((char *)COLS+OFFSET_STRUCT_COLS_BORDER) = main::c_border#0 + *((char *)COLS+OFFSET_STRUCT_COLS_BG) = main::c_bg#0 + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant struct Cols * const COLS = (struct Cols *)$d020 +__constant char OFFSET_STRUCT_COLS_BG = 1 +__constant char OFFSET_STRUCT_COLS_BORDER = 0 +void __start() +void main() +__constant char main::a_bg = 2 +__constant char main::a_border = 1 +__constant char main::b_bg = 3 +__constant char main::b_border = 2 +struct Cols main::c +char main::c_bg +char main::c_bg#0 +char main::c_border +char main::c_border#0 +__varcall struct Cols plus(char a_border , char a_bg , char b_border , char b_bg) +char plus::$0 +char plus::$1 +__loadstore char plus::a_bg +__loadstore char plus::a_border +__loadstore char plus::b_bg +__loadstore char plus::b_border +__loadstore struct Cols plus::return +__loadstore char plus::return_bg +__loadstore char plus::return_border + +Simplifying constant pointer cast (struct Cols *) 53280 +Successful SSA optimization PassNCastSimplification +Alias candidate removed (volatile)plus::return_border = plus::$0 +Alias candidate removed (volatile)plus::return_bg = plus::$1 +Constant plus::a_border = main::a_border +Constant plus::a_bg = main::a_bg +Constant plus::b_border = main::b_border +Constant plus::b_bg = main::b_bg +Successful SSA optimization Pass2ConstantIdentification +Simplifying expression containing zero (char *)COLS in [13] *((char *)COLS+OFFSET_STRUCT_COLS_BORDER) = main::c_border#0 +Successful SSA optimization PassNSimplifyExpressionWithZero +Eliminating unused variable plus::return and assignment [4] plus::return = struct-unwound {plus::return_border, plus::return_bg} +Eliminating unused constant OFFSET_STRUCT_COLS_BORDER +Successful SSA optimization PassNEliminateUnusedVars +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Alias candidate removed (volatile)plus::return_border = plus::$0 +Alias candidate removed (volatile)plus::return_bg = plus::$1 +Constant right-side identified [0] plus::$0 = plus::a_border + plus::b_border +Constant right-side identified [1] plus::$1 = plus::a_bg + plus::b_bg +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant plus::$0 = plus::a_border+plus::b_border +Constant plus::$1 = plus::a_bg+plus::b_bg +Successful SSA optimization Pass2ConstantIdentification +Constant plus::return_border = plus::$0 +Constant plus::return_bg = plus::$1 +Successful SSA optimization Pass2ConstantIdentification +Constant main::c_border#0 = plus::return_border +Constant main::c_bg#0 = plus::return_bg +Successful SSA optimization Pass2ConstantIdentification +Parameter inlined plus::b_bg +Parameter inlined plus::a_bg +Parameter inlined plus::b_border +Parameter inlined plus::a_border +Constant inlined main::c_border#0 = plus::return_border +Constant inlined plus::b_bg = main::b_bg +Constant inlined plus::a_bg = main::a_bg +Constant inlined plus::b_border = main::b_border +Constant inlined plus::a_border = main::a_border +Constant inlined plus::$1 = main::a_bg+main::b_bg +Constant inlined plus::$0 = main::a_border+main::b_border +Constant inlined main::c_bg#0 = plus::return_bg +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of main +Adding NOP phi() at start of plus +CALL GRAPH +Calls in [main] to plus:1 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Adding NOP phi() at start of main +Adding NOP phi() at start of plus + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + [1] callexecute plus + [2] *((char *)COLS) = plus::return_border + [3] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = plus::return_bg + to:main::@return +main::@return: scope:[main] from main + [4] return + to:@return + +__varcall struct Cols plus() +plus: scope:[plus] from + [5] phi() + to:plus::@return +plus::@return: scope:[plus] from plus + [6] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +struct Cols main::c +char main::c_bg +char main::c_border +__varcall struct Cols plus() + +Initial phi equivalence classes +Complete equivalence classes +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [2] *((char *)COLS) = plus::return_border [ ] ( [ ] { } ) always clobbers reg byte a +Statement [3] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = plus::return_bg [ ] ( [ ] { } ) always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [Cols] +Uplift Scope [plus] +Uplift Scope [main] +Uplift Scope [] + +Uplifting [Cols] best 63 combination +Uplifting [plus] best 63 combination +Uplifting [main] best 63 combination +Uplifting [] best 63 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test __varcall calling convention +// Struct parameter & return value - only a single call + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-7.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const OFFSET_STRUCT_COLS_BG = 1 + .label COLS = $d020 +.segment Code + // main +main: { + .label a_border = 1 + .label a_bg = 2 + .label b_border = 2 + .label b_bg = 3 + // [1] callexecute plus -- call_vprc1 + jsr plus + // [2] *((char *)COLS) = plus::return_border -- _deref_pbuc1=vbuc2 + lda #plus.return_border + sta COLS + // [3] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = plus::return_bg -- _deref_pbuc1=vbuc2 + lda #plus.return_bg + sta COLS+OFFSET_STRUCT_COLS_BG + jmp __breturn + // main::@return + __breturn: + // [4] return + rts +} + // plus +plus: { + .label return_border = main.a_border+main.b_border + .label return_bg = main.a_bg+main.b_bg + jmp __breturn + // plus::@return + __breturn: + // [6] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant struct Cols * const COLS = (struct Cols *) 53280 +__constant char OFFSET_STRUCT_COLS_BG = 1 +void main() +__constant char main::a_bg = 2 +__constant char main::a_border = 1 +__constant char main::b_bg = 3 +__constant char main::b_border = 2 +struct Cols main::c +char main::c_bg +char main::c_border +__varcall struct Cols plus() +__constant char plus::return_bg = main::a_bg+main::b_bg +__constant char plus::return_border = main::a_border+main::b_border + + + +FINAL ASSEMBLER +Score: 30 + + // File Comments +// Test __varcall calling convention +// Struct parameter & return value - only a single call + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-7.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const OFFSET_STRUCT_COLS_BG = 1 + .label COLS = $d020 +.segment Code + // main +main: { + .label a_border = 1 + .label a_bg = 2 + .label b_border = 2 + .label b_bg = 3 + // struct Cols c = plus(a, b) + // [1] callexecute plus -- call_vprc1 + jsr plus + // *COLS = c + // [2] *((char *)COLS) = plus::return_border -- _deref_pbuc1=vbuc2 + lda #plus.return_border + sta COLS + // [3] *((char *)COLS+OFFSET_STRUCT_COLS_BG) = plus::return_bg -- _deref_pbuc1=vbuc2 + lda #plus.return_bg + sta COLS+OFFSET_STRUCT_COLS_BG + // main::@return + // } + // [4] return + rts +} + // plus +plus: { + .label return_border = main.a_border+main.b_border + .label return_bg = main.a_bg+main.b_bg + // plus::@return + // [6] return + rts +} + // File Data + diff --git a/src/test/ref/varcall-7.sym b/src/test/ref/varcall-7.sym new file mode 100644 index 000000000..135b38925 --- /dev/null +++ b/src/test/ref/varcall-7.sym @@ -0,0 +1,14 @@ +__constant struct Cols * const COLS = (struct Cols *) 53280 +__constant char OFFSET_STRUCT_COLS_BG = 1 +void main() +__constant char main::a_bg = 2 +__constant char main::a_border = 1 +__constant char main::b_bg = 3 +__constant char main::b_border = 2 +struct Cols main::c +char main::c_bg +char main::c_border +__varcall struct Cols plus() +__constant char plus::return_bg = main::a_bg+main::b_bg +__constant char plus::return_border = main::a_border+main::b_border + diff --git a/src/test/ref/varcall-8.asm b/src/test/ref/varcall-8.asm new file mode 100644 index 000000000..a2f170325 --- /dev/null +++ b/src/test/ref/varcall-8.asm @@ -0,0 +1,102 @@ +// Test __varcall calling convention +// Pointer to Struct parameter & return value + // Commodore 64 PRG executable file +.file [name="varcall-8.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .const SIZEOF_STRUCT_COLS = 2 + .const OFFSET_STRUCT_COLS_BG = 1 + .label COLS = $d020 +.segment Code +main: { + .label a = 6 + .label b = 8 + .label c = $a + .label m = 2 + // struct Cols a = { 1, 7 } + ldy #SIZEOF_STRUCT_COLS + !: + lda __0-1,y + sta a-1,y + dey + bne !- + // struct Cols b = { 2, 6 } + ldy #SIZEOF_STRUCT_COLS + !: + lda __1-1,y + sta b-1,y + dey + bne !- + // struct Cols c = { 3, 5 } + ldy #SIZEOF_STRUCT_COLS + !: + lda __2-1,y + sta c-1,y + dey + bne !- + // struct Cols *m = min(&a,&b) + lda #a + sta.z min.a+1 + lda #b + sta.z min.b+1 + jsr min + // *COLS = *m + ldx #SIZEOF_STRUCT_COLS + ldy #0 + !: + lda (m),y + sta COLS,y + iny + dex + bne !- + // min(m,&c) + lda #c + sta.z min.b+1 + jsr min + // m = min(m,&c) + // *COLS = *m + ldx #SIZEOF_STRUCT_COLS + ldy #0 + !: + lda (m),y + sta COLS,y + iny + dex + bne !- + // } + rts +} +// __zp(2) struct Cols * min(__zp(2) struct Cols *a, __zp(4) struct Cols *b) +min: { + .label a = 2 + .label b = 4 + .label return = 2 + // if(a->bg < b->bg) + ldy #OFFSET_STRUCT_COLS_BG + lda (b),y + cmp (a),y + bcc __breturn + // return b; + lda.z b + sta.z return + lda.z b+1 + sta.z return+1 + __breturn: + // } + rts + // return a; +} +.segment Data + __0: .byte 1, 7 + __1: .byte 2, 6 + __2: .byte 3, 5 diff --git a/src/test/ref/varcall-8.cfg b/src/test/ref/varcall-8.cfg new file mode 100644 index 000000000..b1baa09cd --- /dev/null +++ b/src/test/ref/varcall-8.cfg @@ -0,0 +1,34 @@ + +void main() +main: scope:[main] from + [0] *(&main::a) = memcpy(*(&$0), struct Cols, SIZEOF_STRUCT_COLS) + [1] *(&main::b) = memcpy(*(&$1), struct Cols, SIZEOF_STRUCT_COLS) + [2] *(&main::c) = memcpy(*(&$2), struct Cols, SIZEOF_STRUCT_COLS) + [3] min::a = &main::a + [4] min::b = &main::b + [5] callexecute min + [6] main::m#0 = min::return + [7] *COLS = memcpy(*main::m#0, struct Cols, SIZEOF_STRUCT_COLS) + [8] min::a = main::m#0 + [9] min::b = &main::c + [10] callexecute min + [11] main::m#1 = min::return + [12] *COLS = memcpy(*main::m#1, struct Cols, SIZEOF_STRUCT_COLS) + to:main::@return +main::@return: scope:[main] from main + [13] return + to:@return + +__varcall struct Cols * min(struct Cols *a , struct Cols *b) +min: scope:[min] from + [14] if(((char *)min::a)[OFFSET_STRUCT_COLS_BG]<((char *)min::b)[OFFSET_STRUCT_COLS_BG]) goto min::@1 + to:min::@2 +min::@2: scope:[min] from min + [15] min::return = min::b + to:min::@return +min::@return: scope:[min] from min::@1 min::@2 + [16] return + to:@return +min::@1: scope:[min] from min + [17] min::return = min::a + to:min::@return diff --git a/src/test/ref/varcall-8.log b/src/test/ref/varcall-8.log new file mode 100644 index 000000000..29ecb72f8 --- /dev/null +++ b/src/test/ref/varcall-8.log @@ -0,0 +1,539 @@ +Setting struct to load/store in variable affected by address-of main::m = call min(&main::a, &main::b) +Setting struct to load/store in variable affected by address-of main::m = call min(&main::a, &main::b) +Setting struct to load/store in variable affected by address-of main::$1 = call min(main::m, &main::c) +Converting parameter in __varcall procedure to load/store min::a +Converting parameter in __varcall procedure to load/store min::b +Converting return in __varcall procedure to load/store min::return +Eliminating unused variable with no statement main::$0 +Calling convention __varcall adding prepare/execute/finalize for main::m = call min(&main::a, &main::b) +Calling convention __varcall adding prepare/execute/finalize for main::$1 = call min(main::m, &main::c) +Calling convention VAR_CALL adding return value assignment main::m = min::return +Calling convention VAR_CALL adding return value assignment main::$1 = min::return +Removing C-classic struct-unwound assignment main::a = struct-unwound {*(&main::a)} +Removing C-classic struct-unwound assignment main::b = struct-unwound {*(&main::b)} +Removing C-classic struct-unwound assignment main::c = struct-unwound {*(&main::c)} + +CONTROL FLOW GRAPH SSA + +__varcall struct Cols * min(struct Cols *a , struct Cols *b) +min: scope:[min] from + min::$3 = (char *)min::a + min::$1 = min::$3 + OFFSET_STRUCT_COLS_BG + min::$4 = (char *)min::b + min::$2 = min::$4 + OFFSET_STRUCT_COLS_BG + min::$0 = *min::$1 < *min::$2 + if(min::$0) goto min::@1 + to:min::@2 +min::@1: scope:[min] from min + min::return = min::a + to:min::@return +min::@2: scope:[min] from min + min::return = min::b + to:min::@return +min::@return: scope:[min] from min::@1 min::@2 + return + to:@return + +void main() +main: scope:[main] from __start + *(&main::a) = memcpy(*(&$0), struct Cols, SIZEOF_STRUCT_COLS) + *(&main::b) = memcpy(*(&$1), struct Cols, SIZEOF_STRUCT_COLS) + *(&main::c) = memcpy(*(&$2), struct Cols, SIZEOF_STRUCT_COLS) + min::a = &main::a + min::b = &main::b + callexecute min + main::m#0 = min::return + *COLS = memcpy(*main::m#0, struct Cols, SIZEOF_STRUCT_COLS) + min::a = main::m#0 + min::b = &main::c + callexecute min + main::$1 = min::return + main::m#1 = main::$1 + *COLS = memcpy(*main::m#1, struct Cols, SIZEOF_STRUCT_COLS) + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant struct Cols $0 = { border: 1, bg: 7 } +__constant struct Cols $1 = { border: 2, bg: 6 } +__constant struct Cols $2 = { border: 3, bg: 5 } +__constant struct Cols * const COLS = (struct Cols *)$d020 +__constant char OFFSET_STRUCT_COLS_BG = 1 +__constant char SIZEOF_STRUCT_COLS = 2 +void __start() +void main() +struct Cols *main::$1 +__loadstore volatile struct Cols main::a +__loadstore volatile struct Cols main::b +__loadstore volatile struct Cols main::c +struct Cols *main::m +struct Cols *main::m#0 +struct Cols *main::m#1 +__varcall struct Cols * min(struct Cols *a , struct Cols *b) +bool min::$0 +char *min::$1 +char *min::$2 +char *min::$3 +char *min::$4 +__loadstore struct Cols *min::a +__loadstore struct Cols *min::b +__loadstore struct Cols *min::return + +Simplifying constant pointer cast (struct Cols *) 53280 +Successful SSA optimization PassNCastSimplification +Alias main::m#1 = main::$1 +Successful SSA optimization Pass2AliasElimination +Simple Condition min::$0 [5] if(*min::$1<*min::$2) goto min::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Converting *(pointer+n) to pointer[n] [5] if(*min::$1<*min::$2) goto min::@1 -- min::$3[OFFSET_STRUCT_COLS_BG] +Converting *(pointer+n) to pointer[n] [5] if(min::$3[OFFSET_STRUCT_COLS_BG]<*min::$2) goto min::@1 -- min::$4[OFFSET_STRUCT_COLS_BG] +Successful SSA optimization Pass2InlineDerefIdx +Eliminating unused variable min::$1 and assignment [1] min::$1 = min::$3 + OFFSET_STRUCT_COLS_BG +Eliminating unused variable min::$2 and assignment [3] min::$2 = min::$4 + OFFSET_STRUCT_COLS_BG +Successful SSA optimization PassNEliminateUnusedVars +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining Noop Cast [0] min::$3 = (char *)min::a keeping min::a +Inlining Noop Cast [1] min::$4 = (char *)min::b keeping min::b +Successful SSA optimization Pass2NopCastInlining +CALL GRAPH +Calls in [main] to min:5 min:10 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] *(&main::a) = memcpy(*(&$0), struct Cols, SIZEOF_STRUCT_COLS) + [1] *(&main::b) = memcpy(*(&$1), struct Cols, SIZEOF_STRUCT_COLS) + [2] *(&main::c) = memcpy(*(&$2), struct Cols, SIZEOF_STRUCT_COLS) + [3] min::a = &main::a + [4] min::b = &main::b + [5] callexecute min + [6] main::m#0 = min::return + [7] *COLS = memcpy(*main::m#0, struct Cols, SIZEOF_STRUCT_COLS) + [8] min::a = main::m#0 + [9] min::b = &main::c + [10] callexecute min + [11] main::m#1 = min::return + [12] *COLS = memcpy(*main::m#1, struct Cols, SIZEOF_STRUCT_COLS) + to:main::@return +main::@return: scope:[main] from main + [13] return + to:@return + +__varcall struct Cols * min(struct Cols *a , struct Cols *b) +min: scope:[min] from + [14] if(((char *)min::a)[OFFSET_STRUCT_COLS_BG]<((char *)min::b)[OFFSET_STRUCT_COLS_BG]) goto min::@1 + to:min::@2 +min::@2: scope:[min] from min + [15] min::return = min::b + to:min::@return +min::@return: scope:[min] from min::@1 min::@2 + [16] return + to:@return +min::@1: scope:[min] from min + [17] min::return = min::a + to:min::@return + + +VARIABLE REGISTER WEIGHTS +void main() +__loadstore volatile struct Cols main::a +__loadstore volatile struct Cols main::b +__loadstore volatile struct Cols main::c +struct Cols *main::m +struct Cols *main::m#0 // 2.0 +struct Cols *main::m#1 // 2.0 +__varcall struct Cols * min(struct Cols *a , struct Cols *b) +__loadstore struct Cols *min::a // 3.0 +__loadstore struct Cols *min::b // 5.0 +__loadstore struct Cols *min::return // 5.2 + +Initial phi equivalence classes +Added variable min::a to live range equivalence class [ min::a ] +Added variable min::b to live range equivalence class [ min::b ] +Added variable main::m#0 to live range equivalence class [ main::m#0 ] +Added variable main::m#1 to live range equivalence class [ main::m#1 ] +Added variable min::return to live range equivalence class [ min::return ] +Added variable main::a to live range equivalence class [ main::a ] +Added variable main::b to live range equivalence class [ main::b ] +Added variable main::c to live range equivalence class [ main::c ] +Complete equivalence classes +[ min::a ] +[ min::b ] +[ main::m#0 ] +[ main::m#1 ] +[ min::return ] +[ main::a ] +[ main::b ] +[ main::c ] +Allocated zp[2]:2 [ min::return ] +Allocated zp[2]:4 [ min::b ] +Allocated zp[2]:6 [ min::a ] +Allocated zp[2]:8 [ main::m#0 ] +Allocated zp[2]:10 [ main::m#1 ] +Allocated zp[2]:12 [ main::a ] +Allocated zp[2]:14 [ main::b ] +Allocated zp[2]:16 [ main::c ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] *(&main::a) = memcpy(*(&$0), struct Cols, SIZEOF_STRUCT_COLS) [ main::b main::c main::a ] ( [ main::b main::c main::a ] { } ) always clobbers reg byte a reg byte y +Statement [1] *(&main::b) = memcpy(*(&$1), struct Cols, SIZEOF_STRUCT_COLS) [ main::b main::c main::a ] ( [ main::b main::c main::a ] { } ) always clobbers reg byte a reg byte y +Statement [2] *(&main::c) = memcpy(*(&$2), struct Cols, SIZEOF_STRUCT_COLS) [ main::b main::c main::a ] ( [ main::b main::c main::a ] { } ) always clobbers reg byte a reg byte y +Statement [3] min::a = &main::a [ main::b main::c min::a ] ( [ main::b main::c min::a ] { } ) always clobbers reg byte a +Statement [4] min::b = &main::b [ main::c min::a min::b ] ( [ main::c min::a min::b ] { } ) always clobbers reg byte a +Statement [6] main::m#0 = min::return [ main::c main::m#0 ] ( [ main::c main::m#0 ] { } ) always clobbers reg byte a +Statement [7] *COLS = memcpy(*main::m#0, struct Cols, SIZEOF_STRUCT_COLS) [ main::c main::m#0 ] ( [ main::c main::m#0 ] { } ) always clobbers reg byte a reg byte x reg byte y +Statement [8] min::a = main::m#0 [ main::c min::a ] ( [ main::c min::a ] { } ) always clobbers reg byte a +Statement [9] min::b = &main::c [ min::a min::b ] ( [ min::a min::b ] { } ) always clobbers reg byte a +Statement [11] main::m#1 = min::return [ main::m#1 ] ( [ main::m#1 ] { } ) always clobbers reg byte a +Statement [12] *COLS = memcpy(*main::m#1, struct Cols, SIZEOF_STRUCT_COLS) [ ] ( [ ] { } ) always clobbers reg byte a reg byte x reg byte y +Statement [14] if(((char *)min::a)[OFFSET_STRUCT_COLS_BG]<((char *)min::b)[OFFSET_STRUCT_COLS_BG]) goto min::@1 [ min::a min::b ] ( min:5 [ main::c min::a min::b ] { { main::m#0 = min::a } } min:10 [ min::a min::b ] { { main::m#0 = min::a } } ) always clobbers reg byte a reg byte y +Statement [15] min::return = min::b [ min::return ] ( min:5 [ main::c min::return ] { { main::m#0 = min::a } } min:10 [ min::return ] { { main::m#0 = min::a } } ) always clobbers reg byte a +Statement [17] min::return = min::a [ min::return ] ( min:5 [ main::c min::return ] { { main::m#0 = min::a } } min:10 [ min::return ] { { main::m#0 = min::a } } ) always clobbers reg byte a +Potential registers zp[2]:6 [ min::a ] : zp[2]:6 , +Potential registers zp[2]:4 [ min::b ] : zp[2]:4 , +Potential registers zp[2]:8 [ main::m#0 ] : zp[2]:8 , +Potential registers zp[2]:10 [ main::m#1 ] : zp[2]:10 , +Potential registers zp[2]:2 [ min::return ] : zp[2]:2 , +Potential registers zp[2]:12 [ main::a ] : zp[2]:12 , +Potential registers zp[2]:14 [ main::b ] : zp[2]:14 , +Potential registers zp[2]:16 [ main::c ] : zp[2]:16 , + +REGISTER UPLIFT SCOPES +Uplift Scope [min] 5.2: zp[2]:2 [ min::return ] 5: zp[2]:4 [ min::b ] 3: zp[2]:6 [ min::a ] +Uplift Scope [main] 2: zp[2]:8 [ main::m#0 ] 2: zp[2]:10 [ main::m#1 ] 0: zp[2]:12 [ main::a ] 0: zp[2]:14 [ main::b ] 0: zp[2]:16 [ main::c ] +Uplift Scope [Cols] +Uplift Scope [] + +Uplifting [min] best 231 combination zp[2]:2 [ min::return ] zp[2]:4 [ min::b ] zp[2]:6 [ min::a ] +Uplifting [main] best 231 combination zp[2]:8 [ main::m#0 ] zp[2]:10 [ main::m#1 ] zp[2]:12 [ main::a ] zp[2]:14 [ main::b ] zp[2]:16 [ main::c ] +Uplifting [Cols] best 231 combination +Uplifting [] best 231 combination +Coalescing zero page register [ zp[2]:6 [ min::a ] ] with [ zp[2]:8 [ main::m#0 ] ] - score: 1 +Coalescing zero page register [ zp[2]:6 [ min::a main::m#0 ] ] with [ zp[2]:2 [ min::return ] ] - score: 1 +Coalescing zero page register [ zp[2]:6 [ min::a main::m#0 min::return ] ] with [ zp[2]:10 [ main::m#1 ] ] - score: 1 +Allocated (was zp[2]:6) zp[2]:2 [ min::a main::m#0 min::return main::m#1 ] +Allocated (was zp[2]:12) zp[2]:6 [ main::a ] +Allocated (was zp[2]:14) zp[2]:8 [ main::b ] +Allocated (was zp[2]:16) zp[2]:10 [ main::c ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test __varcall calling convention +// Pointer to Struct parameter & return value + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-8.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const SIZEOF_STRUCT_COLS = 2 + .const OFFSET_STRUCT_COLS_BG = 1 + .label COLS = $d020 +.segment Code + // main +main: { + .label a = 6 + .label b = 8 + .label c = $a + .label m = 2 + // [0] *(&main::a) = memcpy(*(&$0), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda __0-1,y + sta a-1,y + dey + bne !- + // [1] *(&main::b) = memcpy(*(&$1), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda __1-1,y + sta b-1,y + dey + bne !- + // [2] *(&main::c) = memcpy(*(&$2), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda __2-1,y + sta c-1,y + dey + bne !- + // [3] min::a = &main::a -- pssz1=pssc1 + lda #a + sta.z min.a+1 + // [4] min::b = &main::b -- pssz1=pssc1 + lda #b + sta.z min.b+1 + // [5] callexecute min -- call_vprc1 + jsr min + // [6] main::m#0 = min::return + // [7] *COLS = memcpy(*main::m#0, struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssz1_memcpy_vbuc2 + ldx #SIZEOF_STRUCT_COLS + ldy #0 + !: + lda (m),y + sta COLS,y + iny + dex + bne !- + // [8] min::a = main::m#0 + // [9] min::b = &main::c -- pssz1=pssc1 + lda #c + sta.z min.b+1 + // [10] callexecute min -- call_vprc1 + jsr min + // [11] main::m#1 = min::return + // [12] *COLS = memcpy(*main::m#1, struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssz1_memcpy_vbuc2 + ldx #SIZEOF_STRUCT_COLS + ldy #0 + !: + lda (m),y + sta COLS,y + iny + dex + bne !- + jmp __breturn + // main::@return + __breturn: + // [13] return + rts +} + // min +// __zp(2) struct Cols * min(__zp(2) struct Cols *a, __zp(4) struct Cols *b) +min: { + .label a = 2 + .label b = 4 + .label return = 2 + // [14] if(((char *)min::a)[OFFSET_STRUCT_COLS_BG]<((char *)min::b)[OFFSET_STRUCT_COLS_BG]) goto min::@1 -- pbuz1_derefidx_vbuc1_lt_pbuz2_derefidx_vbuc1_then_la1 + ldy #OFFSET_STRUCT_COLS_BG + lda (b),y + cmp (a),y + bcc __b1 + jmp __b2 + // min::@2 + __b2: + // [15] min::return = min::b -- pssz1=pssz2 + lda.z b + sta.z return + lda.z b+1 + sta.z return+1 + jmp __breturn + // min::@return + __breturn: + // [16] return + rts + // min::@1 + __b1: + // [17] min::return = min::a + jmp __breturn +} + // File Data +.segment Data + __0: .byte 1, 7 + __1: .byte 2, 6 + __2: .byte 3, 5 + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Removing instruction jmp __b2 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Removing instruction __b2: +Succesful ASM optimization Pass5UnusedLabelElimination +Skipping double jump to __breturn in bcc __b1 +Replacing jump to rts with rts in jmp __breturn +Succesful ASM optimization Pass5DoubleJumpElimination +Removing instruction __b1: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing unreachable instruction rts +Succesful ASM optimization Pass5UnreachableCodeElimination + +FINAL SYMBOL TABLE +__constant struct Cols $0 = { border: 1, bg: 7 } +__constant struct Cols $1 = { border: 2, bg: 6 } +__constant struct Cols $2 = { border: 3, bg: 5 } +__constant struct Cols * const COLS = (struct Cols *) 53280 +__constant char OFFSET_STRUCT_COLS_BG = 1 +__constant char SIZEOF_STRUCT_COLS = 2 +void main() +__loadstore volatile struct Cols main::a // zp[2]:6 +__loadstore volatile struct Cols main::b // zp[2]:8 +__loadstore volatile struct Cols main::c // zp[2]:10 +struct Cols *main::m +struct Cols *main::m#0 // m zp[2]:2 2.0 +struct Cols *main::m#1 // m zp[2]:2 2.0 +__varcall struct Cols * min(struct Cols *a , struct Cols *b) +__loadstore struct Cols *min::a // zp[2]:2 3.0 +__loadstore struct Cols *min::b // zp[2]:4 5.0 +__loadstore struct Cols *min::return // zp[2]:2 5.2 + +zp[2]:2 [ min::a main::m#0 min::return main::m#1 ] +zp[2]:4 [ min::b ] +zp[2]:6 [ main::a ] +zp[2]:8 [ main::b ] +zp[2]:10 [ main::c ] + + +FINAL ASSEMBLER +Score: 171 + + // File Comments +// Test __varcall calling convention +// Pointer to Struct parameter & return value + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-8.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .const SIZEOF_STRUCT_COLS = 2 + .const OFFSET_STRUCT_COLS_BG = 1 + .label COLS = $d020 +.segment Code + // main +main: { + .label a = 6 + .label b = 8 + .label c = $a + .label m = 2 + // struct Cols a = { 1, 7 } + // [0] *(&main::a) = memcpy(*(&$0), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda __0-1,y + sta a-1,y + dey + bne !- + // struct Cols b = { 2, 6 } + // [1] *(&main::b) = memcpy(*(&$1), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda __1-1,y + sta b-1,y + dey + bne !- + // struct Cols c = { 3, 5 } + // [2] *(&main::c) = memcpy(*(&$2), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda __2-1,y + sta c-1,y + dey + bne !- + // struct Cols *m = min(&a,&b) + // [3] min::a = &main::a -- pssz1=pssc1 + lda #a + sta.z min.a+1 + // [4] min::b = &main::b -- pssz1=pssc1 + lda #b + sta.z min.b+1 + // [5] callexecute min -- call_vprc1 + jsr min + // [6] main::m#0 = min::return + // *COLS = *m + // [7] *COLS = memcpy(*main::m#0, struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssz1_memcpy_vbuc2 + ldx #SIZEOF_STRUCT_COLS + ldy #0 + !: + lda (m),y + sta COLS,y + iny + dex + bne !- + // min(m,&c) + // [8] min::a = main::m#0 + // [9] min::b = &main::c -- pssz1=pssc1 + lda #c + sta.z min.b+1 + // [10] callexecute min -- call_vprc1 + jsr min + // m = min(m,&c) + // [11] main::m#1 = min::return + // *COLS = *m + // [12] *COLS = memcpy(*main::m#1, struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssz1_memcpy_vbuc2 + ldx #SIZEOF_STRUCT_COLS + ldy #0 + !: + lda (m),y + sta COLS,y + iny + dex + bne !- + // main::@return + // } + // [13] return + rts +} + // min +// __zp(2) struct Cols * min(__zp(2) struct Cols *a, __zp(4) struct Cols *b) +min: { + .label a = 2 + .label b = 4 + .label return = 2 + // if(a->bg < b->bg) + // [14] if(((char *)min::a)[OFFSET_STRUCT_COLS_BG]<((char *)min::b)[OFFSET_STRUCT_COLS_BG]) goto min::@1 -- pbuz1_derefidx_vbuc1_lt_pbuz2_derefidx_vbuc1_then_la1 + ldy #OFFSET_STRUCT_COLS_BG + lda (b),y + cmp (a),y + bcc __breturn + // min::@2 + // return b; + // [15] min::return = min::b -- pssz1=pssz2 + lda.z b + sta.z return + lda.z b+1 + sta.z return+1 + // min::@return + __breturn: + // } + // [16] return + rts + // min::@1 + // return a; + // [17] min::return = min::a +} + // File Data +.segment Data + __0: .byte 1, 7 + __1: .byte 2, 6 + __2: .byte 3, 5 + diff --git a/src/test/ref/varcall-8.sym b/src/test/ref/varcall-8.sym new file mode 100644 index 000000000..63634cbc5 --- /dev/null +++ b/src/test/ref/varcall-8.sym @@ -0,0 +1,23 @@ +__constant struct Cols $0 = { border: 1, bg: 7 } +__constant struct Cols $1 = { border: 2, bg: 6 } +__constant struct Cols $2 = { border: 3, bg: 5 } +__constant struct Cols * const COLS = (struct Cols *) 53280 +__constant char OFFSET_STRUCT_COLS_BG = 1 +__constant char SIZEOF_STRUCT_COLS = 2 +void main() +__loadstore volatile struct Cols main::a // zp[2]:6 +__loadstore volatile struct Cols main::b // zp[2]:8 +__loadstore volatile struct Cols main::c // zp[2]:10 +struct Cols *main::m +struct Cols *main::m#0 // m zp[2]:2 2.0 +struct Cols *main::m#1 // m zp[2]:2 2.0 +__varcall struct Cols * min(struct Cols *a , struct Cols *b) +__loadstore struct Cols *min::a // zp[2]:2 3.0 +__loadstore struct Cols *min::b // zp[2]:4 5.0 +__loadstore struct Cols *min::return // zp[2]:2 5.2 + +zp[2]:2 [ min::a main::m#0 min::return main::m#1 ] +zp[2]:4 [ min::b ] +zp[2]:6 [ main::a ] +zp[2]:8 [ main::b ] +zp[2]:10 [ main::c ] diff --git a/src/test/ref/varcall-9.asm b/src/test/ref/varcall-9.asm new file mode 100644 index 000000000..64356c310 --- /dev/null +++ b/src/test/ref/varcall-9.asm @@ -0,0 +1,108 @@ +// Test __varcall calling convention +// Struct of struct parameter value + // Commodore 64 PRG executable file +.file [name="varcall-9.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + .label COLS = $d020 +.segment Code +main: { + .const a_normal_border = 1 + .const a_normal_bg = 2 + .const a_error_border = 3 + .const a_error_bg = 4 + .const b_normal_border = 5 + .const b_normal_bg = 6 + .const b_error_border = 7 + .const b_error_bg = 8 + .const c_normal_border = 9 + .const c_normal_bg = $a + .const c_error_border = $b + .const c_error_bg = $c + // plus(a, b) + lda #a_normal_border + sta.z plus.a_normal_border + lda #a_normal_bg + sta.z plus.a_normal_bg + lda #a_error_border + sta.z plus.a_error_border + lda #a_error_bg + sta.z plus.a_error_bg + lda #b_normal_border + sta.z plus.b_normal_border + lda #b_normal_bg + sta.z plus.b_normal_bg + lda #b_error_border + sta.z plus.b_error_border + lda #b_error_bg + sta.z plus.b_error_bg + jsr plus + lda.z plus.return + // *COLS = plus(a, b) + sta COLS + // plus(b, c) + lda #b_normal_border + sta.z plus.a_normal_border + lda #b_normal_bg + sta.z plus.a_normal_bg + lda #b_error_border + sta.z plus.a_error_border + lda #b_error_bg + sta.z plus.a_error_bg + lda #c_normal_border + sta.z plus.b_normal_border + lda #c_normal_bg + sta.z plus.b_normal_bg + lda #c_error_border + sta.z plus.b_error_border + lda #c_error_bg + sta.z plus.b_error_bg + jsr plus + lda.z plus.return + // *COLS = plus(b, c) + sta COLS + // } + rts +} +// __zp(2) char plus(__zp($a) char a_normal_border, __zp(7) char a_normal_bg, __zp(8) char a_error_border, __zp(9) char a_error_bg, __zp(3) char b_normal_border, __zp(4) char b_normal_bg, __zp(5) char b_error_border, __zp(6) char b_error_bg) +plus: { + .label return = 2 + .label a_normal_border = $a + .label a_normal_bg = 7 + .label a_error_border = 8 + .label a_error_bg = 9 + .label b_normal_border = 3 + .label b_normal_bg = 4 + .label b_error_border = 5 + .label b_error_bg = 6 + // a.normal.border + b.normal.border + lda.z a_normal_border + clc + adc.z b_normal_border + // a.normal.border + b.normal.border + a.normal.bg + clc + adc.z a_normal_bg + // a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + clc + adc.z b_normal_bg + // a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + a.error.border + clc + adc.z a_error_border + // a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + a.error.border + b.error.border + clc + adc.z b_error_border + // a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + a.error.border + b.error.border + a.error.bg + clc + adc.z a_error_bg + // a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + a.error.border + b.error.border + a.error.bg + b.error.bg + clc + adc.z b_error_bg + // return a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + a.error.border + b.error.border + a.error.bg + b.error.bg; + sta.z return + // } + rts +} diff --git a/src/test/ref/varcall-9.cfg b/src/test/ref/varcall-9.cfg new file mode 100644 index 000000000..e96b47d3d --- /dev/null +++ b/src/test/ref/varcall-9.cfg @@ -0,0 +1,44 @@ + +void main() +main: scope:[main] from + [0] plus::a_normal_border = main::a_normal_border + [1] plus::a_normal_bg = main::a_normal_bg + [2] plus::a_error_border = main::a_error_border + [3] plus::a_error_bg = main::a_error_bg + [4] plus::b_normal_border = main::b_normal_border + [5] plus::b_normal_bg = main::b_normal_bg + [6] plus::b_error_border = main::b_error_border + [7] plus::b_error_bg = main::b_error_bg + [8] callexecute plus + [9] main::$0 = plus::return + [10] *COLS = main::$0 + [11] plus::a_normal_border = main::b_normal_border + [12] plus::a_normal_bg = main::b_normal_bg + [13] plus::a_error_border = main::b_error_border + [14] plus::a_error_bg = main::b_error_bg + [15] plus::b_normal_border = main::c_normal_border + [16] plus::b_normal_bg = main::c_normal_bg + [17] plus::b_error_border = main::c_error_border + [18] plus::b_error_bg = main::c_error_bg + [19] callexecute plus + [20] main::$1 = plus::return + [21] *COLS = main::$1 + to:main::@return +main::@return: scope:[main] from main + [22] return + to:@return + +__varcall char plus(char a_normal_border , char a_normal_bg , char a_error_border , char a_error_bg , char b_normal_border , char b_normal_bg , char b_error_border , char b_error_bg) +plus: scope:[plus] from + [23] plus::$0 = plus::a_normal_border + plus::b_normal_border + [24] plus::$1 = plus::$0 + plus::a_normal_bg + [25] plus::$2 = plus::$1 + plus::b_normal_bg + [26] plus::$3 = plus::$2 + plus::a_error_border + [27] plus::$4 = plus::$3 + plus::b_error_border + [28] plus::$5 = plus::$4 + plus::a_error_bg + [29] plus::$6 = plus::$5 + plus::b_error_bg + [30] plus::return = plus::$6 + to:plus::@return +plus::@return: scope:[plus] from plus + [31] return + to:@return diff --git a/src/test/ref/varcall-9.log b/src/test/ref/varcall-9.log new file mode 100644 index 000000000..d917fb1f3 --- /dev/null +++ b/src/test/ref/varcall-9.log @@ -0,0 +1,711 @@ +Converting parameter in __varcall procedure to load/store plus::a +Converting parameter in __varcall procedure to load/store plus::b +Converting return in __varcall procedure to load/store plus::return +Eliminating unused variable with no statement plus::a +Eliminating unused variable with no statement plus::b +Eliminating unused variable with no statement plus::a_normal +Eliminating unused variable with no statement plus::a_error +Eliminating unused variable with no statement plus::b_normal +Eliminating unused variable with no statement plus::b_error +Calling convention __varcall adding prepare/execute/finalize for main::$0 = call plus(main::a_normal_border, main::a_normal_bg, main::a_error_border, main::a_error_bg, main::b_normal_border, main::b_normal_bg, main::b_error_border, main::b_error_bg) +Calling convention __varcall adding prepare/execute/finalize for main::$1 = call plus(main::b_normal_border, main::b_normal_bg, main::b_error_border, main::b_error_bg, main::c_normal_border, main::c_normal_bg, main::c_error_border, main::c_error_bg) +Calling convention VAR_CALL adding return value assignment main::$0 = plus::return +Calling convention VAR_CALL adding return value assignment main::$1 = plus::return + +CONTROL FLOW GRAPH SSA + +__varcall char plus(char a_normal_border , char a_normal_bg , char a_error_border , char a_error_bg , char b_normal_border , char b_normal_bg , char b_error_border , char b_error_bg) +plus: scope:[plus] from + plus::$0 = plus::a_normal_border + plus::b_normal_border + plus::$1 = plus::$0 + plus::a_normal_bg + plus::$2 = plus::$1 + plus::b_normal_bg + plus::$3 = plus::$2 + plus::a_error_border + plus::$4 = plus::$3 + plus::b_error_border + plus::$5 = plus::$4 + plus::a_error_bg + plus::$6 = plus::$5 + plus::b_error_bg + plus::return = plus::$6 + to:plus::@return +plus::@return: scope:[plus] from plus + return + to:@return + +void main() +main: scope:[main] from __start + plus::a_normal_border = main::a_normal_border + plus::a_normal_bg = main::a_normal_bg + plus::a_error_border = main::a_error_border + plus::a_error_bg = main::a_error_bg + plus::b_normal_border = main::b_normal_border + plus::b_normal_bg = main::b_normal_bg + plus::b_error_border = main::b_error_border + plus::b_error_bg = main::b_error_bg + callexecute plus + main::$0 = plus::return + *COLS = main::$0 + plus::a_normal_border = main::b_normal_border + plus::a_normal_bg = main::b_normal_bg + plus::a_error_border = main::b_error_border + plus::a_error_bg = main::b_error_bg + plus::b_normal_border = main::c_normal_border + plus::b_normal_bg = main::c_normal_bg + plus::b_error_border = main::c_error_border + plus::b_error_bg = main::c_error_bg + callexecute plus + main::$1 = plus::return + *COLS = main::$1 + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +void __start() +__start: scope:[__start] from + call main + to:__start::@1 +__start::@1: scope:[__start] from __start + to:__start::@return +__start::@return: scope:[__start] from __start::@1 + return + to:@return + +SYMBOL TABLE SSA +__constant char * const COLS = (char *)$d020 +void __start() +void main() +char main::$0 +char main::$1 +struct Col main::a_error +__constant char main::a_error_bg = 4 +__constant char main::a_error_border = 3 +struct Col main::a_normal +__constant char main::a_normal_bg = 2 +__constant char main::a_normal_border = 1 +struct Col main::b_error +__constant char main::b_error_bg = 8 +__constant char main::b_error_border = 7 +struct Col main::b_normal +__constant char main::b_normal_bg = 6 +__constant char main::b_normal_border = 5 +struct Col main::c_error +__constant char main::c_error_bg = $c +__constant char main::c_error_border = $b +struct Col main::c_normal +__constant char main::c_normal_bg = $a +__constant char main::c_normal_border = 9 +__varcall char plus(char a_normal_border , char a_normal_bg , char a_error_border , char a_error_bg , char b_normal_border , char b_normal_bg , char b_error_border , char b_error_bg) +char plus::$0 +char plus::$1 +char plus::$2 +char plus::$3 +char plus::$4 +char plus::$5 +char plus::$6 +__loadstore char plus::a_error_bg +__loadstore char plus::a_error_border +__loadstore char plus::a_normal_bg +__loadstore char plus::a_normal_border +__loadstore char plus::b_error_bg +__loadstore char plus::b_error_border +__loadstore char plus::b_normal_bg +__loadstore char plus::b_normal_border +__loadstore char plus::return + +Simplifying constant pointer cast (char *) 53280 +Successful SSA optimization PassNCastSimplification +Alias candidate removed (volatile)plus::return = plus::$6 +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Alias candidate removed (volatile)plus::return = plus::$6 +Alias candidate removed (volatile)plus::return = plus::$6 +Alias candidate removed (volatile)plus::return = plus::$6 +CALL GRAPH +Calls in [main] to plus:8 plus:19 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] plus::a_normal_border = main::a_normal_border + [1] plus::a_normal_bg = main::a_normal_bg + [2] plus::a_error_border = main::a_error_border + [3] plus::a_error_bg = main::a_error_bg + [4] plus::b_normal_border = main::b_normal_border + [5] plus::b_normal_bg = main::b_normal_bg + [6] plus::b_error_border = main::b_error_border + [7] plus::b_error_bg = main::b_error_bg + [8] callexecute plus + [9] main::$0 = plus::return + [10] *COLS = main::$0 + [11] plus::a_normal_border = main::b_normal_border + [12] plus::a_normal_bg = main::b_normal_bg + [13] plus::a_error_border = main::b_error_border + [14] plus::a_error_bg = main::b_error_bg + [15] plus::b_normal_border = main::c_normal_border + [16] plus::b_normal_bg = main::c_normal_bg + [17] plus::b_error_border = main::c_error_border + [18] plus::b_error_bg = main::c_error_bg + [19] callexecute plus + [20] main::$1 = plus::return + [21] *COLS = main::$1 + to:main::@return +main::@return: scope:[main] from main + [22] return + to:@return + +__varcall char plus(char a_normal_border , char a_normal_bg , char a_error_border , char a_error_bg , char b_normal_border , char b_normal_bg , char b_error_border , char b_error_bg) +plus: scope:[plus] from + [23] plus::$0 = plus::a_normal_border + plus::b_normal_border + [24] plus::$1 = plus::$0 + plus::a_normal_bg + [25] plus::$2 = plus::$1 + plus::b_normal_bg + [26] plus::$3 = plus::$2 + plus::a_error_border + [27] plus::$4 = plus::$3 + plus::b_error_border + [28] plus::$5 = plus::$4 + plus::a_error_bg + [29] plus::$6 = plus::$5 + plus::b_error_bg + [30] plus::return = plus::$6 + to:plus::@return +plus::@return: scope:[plus] from plus + [31] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +char main::$0 // 4.0 +char main::$1 // 4.0 +struct Col main::a_error +struct Col main::a_normal +struct Col main::b_error +struct Col main::b_normal +struct Col main::c_error +struct Col main::c_normal +__varcall char plus(char a_normal_border , char a_normal_bg , char a_error_border , char a_error_bg , char b_normal_border , char b_normal_bg , char b_error_border , char b_error_bg) +char plus::$0 // 22.0 +char plus::$1 // 22.0 +char plus::$2 // 22.0 +char plus::$3 // 22.0 +char plus::$4 // 22.0 +char plus::$5 // 22.0 +char plus::$6 // 22.0 +__loadstore char plus::a_error_bg // 1.0 +__loadstore char plus::a_error_border // 1.0 +__loadstore char plus::a_normal_bg // 1.0 +__loadstore char plus::a_normal_border // 0.9375 +__loadstore char plus::b_error_bg // 1.875 +__loadstore char plus::b_error_border // 1.875 +__loadstore char plus::b_normal_bg // 1.875 +__loadstore char plus::b_normal_border // 1.875 +__loadstore char plus::return // 3.75 + +Initial phi equivalence classes +Added variable plus::a_normal_border to live range equivalence class [ plus::a_normal_border ] +Added variable plus::a_normal_bg to live range equivalence class [ plus::a_normal_bg ] +Added variable plus::a_error_border to live range equivalence class [ plus::a_error_border ] +Added variable plus::a_error_bg to live range equivalence class [ plus::a_error_bg ] +Added variable plus::b_normal_border to live range equivalence class [ plus::b_normal_border ] +Added variable plus::b_normal_bg to live range equivalence class [ plus::b_normal_bg ] +Added variable plus::b_error_border to live range equivalence class [ plus::b_error_border ] +Added variable plus::b_error_bg to live range equivalence class [ plus::b_error_bg ] +Added variable main::$0 to live range equivalence class [ main::$0 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable plus::$0 to live range equivalence class [ plus::$0 ] +Added variable plus::$1 to live range equivalence class [ plus::$1 ] +Added variable plus::$2 to live range equivalence class [ plus::$2 ] +Added variable plus::$3 to live range equivalence class [ plus::$3 ] +Added variable plus::$4 to live range equivalence class [ plus::$4 ] +Added variable plus::$5 to live range equivalence class [ plus::$5 ] +Added variable plus::$6 to live range equivalence class [ plus::$6 ] +Added variable plus::return to live range equivalence class [ plus::return ] +Complete equivalence classes +[ plus::a_normal_border ] +[ plus::a_normal_bg ] +[ plus::a_error_border ] +[ plus::a_error_bg ] +[ plus::b_normal_border ] +[ plus::b_normal_bg ] +[ plus::b_error_border ] +[ plus::b_error_bg ] +[ main::$0 ] +[ main::$1 ] +[ plus::$0 ] +[ plus::$1 ] +[ plus::$2 ] +[ plus::$3 ] +[ plus::$4 ] +[ plus::$5 ] +[ plus::$6 ] +[ plus::return ] +Allocated zp[1]:2 [ plus::$0 ] +Allocated zp[1]:3 [ plus::$1 ] +Allocated zp[1]:4 [ plus::$2 ] +Allocated zp[1]:5 [ plus::$3 ] +Allocated zp[1]:6 [ plus::$4 ] +Allocated zp[1]:7 [ plus::$5 ] +Allocated zp[1]:8 [ plus::$6 ] +Allocated zp[1]:9 [ main::$0 ] +Allocated zp[1]:10 [ main::$1 ] +Allocated zp[1]:11 [ plus::return ] +Allocated zp[1]:12 [ plus::b_normal_border ] +Allocated zp[1]:13 [ plus::b_normal_bg ] +Allocated zp[1]:14 [ plus::b_error_border ] +Allocated zp[1]:15 [ plus::b_error_bg ] +Allocated zp[1]:16 [ plus::a_normal_bg ] +Allocated zp[1]:17 [ plus::a_error_border ] +Allocated zp[1]:18 [ plus::a_error_bg ] +Allocated zp[1]:19 [ plus::a_normal_border ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] plus::a_normal_border = main::a_normal_border [ plus::a_normal_border ] ( [ plus::a_normal_border ] { } ) always clobbers reg byte a +Statement [1] plus::a_normal_bg = main::a_normal_bg [ plus::a_normal_border plus::a_normal_bg ] ( [ plus::a_normal_border plus::a_normal_bg ] { } ) always clobbers reg byte a +Statement [2] plus::a_error_border = main::a_error_border [ plus::a_normal_border plus::a_normal_bg plus::a_error_border ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border ] { } ) always clobbers reg byte a +Statement [3] plus::a_error_bg = main::a_error_bg [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg ] { } ) always clobbers reg byte a +Statement [4] plus::b_normal_border = main::b_normal_border [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border ] { } ) always clobbers reg byte a +Statement [5] plus::b_normal_bg = main::b_normal_bg [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg ] { } ) always clobbers reg byte a +Statement [6] plus::b_error_border = main::b_error_border [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg plus::b_error_border ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg plus::b_error_border ] { } ) always clobbers reg byte a +Statement [7] plus::b_error_bg = main::b_error_bg [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg plus::b_error_border plus::b_error_bg ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg plus::b_error_border plus::b_error_bg ] { } ) always clobbers reg byte a +Statement [11] plus::a_normal_border = main::b_normal_border [ plus::a_normal_border ] ( [ plus::a_normal_border ] { } ) always clobbers reg byte a +Statement [12] plus::a_normal_bg = main::b_normal_bg [ plus::a_normal_border plus::a_normal_bg ] ( [ plus::a_normal_border plus::a_normal_bg ] { } ) always clobbers reg byte a +Statement [13] plus::a_error_border = main::b_error_border [ plus::a_normal_border plus::a_normal_bg plus::a_error_border ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border ] { } ) always clobbers reg byte a +Statement [14] plus::a_error_bg = main::b_error_bg [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg ] { } ) always clobbers reg byte a +Statement [15] plus::b_normal_border = main::c_normal_border [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border ] { } ) always clobbers reg byte a +Statement [16] plus::b_normal_bg = main::c_normal_bg [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg ] { } ) always clobbers reg byte a +Statement [17] plus::b_error_border = main::c_error_border [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg plus::b_error_border ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg plus::b_error_border ] { } ) always clobbers reg byte a +Statement [18] plus::b_error_bg = main::c_error_bg [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg plus::b_error_border plus::b_error_bg ] ( [ plus::a_normal_border plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_border plus::b_normal_bg plus::b_error_border plus::b_error_bg ] { } ) always clobbers reg byte a +Statement [23] plus::$0 = plus::a_normal_border + plus::b_normal_border [ plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_bg plus::b_error_border plus::b_error_bg plus::$0 ] ( plus:8 [ plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_bg plus::b_error_border plus::b_error_bg plus::$0 ] { } plus:19 [ plus::a_normal_bg plus::a_error_border plus::a_error_bg plus::b_normal_bg plus::b_error_border plus::b_error_bg plus::$0 ] { } ) always clobbers reg byte a +Statement [24] plus::$1 = plus::$0 + plus::a_normal_bg [ plus::a_error_border plus::a_error_bg plus::b_normal_bg plus::b_error_border plus::b_error_bg plus::$1 ] ( plus:8 [ plus::a_error_border plus::a_error_bg plus::b_normal_bg plus::b_error_border plus::b_error_bg plus::$1 ] { } plus:19 [ plus::a_error_border plus::a_error_bg plus::b_normal_bg plus::b_error_border plus::b_error_bg plus::$1 ] { } ) always clobbers reg byte a +Statement [25] plus::$2 = plus::$1 + plus::b_normal_bg [ plus::a_error_border plus::a_error_bg plus::b_error_border plus::b_error_bg plus::$2 ] ( plus:8 [ plus::a_error_border plus::a_error_bg plus::b_error_border plus::b_error_bg plus::$2 ] { } plus:19 [ plus::a_error_border plus::a_error_bg plus::b_error_border plus::b_error_bg plus::$2 ] { } ) always clobbers reg byte a +Statement [26] plus::$3 = plus::$2 + plus::a_error_border [ plus::a_error_bg plus::b_error_border plus::b_error_bg plus::$3 ] ( plus:8 [ plus::a_error_bg plus::b_error_border plus::b_error_bg plus::$3 ] { } plus:19 [ plus::a_error_bg plus::b_error_border plus::b_error_bg plus::$3 ] { } ) always clobbers reg byte a +Statement [27] plus::$4 = plus::$3 + plus::b_error_border [ plus::a_error_bg plus::b_error_bg plus::$4 ] ( plus:8 [ plus::a_error_bg plus::b_error_bg plus::$4 ] { } plus:19 [ plus::a_error_bg plus::b_error_bg plus::$4 ] { } ) always clobbers reg byte a +Statement [28] plus::$5 = plus::$4 + plus::a_error_bg [ plus::b_error_bg plus::$5 ] ( plus:8 [ plus::b_error_bg plus::$5 ] { } plus:19 [ plus::b_error_bg plus::$5 ] { } ) always clobbers reg byte a +Statement [29] plus::$6 = plus::$5 + plus::b_error_bg [ plus::$6 ] ( plus:8 [ plus::$6 ] { } plus:19 [ plus::$6 ] { } ) always clobbers reg byte a +Potential registers zp[1]:19 [ plus::a_normal_border ] : zp[1]:19 , +Potential registers zp[1]:16 [ plus::a_normal_bg ] : zp[1]:16 , +Potential registers zp[1]:17 [ plus::a_error_border ] : zp[1]:17 , +Potential registers zp[1]:18 [ plus::a_error_bg ] : zp[1]:18 , +Potential registers zp[1]:12 [ plus::b_normal_border ] : zp[1]:12 , +Potential registers zp[1]:13 [ plus::b_normal_bg ] : zp[1]:13 , +Potential registers zp[1]:14 [ plus::b_error_border ] : zp[1]:14 , +Potential registers zp[1]:15 [ plus::b_error_bg ] : zp[1]:15 , +Potential registers zp[1]:9 [ main::$0 ] : zp[1]:9 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:10 [ main::$1 ] : zp[1]:10 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:2 [ plus::$0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ plus::$1 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:4 [ plus::$2 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ plus::$3 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:6 [ plus::$4 ] : zp[1]:6 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:7 [ plus::$5 ] : zp[1]:7 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:8 [ plus::$6 ] : zp[1]:8 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:11 [ plus::return ] : zp[1]:11 , + +REGISTER UPLIFT SCOPES +Uplift Scope [plus] 22: zp[1]:2 [ plus::$0 ] 22: zp[1]:3 [ plus::$1 ] 22: zp[1]:4 [ plus::$2 ] 22: zp[1]:5 [ plus::$3 ] 22: zp[1]:6 [ plus::$4 ] 22: zp[1]:7 [ plus::$5 ] 22: zp[1]:8 [ plus::$6 ] 3.75: zp[1]:11 [ plus::return ] 1.88: zp[1]:12 [ plus::b_normal_border ] 1.88: zp[1]:13 [ plus::b_normal_bg ] 1.88: zp[1]:14 [ plus::b_error_border ] 1.88: zp[1]:15 [ plus::b_error_bg ] 1: zp[1]:16 [ plus::a_normal_bg ] 1: zp[1]:17 [ plus::a_error_border ] 1: zp[1]:18 [ plus::a_error_bg ] 0.94: zp[1]:19 [ plus::a_normal_border ] +Uplift Scope [main] 4: zp[1]:9 [ main::$0 ] 4: zp[1]:10 [ main::$1 ] +Uplift Scope [Col] +Uplift Scope [Cols] +Uplift Scope [] + +Uplifting [plus] best 195 combination reg byte a [ plus::$0 ] reg byte a [ plus::$1 ] reg byte a [ plus::$2 ] reg byte a [ plus::$3 ] zp[1]:6 [ plus::$4 ] zp[1]:7 [ plus::$5 ] zp[1]:8 [ plus::$6 ] zp[1]:11 [ plus::return ] zp[1]:12 [ plus::b_normal_border ] zp[1]:13 [ plus::b_normal_bg ] zp[1]:14 [ plus::b_error_border ] zp[1]:15 [ plus::b_error_bg ] zp[1]:16 [ plus::a_normal_bg ] zp[1]:17 [ plus::a_error_border ] zp[1]:18 [ plus::a_error_bg ] zp[1]:19 [ plus::a_normal_border ] +Limited combination testing to 100 combinations of 16384 possible. +Uplifting [main] best 183 combination reg byte a [ main::$0 ] reg byte a [ main::$1 ] +Uplifting [Col] best 183 combination +Uplifting [Cols] best 183 combination +Uplifting [] best 183 combination +Attempting to uplift remaining variables inzp[1]:6 [ plus::$4 ] +Uplifting [plus] best 177 combination reg byte a [ plus::$4 ] +Attempting to uplift remaining variables inzp[1]:7 [ plus::$5 ] +Uplifting [plus] best 171 combination reg byte a [ plus::$5 ] +Attempting to uplift remaining variables inzp[1]:8 [ plus::$6 ] +Uplifting [plus] best 165 combination reg byte a [ plus::$6 ] +Attempting to uplift remaining variables inzp[1]:11 [ plus::return ] +Uplifting [plus] best 165 combination zp[1]:11 [ plus::return ] +Attempting to uplift remaining variables inzp[1]:12 [ plus::b_normal_border ] +Uplifting [plus] best 165 combination zp[1]:12 [ plus::b_normal_border ] +Attempting to uplift remaining variables inzp[1]:13 [ plus::b_normal_bg ] +Uplifting [plus] best 165 combination zp[1]:13 [ plus::b_normal_bg ] +Attempting to uplift remaining variables inzp[1]:14 [ plus::b_error_border ] +Uplifting [plus] best 165 combination zp[1]:14 [ plus::b_error_border ] +Attempting to uplift remaining variables inzp[1]:15 [ plus::b_error_bg ] +Uplifting [plus] best 165 combination zp[1]:15 [ plus::b_error_bg ] +Attempting to uplift remaining variables inzp[1]:16 [ plus::a_normal_bg ] +Uplifting [plus] best 165 combination zp[1]:16 [ plus::a_normal_bg ] +Attempting to uplift remaining variables inzp[1]:17 [ plus::a_error_border ] +Uplifting [plus] best 165 combination zp[1]:17 [ plus::a_error_border ] +Attempting to uplift remaining variables inzp[1]:18 [ plus::a_error_bg ] +Uplifting [plus] best 165 combination zp[1]:18 [ plus::a_error_bg ] +Attempting to uplift remaining variables inzp[1]:19 [ plus::a_normal_border ] +Uplifting [plus] best 165 combination zp[1]:19 [ plus::a_normal_border ] +Allocated (was zp[1]:11) zp[1]:2 [ plus::return ] +Allocated (was zp[1]:12) zp[1]:3 [ plus::b_normal_border ] +Allocated (was zp[1]:13) zp[1]:4 [ plus::b_normal_bg ] +Allocated (was zp[1]:14) zp[1]:5 [ plus::b_error_border ] +Allocated (was zp[1]:15) zp[1]:6 [ plus::b_error_bg ] +Allocated (was zp[1]:16) zp[1]:7 [ plus::a_normal_bg ] +Allocated (was zp[1]:17) zp[1]:8 [ plus::a_error_border ] +Allocated (was zp[1]:18) zp[1]:9 [ plus::a_error_bg ] +Allocated (was zp[1]:19) zp[1]:10 [ plus::a_normal_border ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test __varcall calling convention +// Struct of struct parameter value + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-9.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label COLS = $d020 +.segment Code + // main +main: { + .const a_normal_border = 1 + .const a_normal_bg = 2 + .const a_error_border = 3 + .const a_error_bg = 4 + .const b_normal_border = 5 + .const b_normal_bg = 6 + .const b_error_border = 7 + .const b_error_bg = 8 + .const c_normal_border = 9 + .const c_normal_bg = $a + .const c_error_border = $b + .const c_error_bg = $c + // [0] plus::a_normal_border = main::a_normal_border -- vbuz1=vbuc1 + lda #a_normal_border + sta.z plus.a_normal_border + // [1] plus::a_normal_bg = main::a_normal_bg -- vbuz1=vbuc1 + lda #a_normal_bg + sta.z plus.a_normal_bg + // [2] plus::a_error_border = main::a_error_border -- vbuz1=vbuc1 + lda #a_error_border + sta.z plus.a_error_border + // [3] plus::a_error_bg = main::a_error_bg -- vbuz1=vbuc1 + lda #a_error_bg + sta.z plus.a_error_bg + // [4] plus::b_normal_border = main::b_normal_border -- vbuz1=vbuc1 + lda #b_normal_border + sta.z plus.b_normal_border + // [5] plus::b_normal_bg = main::b_normal_bg -- vbuz1=vbuc1 + lda #b_normal_bg + sta.z plus.b_normal_bg + // [6] plus::b_error_border = main::b_error_border -- vbuz1=vbuc1 + lda #b_error_border + sta.z plus.b_error_border + // [7] plus::b_error_bg = main::b_error_bg -- vbuz1=vbuc1 + lda #b_error_bg + sta.z plus.b_error_bg + // [8] callexecute plus -- call_vprc1 + jsr plus + // [9] main::$0 = plus::return -- vbuaa=vbuz1 + lda.z plus.return + // [10] *COLS = main::$0 -- _deref_pbuc1=vbuaa + sta COLS + // [11] plus::a_normal_border = main::b_normal_border -- vbuz1=vbuc1 + lda #b_normal_border + sta.z plus.a_normal_border + // [12] plus::a_normal_bg = main::b_normal_bg -- vbuz1=vbuc1 + lda #b_normal_bg + sta.z plus.a_normal_bg + // [13] plus::a_error_border = main::b_error_border -- vbuz1=vbuc1 + lda #b_error_border + sta.z plus.a_error_border + // [14] plus::a_error_bg = main::b_error_bg -- vbuz1=vbuc1 + lda #b_error_bg + sta.z plus.a_error_bg + // [15] plus::b_normal_border = main::c_normal_border -- vbuz1=vbuc1 + lda #c_normal_border + sta.z plus.b_normal_border + // [16] plus::b_normal_bg = main::c_normal_bg -- vbuz1=vbuc1 + lda #c_normal_bg + sta.z plus.b_normal_bg + // [17] plus::b_error_border = main::c_error_border -- vbuz1=vbuc1 + lda #c_error_border + sta.z plus.b_error_border + // [18] plus::b_error_bg = main::c_error_bg -- vbuz1=vbuc1 + lda #c_error_bg + sta.z plus.b_error_bg + // [19] callexecute plus -- call_vprc1 + jsr plus + // [20] main::$1 = plus::return -- vbuaa=vbuz1 + lda.z plus.return + // [21] *COLS = main::$1 -- _deref_pbuc1=vbuaa + sta COLS + jmp __breturn + // main::@return + __breturn: + // [22] return + rts +} + // plus +// __zp(2) char plus(__zp($a) char a_normal_border, __zp(7) char a_normal_bg, __zp(8) char a_error_border, __zp(9) char a_error_bg, __zp(3) char b_normal_border, __zp(4) char b_normal_bg, __zp(5) char b_error_border, __zp(6) char b_error_bg) +plus: { + .label return = 2 + .label a_normal_border = $a + .label a_normal_bg = 7 + .label a_error_border = 8 + .label a_error_bg = 9 + .label b_normal_border = 3 + .label b_normal_bg = 4 + .label b_error_border = 5 + .label b_error_bg = 6 + // [23] plus::$0 = plus::a_normal_border + plus::b_normal_border -- vbuaa=vbuz1_plus_vbuz2 + lda.z a_normal_border + clc + adc.z b_normal_border + // [24] plus::$1 = plus::$0 + plus::a_normal_bg -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z a_normal_bg + // [25] plus::$2 = plus::$1 + plus::b_normal_bg -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z b_normal_bg + // [26] plus::$3 = plus::$2 + plus::a_error_border -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z a_error_border + // [27] plus::$4 = plus::$3 + plus::b_error_border -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z b_error_border + // [28] plus::$5 = plus::$4 + plus::a_error_bg -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z a_error_bg + // [29] plus::$6 = plus::$5 + plus::b_error_bg -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z b_error_bg + // [30] plus::return = plus::$6 -- vbuz1=vbuaa + sta.z return + jmp __breturn + // plus::@return + __breturn: + // [31] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +__constant char * const COLS = (char *) 53280 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +struct Col main::a_error +__constant char main::a_error_bg = 4 +__constant char main::a_error_border = 3 +struct Col main::a_normal +__constant char main::a_normal_bg = 2 +__constant char main::a_normal_border = 1 +struct Col main::b_error +__constant char main::b_error_bg = 8 +__constant char main::b_error_border = 7 +struct Col main::b_normal +__constant char main::b_normal_bg = 6 +__constant char main::b_normal_border = 5 +struct Col main::c_error +__constant char main::c_error_bg = $c +__constant char main::c_error_border = $b +struct Col main::c_normal +__constant char main::c_normal_bg = $a +__constant char main::c_normal_border = 9 +__varcall char plus(char a_normal_border , char a_normal_bg , char a_error_border , char a_error_bg , char b_normal_border , char b_normal_bg , char b_error_border , char b_error_bg) +char plus::$0 // reg byte a 22.0 +char plus::$1 // reg byte a 22.0 +char plus::$2 // reg byte a 22.0 +char plus::$3 // reg byte a 22.0 +char plus::$4 // reg byte a 22.0 +char plus::$5 // reg byte a 22.0 +char plus::$6 // reg byte a 22.0 +__loadstore char plus::a_error_bg // zp[1]:9 1.0 +__loadstore char plus::a_error_border // zp[1]:8 1.0 +__loadstore char plus::a_normal_bg // zp[1]:7 1.0 +__loadstore char plus::a_normal_border // zp[1]:10 0.9375 +__loadstore char plus::b_error_bg // zp[1]:6 1.875 +__loadstore char plus::b_error_border // zp[1]:5 1.875 +__loadstore char plus::b_normal_bg // zp[1]:4 1.875 +__loadstore char plus::b_normal_border // zp[1]:3 1.875 +__loadstore char plus::return // zp[1]:2 3.75 + +zp[1]:10 [ plus::a_normal_border ] +zp[1]:7 [ plus::a_normal_bg ] +zp[1]:8 [ plus::a_error_border ] +zp[1]:9 [ plus::a_error_bg ] +zp[1]:3 [ plus::b_normal_border ] +zp[1]:4 [ plus::b_normal_bg ] +zp[1]:5 [ plus::b_error_border ] +zp[1]:6 [ plus::b_error_bg ] +reg byte a [ main::$0 ] +reg byte a [ main::$1 ] +reg byte a [ plus::$0 ] +reg byte a [ plus::$1 ] +reg byte a [ plus::$2 ] +reg byte a [ plus::$3 ] +reg byte a [ plus::$4 ] +reg byte a [ plus::$5 ] +reg byte a [ plus::$6 ] +zp[1]:2 [ plus::return ] + + +FINAL ASSEMBLER +Score: 159 + + // File Comments +// Test __varcall calling convention +// Struct of struct parameter value + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-9.prg", type="prg", segments="Program"] +.segmentdef Program [segments="Basic, Code, Data"] +.segmentdef Basic [start=$0801] +.segmentdef Code [start=$80d] +.segmentdef Data [startAfter="Code"] +.segment Basic +:BasicUpstart(main) + // Global Constants & labels + .label COLS = $d020 +.segment Code + // main +main: { + .const a_normal_border = 1 + .const a_normal_bg = 2 + .const a_error_border = 3 + .const a_error_bg = 4 + .const b_normal_border = 5 + .const b_normal_bg = 6 + .const b_error_border = 7 + .const b_error_bg = 8 + .const c_normal_border = 9 + .const c_normal_bg = $a + .const c_error_border = $b + .const c_error_bg = $c + // plus(a, b) + // [0] plus::a_normal_border = main::a_normal_border -- vbuz1=vbuc1 + lda #a_normal_border + sta.z plus.a_normal_border + // [1] plus::a_normal_bg = main::a_normal_bg -- vbuz1=vbuc1 + lda #a_normal_bg + sta.z plus.a_normal_bg + // [2] plus::a_error_border = main::a_error_border -- vbuz1=vbuc1 + lda #a_error_border + sta.z plus.a_error_border + // [3] plus::a_error_bg = main::a_error_bg -- vbuz1=vbuc1 + lda #a_error_bg + sta.z plus.a_error_bg + // [4] plus::b_normal_border = main::b_normal_border -- vbuz1=vbuc1 + lda #b_normal_border + sta.z plus.b_normal_border + // [5] plus::b_normal_bg = main::b_normal_bg -- vbuz1=vbuc1 + lda #b_normal_bg + sta.z plus.b_normal_bg + // [6] plus::b_error_border = main::b_error_border -- vbuz1=vbuc1 + lda #b_error_border + sta.z plus.b_error_border + // [7] plus::b_error_bg = main::b_error_bg -- vbuz1=vbuc1 + lda #b_error_bg + sta.z plus.b_error_bg + // [8] callexecute plus -- call_vprc1 + jsr plus + // [9] main::$0 = plus::return -- vbuaa=vbuz1 + lda.z plus.return + // *COLS = plus(a, b) + // [10] *COLS = main::$0 -- _deref_pbuc1=vbuaa + sta COLS + // plus(b, c) + // [11] plus::a_normal_border = main::b_normal_border -- vbuz1=vbuc1 + lda #b_normal_border + sta.z plus.a_normal_border + // [12] plus::a_normal_bg = main::b_normal_bg -- vbuz1=vbuc1 + lda #b_normal_bg + sta.z plus.a_normal_bg + // [13] plus::a_error_border = main::b_error_border -- vbuz1=vbuc1 + lda #b_error_border + sta.z plus.a_error_border + // [14] plus::a_error_bg = main::b_error_bg -- vbuz1=vbuc1 + lda #b_error_bg + sta.z plus.a_error_bg + // [15] plus::b_normal_border = main::c_normal_border -- vbuz1=vbuc1 + lda #c_normal_border + sta.z plus.b_normal_border + // [16] plus::b_normal_bg = main::c_normal_bg -- vbuz1=vbuc1 + lda #c_normal_bg + sta.z plus.b_normal_bg + // [17] plus::b_error_border = main::c_error_border -- vbuz1=vbuc1 + lda #c_error_border + sta.z plus.b_error_border + // [18] plus::b_error_bg = main::c_error_bg -- vbuz1=vbuc1 + lda #c_error_bg + sta.z plus.b_error_bg + // [19] callexecute plus -- call_vprc1 + jsr plus + // [20] main::$1 = plus::return -- vbuaa=vbuz1 + lda.z plus.return + // *COLS = plus(b, c) + // [21] *COLS = main::$1 -- _deref_pbuc1=vbuaa + sta COLS + // main::@return + // } + // [22] return + rts +} + // plus +// __zp(2) char plus(__zp($a) char a_normal_border, __zp(7) char a_normal_bg, __zp(8) char a_error_border, __zp(9) char a_error_bg, __zp(3) char b_normal_border, __zp(4) char b_normal_bg, __zp(5) char b_error_border, __zp(6) char b_error_bg) +plus: { + .label return = 2 + .label a_normal_border = $a + .label a_normal_bg = 7 + .label a_error_border = 8 + .label a_error_bg = 9 + .label b_normal_border = 3 + .label b_normal_bg = 4 + .label b_error_border = 5 + .label b_error_bg = 6 + // a.normal.border + b.normal.border + // [23] plus::$0 = plus::a_normal_border + plus::b_normal_border -- vbuaa=vbuz1_plus_vbuz2 + lda.z a_normal_border + clc + adc.z b_normal_border + // a.normal.border + b.normal.border + a.normal.bg + // [24] plus::$1 = plus::$0 + plus::a_normal_bg -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z a_normal_bg + // a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + // [25] plus::$2 = plus::$1 + plus::b_normal_bg -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z b_normal_bg + // a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + a.error.border + // [26] plus::$3 = plus::$2 + plus::a_error_border -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z a_error_border + // a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + a.error.border + b.error.border + // [27] plus::$4 = plus::$3 + plus::b_error_border -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z b_error_border + // a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + a.error.border + b.error.border + a.error.bg + // [28] plus::$5 = plus::$4 + plus::a_error_bg -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z a_error_bg + // a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + a.error.border + b.error.border + a.error.bg + b.error.bg + // [29] plus::$6 = plus::$5 + plus::b_error_bg -- vbuaa=vbuaa_plus_vbuz1 + clc + adc.z b_error_bg + // return a.normal.border + b.normal.border + a.normal.bg + b.normal.bg + a.error.border + b.error.border + a.error.bg + b.error.bg; + // [30] plus::return = plus::$6 -- vbuz1=vbuaa + sta.z return + // plus::@return + // } + // [31] return + rts +} + // File Data + diff --git a/src/test/ref/varcall-9.sym b/src/test/ref/varcall-9.sym new file mode 100644 index 000000000..a2c916dc9 --- /dev/null +++ b/src/test/ref/varcall-9.sym @@ -0,0 +1,58 @@ +__constant char * const COLS = (char *) 53280 +void main() +char main::$0 // reg byte a 4.0 +char main::$1 // reg byte a 4.0 +struct Col main::a_error +__constant char main::a_error_bg = 4 +__constant char main::a_error_border = 3 +struct Col main::a_normal +__constant char main::a_normal_bg = 2 +__constant char main::a_normal_border = 1 +struct Col main::b_error +__constant char main::b_error_bg = 8 +__constant char main::b_error_border = 7 +struct Col main::b_normal +__constant char main::b_normal_bg = 6 +__constant char main::b_normal_border = 5 +struct Col main::c_error +__constant char main::c_error_bg = $c +__constant char main::c_error_border = $b +struct Col main::c_normal +__constant char main::c_normal_bg = $a +__constant char main::c_normal_border = 9 +__varcall char plus(char a_normal_border , char a_normal_bg , char a_error_border , char a_error_bg , char b_normal_border , char b_normal_bg , char b_error_border , char b_error_bg) +char plus::$0 // reg byte a 22.0 +char plus::$1 // reg byte a 22.0 +char plus::$2 // reg byte a 22.0 +char plus::$3 // reg byte a 22.0 +char plus::$4 // reg byte a 22.0 +char plus::$5 // reg byte a 22.0 +char plus::$6 // reg byte a 22.0 +__loadstore char plus::a_error_bg // zp[1]:9 1.0 +__loadstore char plus::a_error_border // zp[1]:8 1.0 +__loadstore char plus::a_normal_bg // zp[1]:7 1.0 +__loadstore char plus::a_normal_border // zp[1]:10 0.9375 +__loadstore char plus::b_error_bg // zp[1]:6 1.875 +__loadstore char plus::b_error_border // zp[1]:5 1.875 +__loadstore char plus::b_normal_bg // zp[1]:4 1.875 +__loadstore char plus::b_normal_border // zp[1]:3 1.875 +__loadstore char plus::return // zp[1]:2 3.75 + +zp[1]:10 [ plus::a_normal_border ] +zp[1]:7 [ plus::a_normal_bg ] +zp[1]:8 [ plus::a_error_border ] +zp[1]:9 [ plus::a_error_bg ] +zp[1]:3 [ plus::b_normal_border ] +zp[1]:4 [ plus::b_normal_bg ] +zp[1]:5 [ plus::b_error_border ] +zp[1]:6 [ plus::b_error_bg ] +reg byte a [ main::$0 ] +reg byte a [ main::$1 ] +reg byte a [ plus::$0 ] +reg byte a [ plus::$1 ] +reg byte a [ plus::$2 ] +reg byte a [ plus::$3 ] +reg byte a [ plus::$4 ] +reg byte a [ plus::$5 ] +reg byte a [ plus::$6 ] +zp[1]:2 [ plus::return ]