From 29633a247967e97429c791d85ccccc8c052327b1 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Sat, 24 Jul 2021 11:33:07 +0200 Subject: [PATCH] Working on varcall calling convention, structs and unions. #197 #372 --- .../java/dk/camelot64/kickc/Compiler.java | 3 + .../camelot64/kickc/model/Initializers.java | 25 +- .../kickc/model/symbols/Variable.java | 20 +- .../kickc/passes/Pass1UnwindStructValues.java | 48 +- ...s4LiveRangeEquivalenceClassesFinalize.java | 3 + .../kickc/test/TestProgramsFast.java | 17 +- src/test/kc/union-7.c | 23 + src/test/kc/varcall-4.c | 25 +- src/test/kc/varcall-5.c | 22 + src/test/kc/varcall-6.c | 22 + src/test/ref/varcall-4.asm | 80 +++ src/test/ref/varcall-4.cfg | 27 ++ src/test/ref/varcall-4.log | 458 ++++++++++++++++++ src/test/ref/varcall-4.sym | 28 ++ 14 files changed, 750 insertions(+), 51 deletions(-) create mode 100644 src/test/kc/union-7.c create mode 100644 src/test/kc/varcall-5.c create mode 100644 src/test/kc/varcall-6.c create mode 100644 src/test/ref/varcall-4.asm create mode 100644 src/test/ref/varcall-4.cfg create mode 100644 src/test/ref/varcall-4.log create mode 100644 src/test/ref/varcall-4.sym diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index ddf2aca05..11e967b72 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -312,6 +312,9 @@ public class Compiler { new Pass1CallPhiReturn(program).execute(); new PassNUnwindLValueLists(program).execute(); + new Pass1UnwindStructValues(program).execute(); + + getLog().append("\nCONTROL FLOW GRAPH SSA"); getLog().append(program.getGraph().toString(program)); diff --git a/src/main/java/dk/camelot64/kickc/model/Initializers.java b/src/main/java/dk/camelot64/kickc/model/Initializers.java index 6281b9360..7c386a519 100644 --- a/src/main/java/dk/camelot64/kickc/model/Initializers.java +++ b/src/main/java/dk/camelot64/kickc/model/Initializers.java @@ -1,6 +1,5 @@ package dk.camelot64.kickc.model; -import dk.camelot64.kickc.model.operators.Operators; import dk.camelot64.kickc.model.statements.StatementSource; import dk.camelot64.kickc.model.symbols.ArraySpec; import dk.camelot64.kickc.model.symbols.StructDefinition; @@ -92,7 +91,7 @@ public class Initializers { } } else if(initValue instanceof ValueList) { ValueList initList = (ValueList) initValue; - if(typeSpec.getType() instanceof SymbolTypePointer && ((SymbolTypePointer)typeSpec.getType()).getArraySpec() != null) { + if(typeSpec.getType() instanceof SymbolTypePointer && ((SymbolTypePointer) typeSpec.getType()).getArraySpec() != null) { // Type is an array initValue = constantifyArray(initList, (SymbolTypePointer) typeSpec.getType(), program, source); } else if(typeSpec.getType() instanceof SymbolTypeStruct) { @@ -107,7 +106,7 @@ public class Initializers { if(typeSpec.getType() instanceof SymbolTypeIntegerFixed) { SymbolTypeIntegerFixed typeIntegerFixed = (SymbolTypeIntegerFixed) typeSpec.getType(); if(!typeIntegerFixed.contains(integer)) { - throw new CompileError( "Constant init-value has a non-matching type \n type: " + typeSpec.getType().toString() +"\n value: " + initValue.toString(), source); + throw new CompileError("Constant init-value has a non-matching type \n type: " + typeSpec.getType().toString() + "\n value: " + initValue.toString(), source); } } initValue = new ConstantInteger(integer, typeSpec.getType()); @@ -141,13 +140,21 @@ public class Initializers { // Recursively cast all sub-elements StructDefinition structDefinition = structType.getStructDefinition(program.getScope()); Collection memberDefinitions = structDefinition.getAllVars(false); - int structInitNeedSize = structDefinition.isUnion()? 1 : memberDefinitions.size() ; + int structInitNeedSize = structDefinition.isUnion() ? 1 : memberDefinitions.size(); if(structInitNeedSize != valueList.getList().size()) { - throw new CompileError( - "Struct initializer has wrong size (" + valueList.getList().size() + "), " + - "which does not match the number of members in " + structType.getTypeName() + " (" + structInitNeedSize + " members).\n" + - " Struct initializer: " + valueList.toString(program), - source); + if(structDefinition.isUnion()) { + throw new CompileError( + "Union initializer has too many values, since only one is allowed.\n" + + " Union initializer: " + valueList.toString(program), + source); + } else { + throw new CompileError( + "Struct initializer has wrong size (" + valueList.getList().size() + "), " + + "which does not match the number of members in " + structType.getTypeName() + " (" + structInitNeedSize + " members).\n" + + " Struct initializer: " + valueList.toString(program), + source); + + } } boolean allConst = true; diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java b/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java index fc25399e6..59a79833d 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Variable.java @@ -505,10 +505,12 @@ public class Variable implements Symbol { * @return true if an unwinding struct */ public boolean isStructUnwind() { - if(getType() instanceof SymbolTypeStruct) - return isKindPhiMaster() || isKindIntermediate() || isKindPhiVersion(); - else - return false; + if(getType() instanceof SymbolTypeStruct) { + final SymbolTypeStruct typeStruct = (SymbolTypeStruct) getType(); + if(!typeStruct.isUnion()) + return isKindPhiMaster() || isKindIntermediate() || isKindPhiVersion(); + } + return false; } /** @@ -517,7 +519,15 @@ public class Variable implements Symbol { * @return true if an classic struct */ public boolean isStructClassic() { - return getType() instanceof SymbolTypeStruct && isKindLoadStore(); + + if(getType() instanceof SymbolTypeStruct) { + final SymbolTypeStruct typeStruct = (SymbolTypeStruct) getType(); + if(typeStruct.isUnion()) + return true; + if(isKindLoadStore()) + return true; + } + return false; } public boolean isDeclarationOnly() { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java index 505de3476..59a3ac48c 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1UnwindStructValues.java @@ -98,7 +98,7 @@ public class Pass1UnwindStructValues extends Pass1Base { Variable procReturnVar = procedure.getLocalVariable("return"); // TODO: Return-variable has been unwound - detect that instead - use getProgram().getStructVariableMemberUnwinding().getUnwindingMaster() like for parameters if(procReturnVar != null && procReturnVar.isStructUnwind()) { - if(!(call.getlValue() instanceof ValueList)) { + if(call.getlValue()!=null && !(call.getlValue() instanceof ValueList)) { // Return value already unwound - move on final ValueSource valueSource = ValueSourceFactory.getValueSource(call.getlValue(), getProgram(), getScope(), call, stmtIt, currentBlock); RValue unwoundLValue = unwindValue(valueSource, call, stmtIt, currentBlock); @@ -118,30 +118,32 @@ public class Pass1UnwindStructValues extends Pass1Base { boolean anyParameterUnwound = false; final List procParameters = procedure.getParameters(); final List callParameters = call.getParameters(); - for(int idx_call = 0, idx_proc = 0; idx_call < callParameters.size(); idx_call++) { - final RValue callParameter = callParameters.get(idx_call); - final Variable procParameter = procParameters.get(idx_proc); - boolean unwound = false; - final SymbolVariableRef unwindingMaster = getProgram().getStructVariableMemberUnwinding().getUnwindingMaster(procParameter.getRef()); - if(unwindingMaster != null) { - // The procedure parameter is unwound - final ValueSource parameterSource = ValueSourceFactory.getValueSource(callParameter, getProgram(), getScope(), call, stmtIt, currentBlock); - if(parameterSource != null && parameterSource.isUnwindable()) - // Passing an unwinding struct value - for(String memberName : parameterSource.getMemberNames(getScope())) { - ValueSource memberUnwinding = parameterSource.getMemberUnwinding(memberName, getProgram(), getScope(), call, stmtIt, currentBlock); - unwoundParameters.add(memberUnwinding.getSimpleValue(getScope())); - unwound = true; - anyParameterUnwound = true; + if(callParameters!=null && callParameters.size()>0 ){ + for(int idx_call = 0, idx_proc = 0; idx_call < callParameters.size(); idx_call++) { + final RValue callParameter = callParameters.get(idx_call); + final Variable procParameter = procParameters.get(idx_proc); + boolean unwound = false; + final SymbolVariableRef unwindingMaster = getProgram().getStructVariableMemberUnwinding().getUnwindingMaster(procParameter.getRef()); + if(unwindingMaster != null) { + // The procedure parameter is unwound + final ValueSource parameterSource = ValueSourceFactory.getValueSource(callParameter, getProgram(), getScope(), call, stmtIt, currentBlock); + if(parameterSource != null && parameterSource.isUnwindable()) + // Passing an unwinding struct value + for(String memberName : parameterSource.getMemberNames(getScope())) { + ValueSource memberUnwinding = parameterSource.getMemberUnwinding(memberName, getProgram(), getScope(), call, stmtIt, currentBlock); + unwoundParameters.add(memberUnwinding.getSimpleValue(getScope())); + unwound = true; + anyParameterUnwound = true; + idx_proc++; + } + else idx_proc++; - } - else + } else { idx_proc++; - } else { - idx_proc++; - } - if(!unwound) { - unwoundParameters.add(callParameter); + } + if(!unwound) { + unwoundParameters.add(callParameter); + } } } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java b/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java index 3f85505e2..9a7246a7a 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4LiveRangeEquivalenceClassesFinalize.java @@ -4,6 +4,7 @@ import dk.camelot64.kickc.model.*; import dk.camelot64.kickc.model.statements.StatementAssignment; import dk.camelot64.kickc.model.statements.StatementCall; import dk.camelot64.kickc.model.statements.StatementCallFinalize; +import dk.camelot64.kickc.model.symbols.StructDefinition; import dk.camelot64.kickc.model.symbols.Variable; import dk.camelot64.kickc.model.values.RValue; import dk.camelot64.kickc.model.values.ValueList; @@ -59,6 +60,8 @@ public class Pass4LiveRangeEquivalenceClassesFinalize extends Pass2Base { // Add any load/store variables with an initializer - and load/store struct variables for(Variable variable : getSymbols().getAllVariables(true)) { + if(variable.getScope() instanceof StructDefinition) + continue; if(variable.isKindLoadStore() && variable.getInitValue()!=null) addToEquivalenceClassSet(variable.getVariableRef(), new ArrayList<>(), liveRangeEquivalenceClassSet); else if(variable.isStructClassic()) diff --git a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java index c16451a99..f655f403c 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java +++ b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java @@ -221,12 +221,16 @@ public class TestProgramsFast extends TestPrograms { compileAndCompare("struct-unwinding-1.c"); } - // TODO: Fix __varcall returning structs //@Test - //public void testVarCall4() throws IOException { - // compileAndCompare("varcall-4.c", log().verboseStructUnwind().verboseCreateSsa()); + //public void testVarCall5() throws IOException { + // compileAndCompare("varcall-5.c", log().verboseCreateSsa().verboseStructUnwind()); //} + @Test + public void testVarCall4() throws IOException { + compileAndCompare("varcall-4.c"); + } + @Test public void testVarCall3() throws IOException { compileAndCompare("varcall-3.c"); @@ -2223,6 +2227,11 @@ public class TestProgramsFast extends TestPrograms { compileAndCompare("struct-directives.c"); } + //@Test + //public void testUnion7() throws IOException { + // compileAndCompare("union-7.c", log().verboseStructUnwind()); + //} + @Test public void testUnion6() throws IOException { compileAndCompare("union-6.c"); @@ -2814,7 +2823,7 @@ public class TestProgramsFast extends TestPrograms { @Test public void testTypeIdPlusBytes() throws IOException { - compileAndCompare("typeid-plus-bytes.c", log()); + compileAndCompare("typeid-plus-bytes.c"); } @Test diff --git a/src/test/kc/union-7.c b/src/test/kc/union-7.c new file mode 100644 index 000000000..c89734b36 --- /dev/null +++ b/src/test/kc/union-7.c @@ -0,0 +1,23 @@ +// Minimal union with C-Standard behavior - union parameter + +union Data { + unsigned char b; + unsigned int w; +}; + +union Data data1 = { 0x12 }; +union Data data2 = { 0x34 }; + +void main() { + print(data1); + print(data2); +} + +char* const SCREEN = (char*)0x0400; +char idx = 0; + +void print(union Data data) { + SCREEN[idx++] = BYTE1(data.w); + SCREEN[idx++] = data.b; + SCREEN[idx++] = ' '; +} \ No newline at end of file diff --git a/src/test/kc/varcall-4.c b/src/test/kc/varcall-4.c index f5e00cc96..c244fec17 100644 --- a/src/test/kc/varcall-4.c +++ b/src/test/kc/varcall-4.c @@ -1,22 +1,27 @@ // Test __varcall calling convention -// Struct parameter & return value +// Struct parameter struct Cols { char border; char bg; + char fg; }; -struct Cols * const COLS = (struct Cols *)0xd020; +char * const COLS = (char *)0xd020; + +struct Cols a = { 1, 2, 3 }; +struct Cols b = { 3, 4, 6 }; +struct Cols c = { 5, 6, 7 }; +struct Cols d; void main() { - struct Cols a = { 1, 2 }; - //*COLS = a; - a = plus(a, { 2, 3 } ); - *COLS = a; - //a = plus(a, a); - //*COLS = a; + char sum1 = fg_sum(a, b); + *COLS = sum1; + d = b; + char sum2 = fg_sum(c, d); + *COLS = sum2; } -__varcall struct Cols plus(struct Cols a, struct Cols b) { - return { a.border+b.border, a.bg+b.bg }; +__varcall char fg_sum(struct Cols a, struct Cols b) { + return a.fg+b.fg; } \ No newline at end of file diff --git a/src/test/kc/varcall-5.c b/src/test/kc/varcall-5.c new file mode 100644 index 000000000..0e0b1cd1a --- /dev/null +++ b/src/test/kc/varcall-5.c @@ -0,0 +1,22 @@ +// Test __varcall calling convention +// Struct return value + +struct Cols { + char border; + char bg; +}; + +struct Cols * const COLS = (struct Cols *)0xd020; + +struct Cols a; + +void main() { + a = make(1); + *COLS = a; + a = make(2); + *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 new file mode 100644 index 000000000..f5e00cc96 --- /dev/null +++ b/src/test/kc/varcall-6.c @@ -0,0 +1,22 @@ +// Test __varcall calling convention +// Struct parameter & return value + +struct Cols { + char border; + char bg; +}; + +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 diff --git a/src/test/ref/varcall-4.asm b/src/test/ref/varcall-4.asm new file mode 100644 index 000000000..52e4fa4e3 --- /dev/null +++ b/src/test/ref/varcall-4.asm @@ -0,0 +1,80 @@ +// Test __varcall calling convention +// Struct parameter + // Commodore 64 PRG executable file +.file [name="varcall-4.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 = 3 + .const OFFSET_STRUCT_COLS_FG = 2 + .label COLS = $d020 +.segment Code +// fg_sum(struct Cols zp(3) a, struct Cols zp(6) b) +fg_sum: { + .label a = 3 + .label b = 6 + .label return = 2 + // a.fg+b.fg + lda a+OFFSET_STRUCT_COLS_FG + clc + adc b+OFFSET_STRUCT_COLS_FG + // return a.fg+b.fg; + sta.z return + // } + rts +} +main: { + // fg_sum(a, b) + ldy #SIZEOF_STRUCT_COLS + !: + lda a-1,y + sta fg_sum.a-1,y + dey + bne !- + ldy #SIZEOF_STRUCT_COLS + !: + lda b-1,y + sta fg_sum.b-1,y + dey + bne !- + jsr fg_sum + // char sum1 = fg_sum(a, b) + lda.z fg_sum.return + // *COLS = sum1 + sta COLS + // d = b + ldy #SIZEOF_STRUCT_COLS + !: + lda b-1,y + sta d-1,y + dey + bne !- + // fg_sum(c, d) + ldy #SIZEOF_STRUCT_COLS + !: + lda c-1,y + sta fg_sum.a-1,y + dey + bne !- + ldy #SIZEOF_STRUCT_COLS + !: + lda d-1,y + sta fg_sum.b-1,y + dey + bne !- + jsr fg_sum + // char sum2 = fg_sum(c, d) + lda.z fg_sum.return + // *COLS = sum2 + sta COLS + // } + rts +} +.segment Data + a: .byte 1, 2, 3 + b: .byte 3, 4, 6 + c: .byte 5, 6, 7 + d: .fill SIZEOF_STRUCT_COLS, 0 diff --git a/src/test/ref/varcall-4.cfg b/src/test/ref/varcall-4.cfg new file mode 100644 index 000000000..94dd81807 --- /dev/null +++ b/src/test/ref/varcall-4.cfg @@ -0,0 +1,27 @@ + +__varcall byte fg_sum(struct Cols fg_sum::a , struct Cols fg_sum::b) +fg_sum: scope:[fg_sum] from + [0] fg_sum::$0 = *((byte*)&fg_sum::a+OFFSET_STRUCT_COLS_FG) + *((byte*)&fg_sum::b+OFFSET_STRUCT_COLS_FG) + [1] fg_sum::return = fg_sum::$0 + to:fg_sum::@return +fg_sum::@return: scope:[fg_sum] from fg_sum + [2] return + to:@return + +void main() +main: scope:[main] from + [3] *(&fg_sum::a) = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) + [4] *(&fg_sum::b) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) + [5] callexecute fg_sum + [6] main::sum1#0 = fg_sum::return + [7] *COLS = main::sum1#0 + [8] *(&d) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) + [9] *(&fg_sum::a) = memcpy(*(&c), struct Cols, SIZEOF_STRUCT_COLS) + [10] *(&fg_sum::b) = memcpy(*(&d), struct Cols, SIZEOF_STRUCT_COLS) + [11] callexecute fg_sum + [12] main::sum2#0 = fg_sum::return + [13] *COLS = main::sum2#0 + to:main::@return +main::@return: scope:[main] from main + [14] return + to:@return diff --git a/src/test/ref/varcall-4.log b/src/test/ref/varcall-4.log new file mode 100644 index 000000000..380afcea9 --- /dev/null +++ b/src/test/ref/varcall-4.log @@ -0,0 +1,458 @@ +Converting parameter in __varcall procedure to load/store fg_sum::a +Converting parameter in __varcall procedure to load/store fg_sum::b +Converting return in __varcall procedure to load/store fg_sum::return +Calling convention __varcall adding prepare/execute/finalize for main::$0 = call fg_sum a b +Calling convention __varcall adding prepare/execute/finalize for main::$1 = call fg_sum c d +Calling convention VAR_CALL adding return value assignment main::$0 = fg_sum::return +Calling convention VAR_CALL adding return value assignment main::$1 = fg_sum::return + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + *(&fg_sum::a) = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) + fg_sum::a = struct-unwound {*(&fg_sum::a)} + *(&fg_sum::b) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) + fg_sum::b = struct-unwound {*(&fg_sum::b)} + callexecute fg_sum + main::$0 = fg_sum::return + main::sum1#0 = main::$0 + *COLS = main::sum1#0 + *(&d) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) + d = struct-unwound {*(&d)} + *(&fg_sum::a) = memcpy(*(&c), struct Cols, SIZEOF_STRUCT_COLS) + fg_sum::a = struct-unwound {*(&fg_sum::a)} + *(&fg_sum::b) = memcpy(*(&d), struct Cols, SIZEOF_STRUCT_COLS) + fg_sum::b = struct-unwound {*(&fg_sum::b)} + callexecute fg_sum + main::$1 = fg_sum::return + main::sum2#0 = main::$1 + *COLS = main::sum2#0 + to:main::@return +main::@return: scope:[main] from main + return + to:@return + +__varcall byte fg_sum(struct Cols fg_sum::a , struct Cols fg_sum::b) +fg_sum: scope:[fg_sum] from + fg_sum::$0 = *((byte*)&fg_sum::a+OFFSET_STRUCT_COLS_FG) + *((byte*)&fg_sum::b+OFFSET_STRUCT_COLS_FG) + fg_sum::return = fg_sum::$0 + to:fg_sum::@return +fg_sum::@return: scope:[fg_sum] from fg_sum + 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 byte* const COLS = (byte*)$d020 +constant byte OFFSET_STRUCT_COLS_FG = 2 +constant byte SIZEOF_STRUCT_COLS = 3 +void __start() +struct Cols a loadstore = { border: 1, bg: 2, fg: 3 } +struct Cols b loadstore = { border: 3, bg: 4, fg: 6 } +struct Cols c loadstore = { border: 5, bg: 6, fg: 7 } +struct Cols d loadstore = {} +__varcall byte fg_sum(struct Cols fg_sum::a , struct Cols fg_sum::b) +byte~ fg_sum::$0 +struct Cols fg_sum::a loadstore +struct Cols fg_sum::b loadstore +byte fg_sum::return loadstore +void main() +byte~ main::$0 +byte~ main::$1 +byte main::sum1 +byte main::sum1#0 +byte main::sum2 +byte main::sum2#0 + +Simplifying constant pointer cast (byte*) 53280 +Successful SSA optimization PassNCastSimplification +Alias candidate removed (volatile)fg_sum::return = fg_sum::$0 +Alias main::sum1#0 = main::$0 +Alias main::sum2#0 = main::$1 +Successful SSA optimization Pass2AliasElimination +Alias candidate removed (volatile)fg_sum::return = fg_sum::$0 +Removing C-classic struct-unwound assignment [1] fg_sum::a = struct-unwound {*(&fg_sum::a)} +Removing C-classic struct-unwound assignment [3] fg_sum::b = struct-unwound {*(&fg_sum::b)} +Removing C-classic struct-unwound assignment [8] d = struct-unwound {*(&d)} +Removing C-classic struct-unwound assignment [10] fg_sum::a = struct-unwound {*(&fg_sum::a)} +Removing C-classic struct-unwound assignment [12] fg_sum::b = struct-unwound {*(&fg_sum::b)} +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)fg_sum::return = fg_sum::$0 +Alias candidate removed (volatile)fg_sum::return = fg_sum::$0 +Alias candidate removed (volatile)fg_sum::return = fg_sum::$0 +CALL GRAPH +Calls in [main] to fg_sum:5 fg_sum:11 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +__varcall byte fg_sum(struct Cols fg_sum::a , struct Cols fg_sum::b) +fg_sum: scope:[fg_sum] from + [0] fg_sum::$0 = *((byte*)&fg_sum::a+OFFSET_STRUCT_COLS_FG) + *((byte*)&fg_sum::b+OFFSET_STRUCT_COLS_FG) + [1] fg_sum::return = fg_sum::$0 + to:fg_sum::@return +fg_sum::@return: scope:[fg_sum] from fg_sum + [2] return + to:@return + +void main() +main: scope:[main] from + [3] *(&fg_sum::a) = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) + [4] *(&fg_sum::b) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) + [5] callexecute fg_sum + [6] main::sum1#0 = fg_sum::return + [7] *COLS = main::sum1#0 + [8] *(&d) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) + [9] *(&fg_sum::a) = memcpy(*(&c), struct Cols, SIZEOF_STRUCT_COLS) + [10] *(&fg_sum::b) = memcpy(*(&d), struct Cols, SIZEOF_STRUCT_COLS) + [11] callexecute fg_sum + [12] main::sum2#0 = fg_sum::return + [13] *COLS = main::sum2#0 + to:main::@return +main::@return: scope:[main] from main + [14] return + to:@return + + +VARIABLE REGISTER WEIGHTS +struct Cols a loadstore = { border: 1, bg: 2, fg: 3 } +struct Cols b loadstore = { border: 3, bg: 4, fg: 6 } +struct Cols c loadstore = { border: 5, bg: 6, fg: 7 } +struct Cols d loadstore = {} +__varcall byte fg_sum(struct Cols fg_sum::a , struct Cols fg_sum::b) +byte~ fg_sum::$0 22.0 +struct Cols fg_sum::a loadstore +struct Cols fg_sum::b loadstore +byte fg_sum::return loadstore 3.75 +void main() +byte main::sum1 +byte main::sum1#0 4.0 +byte main::sum2 +byte main::sum2#0 4.0 + +Initial phi equivalence classes +Added variable fg_sum::$0 to live range equivalence class [ fg_sum::$0 ] +Added variable fg_sum::return to live range equivalence class [ fg_sum::return ] +Added variable main::sum1#0 to live range equivalence class [ main::sum1#0 ] +Added variable main::sum2#0 to live range equivalence class [ main::sum2#0 ] +Added variable a to live range equivalence class [ a ] +Added variable b to live range equivalence class [ b ] +Added variable c to live range equivalence class [ c ] +Added variable d to live range equivalence class [ d ] +Added variable fg_sum::a to live range equivalence class [ fg_sum::a ] +Added variable fg_sum::b to live range equivalence class [ fg_sum::b ] +Complete equivalence classes +[ fg_sum::$0 ] +[ fg_sum::return ] +[ main::sum1#0 ] +[ main::sum2#0 ] +[ a ] +[ b ] +[ c ] +[ d ] +[ fg_sum::a ] +[ fg_sum::b ] +Allocated zp[1]:2 [ fg_sum::$0 ] +Allocated zp[1]:3 [ fg_sum::return ] +Allocated zp[1]:4 [ main::sum1#0 ] +Allocated zp[1]:5 [ main::sum2#0 ] +Allocated mem[3] [ a ] +Allocated mem[3] [ b ] +Allocated mem[3] [ c ] +Allocated mem[3] [ d ] +Allocated zp[3]:6 [ fg_sum::a ] +Allocated zp[3]:9 [ fg_sum::b ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [0] fg_sum::$0 = *((byte*)&fg_sum::a+OFFSET_STRUCT_COLS_FG) + *((byte*)&fg_sum::b+OFFSET_STRUCT_COLS_FG) [ fg_sum::$0 fg_sum::a fg_sum::b ] ( fg_sum:5 [ b d c fg_sum::$0 fg_sum::a fg_sum::b ] { } fg_sum:11 [ fg_sum::$0 fg_sum::a fg_sum::b ] { } ) always clobbers reg byte a +Statement [3] *(&fg_sum::a) = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) [ fg_sum::a fg_sum::b b d c ] ( [ fg_sum::a fg_sum::b b d c ] { } ) always clobbers reg byte a reg byte y +Statement [4] *(&fg_sum::b) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) [ fg_sum::a fg_sum::b b d c ] ( [ fg_sum::a fg_sum::b b d c ] { } ) always clobbers reg byte a reg byte y +Statement [8] *(&d) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) [ fg_sum::a fg_sum::b d c ] ( [ fg_sum::a fg_sum::b d c ] { } ) always clobbers reg byte a reg byte y +Statement [9] *(&fg_sum::a) = memcpy(*(&c), struct Cols, SIZEOF_STRUCT_COLS) [ fg_sum::a fg_sum::b d ] ( [ fg_sum::a fg_sum::b d ] { } ) always clobbers reg byte a reg byte y +Statement [10] *(&fg_sum::b) = memcpy(*(&d), struct Cols, SIZEOF_STRUCT_COLS) [ fg_sum::a fg_sum::b ] ( [ fg_sum::a fg_sum::b ] { } ) always clobbers reg byte a reg byte y +Potential registers zp[1]:2 [ fg_sum::$0 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ fg_sum::return ] : zp[1]:3 , +Potential registers zp[1]:4 [ main::sum1#0 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:5 [ main::sum2#0 ] : zp[1]:5 , reg byte a , reg byte x , reg byte y , +Potential registers mem[3] [ a ] : mem[3] , +Potential registers mem[3] [ b ] : mem[3] , +Potential registers mem[3] [ c ] : mem[3] , +Potential registers mem[3] [ d ] : mem[3] , +Potential registers zp[3]:6 [ fg_sum::a ] : zp[3]:6 , +Potential registers zp[3]:9 [ fg_sum::b ] : zp[3]:9 , + +REGISTER UPLIFT SCOPES +Uplift Scope [fg_sum] 22: zp[1]:2 [ fg_sum::$0 ] 3.75: zp[1]:3 [ fg_sum::return ] 0: zp[3]:6 [ fg_sum::a ] 0: zp[3]:9 [ fg_sum::b ] +Uplift Scope [main] 4: zp[1]:4 [ main::sum1#0 ] 4: zp[1]:5 [ main::sum2#0 ] +Uplift Scope [Cols] +Uplift Scope [] 0: mem[3] [ a ] 0: mem[3] [ b ] 0: mem[3] [ c ] 0: mem[3] [ d ] + +Uplifting [fg_sum] best 149 combination reg byte a [ fg_sum::$0 ] zp[1]:3 [ fg_sum::return ] zp[3]:6 [ fg_sum::a ] zp[3]:9 [ fg_sum::b ] +Uplifting [main] best 137 combination reg byte a [ main::sum1#0 ] reg byte a [ main::sum2#0 ] +Uplifting [Cols] best 137 combination +Uplifting [] best 137 combination mem[3] [ a ] mem[3] [ b ] mem[3] [ c ] mem[3] [ d ] +Attempting to uplift remaining variables inzp[1]:3 [ fg_sum::return ] +Uplifting [fg_sum] best 137 combination zp[1]:3 [ fg_sum::return ] +Allocated (was zp[1]:3) zp[1]:2 [ fg_sum::return ] +Allocated (was zp[3]:6) zp[3]:3 [ fg_sum::a ] +Allocated (was zp[3]:9) zp[3]:6 [ fg_sum::b ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test __varcall calling convention +// Struct parameter + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-4.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 = 3 + .const OFFSET_STRUCT_COLS_FG = 2 + .label COLS = $d020 +.segment Code + // fg_sum +// fg_sum(struct Cols zp(3) a, struct Cols zp(6) b) +fg_sum: { + .label a = 3 + .label b = 6 + .label return = 2 + // [0] fg_sum::$0 = *((byte*)&fg_sum::a+OFFSET_STRUCT_COLS_FG) + *((byte*)&fg_sum::b+OFFSET_STRUCT_COLS_FG) -- vbuaa=_deref_pbuc1_plus__deref_pbuc2 + lda a+OFFSET_STRUCT_COLS_FG + clc + adc b+OFFSET_STRUCT_COLS_FG + // [1] fg_sum::return = fg_sum::$0 -- vbuz1=vbuaa + sta.z return + jmp __breturn + // fg_sum::@return + __breturn: + // [2] return + rts +} + // main +main: { + // [3] *(&fg_sum::a) = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda a-1,y + sta fg_sum.a-1,y + dey + bne !- + // [4] *(&fg_sum::b) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda b-1,y + sta fg_sum.b-1,y + dey + bne !- + // [5] callexecute fg_sum -- jsr + jsr fg_sum + // [6] main::sum1#0 = fg_sum::return -- vbuaa=vbuz1 + lda.z fg_sum.return + // [7] *COLS = main::sum1#0 -- _deref_pbuc1=vbuaa + sta COLS + // [8] *(&d) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda b-1,y + sta d-1,y + dey + bne !- + // [9] *(&fg_sum::a) = memcpy(*(&c), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda c-1,y + sta fg_sum.a-1,y + dey + bne !- + // [10] *(&fg_sum::b) = memcpy(*(&d), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda d-1,y + sta fg_sum.b-1,y + dey + bne !- + // [11] callexecute fg_sum -- jsr + jsr fg_sum + // [12] main::sum2#0 = fg_sum::return -- vbuaa=vbuz1 + lda.z fg_sum.return + // [13] *COLS = main::sum2#0 -- _deref_pbuc1=vbuaa + sta COLS + jmp __breturn + // main::@return + __breturn: + // [14] return + rts +} + // File Data +.segment Data + a: .byte 1, 2, 3 + b: .byte 3, 4, 6 + c: .byte 5, 6, 7 + d: .fill SIZEOF_STRUCT_COLS, 0 + +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 byte* const COLS = (byte*) 53280 +constant byte OFFSET_STRUCT_COLS_FG = 2 +constant byte SIZEOF_STRUCT_COLS = 3 +struct Cols a loadstore mem[3] = { border: 1, bg: 2, fg: 3 } +struct Cols b loadstore mem[3] = { border: 3, bg: 4, fg: 6 } +struct Cols c loadstore mem[3] = { border: 5, bg: 6, fg: 7 } +struct Cols d loadstore mem[3] = {} +__varcall byte fg_sum(struct Cols fg_sum::a , struct Cols fg_sum::b) +byte~ fg_sum::$0 reg byte a 22.0 +struct Cols fg_sum::a loadstore zp[3]:3 +struct Cols fg_sum::b loadstore zp[3]:6 +byte fg_sum::return loadstore zp[1]:2 3.75 +void main() +byte main::sum1 +byte main::sum1#0 reg byte a 4.0 +byte main::sum2 +byte main::sum2#0 reg byte a 4.0 + +reg byte a [ fg_sum::$0 ] +zp[1]:2 [ fg_sum::return ] +reg byte a [ main::sum1#0 ] +reg byte a [ main::sum2#0 ] +mem[3] [ a ] +mem[3] [ b ] +mem[3] [ c ] +mem[3] [ d ] +zp[3]:3 [ fg_sum::a ] +zp[3]:6 [ fg_sum::b ] + + +FINAL ASSEMBLER +Score: 131 + + // File Comments +// Test __varcall calling convention +// Struct parameter + // Upstart + // Commodore 64 PRG executable file +.file [name="varcall-4.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 = 3 + .const OFFSET_STRUCT_COLS_FG = 2 + .label COLS = $d020 +.segment Code + // fg_sum +// fg_sum(struct Cols zp(3) a, struct Cols zp(6) b) +fg_sum: { + .label a = 3 + .label b = 6 + .label return = 2 + // a.fg+b.fg + // [0] fg_sum::$0 = *((byte*)&fg_sum::a+OFFSET_STRUCT_COLS_FG) + *((byte*)&fg_sum::b+OFFSET_STRUCT_COLS_FG) -- vbuaa=_deref_pbuc1_plus__deref_pbuc2 + lda a+OFFSET_STRUCT_COLS_FG + clc + adc b+OFFSET_STRUCT_COLS_FG + // return a.fg+b.fg; + // [1] fg_sum::return = fg_sum::$0 -- vbuz1=vbuaa + sta.z return + // fg_sum::@return + // } + // [2] return + rts +} + // main +main: { + // fg_sum(a, b) + // [3] *(&fg_sum::a) = memcpy(*(&a), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda a-1,y + sta fg_sum.a-1,y + dey + bne !- + // [4] *(&fg_sum::b) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda b-1,y + sta fg_sum.b-1,y + dey + bne !- + // [5] callexecute fg_sum -- jsr + jsr fg_sum + // char sum1 = fg_sum(a, b) + // [6] main::sum1#0 = fg_sum::return -- vbuaa=vbuz1 + lda.z fg_sum.return + // *COLS = sum1 + // [7] *COLS = main::sum1#0 -- _deref_pbuc1=vbuaa + sta COLS + // d = b + // [8] *(&d) = memcpy(*(&b), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda b-1,y + sta d-1,y + dey + bne !- + // fg_sum(c, d) + // [9] *(&fg_sum::a) = memcpy(*(&c), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda c-1,y + sta fg_sum.a-1,y + dey + bne !- + // [10] *(&fg_sum::b) = memcpy(*(&d), struct Cols, SIZEOF_STRUCT_COLS) -- _deref_pssc1=_deref_pssc2_memcpy_vbuc3 + ldy #SIZEOF_STRUCT_COLS + !: + lda d-1,y + sta fg_sum.b-1,y + dey + bne !- + // [11] callexecute fg_sum -- jsr + jsr fg_sum + // char sum2 = fg_sum(c, d) + // [12] main::sum2#0 = fg_sum::return -- vbuaa=vbuz1 + lda.z fg_sum.return + // *COLS = sum2 + // [13] *COLS = main::sum2#0 -- _deref_pbuc1=vbuaa + sta COLS + // main::@return + // } + // [14] return + rts +} + // File Data +.segment Data + a: .byte 1, 2, 3 + b: .byte 3, 4, 6 + c: .byte 5, 6, 7 + d: .fill SIZEOF_STRUCT_COLS, 0 + diff --git a/src/test/ref/varcall-4.sym b/src/test/ref/varcall-4.sym new file mode 100644 index 000000000..390cc774b --- /dev/null +++ b/src/test/ref/varcall-4.sym @@ -0,0 +1,28 @@ +constant byte* const COLS = (byte*) 53280 +constant byte OFFSET_STRUCT_COLS_FG = 2 +constant byte SIZEOF_STRUCT_COLS = 3 +struct Cols a loadstore mem[3] = { border: 1, bg: 2, fg: 3 } +struct Cols b loadstore mem[3] = { border: 3, bg: 4, fg: 6 } +struct Cols c loadstore mem[3] = { border: 5, bg: 6, fg: 7 } +struct Cols d loadstore mem[3] = {} +__varcall byte fg_sum(struct Cols fg_sum::a , struct Cols fg_sum::b) +byte~ fg_sum::$0 reg byte a 22.0 +struct Cols fg_sum::a loadstore zp[3]:3 +struct Cols fg_sum::b loadstore zp[3]:6 +byte fg_sum::return loadstore zp[1]:2 3.75 +void main() +byte main::sum1 +byte main::sum1#0 reg byte a 4.0 +byte main::sum2 +byte main::sum2#0 reg byte a 4.0 + +reg byte a [ fg_sum::$0 ] +zp[1]:2 [ fg_sum::return ] +reg byte a [ main::sum1#0 ] +reg byte a [ main::sum2#0 ] +mem[3] [ a ] +mem[3] [ b ] +mem[3] [ c ] +mem[3] [ d ] +zp[3]:3 [ fg_sum::a ] +zp[3]:6 [ fg_sum::b ]