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 ]