diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 6e615622c..6991eb5f7 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -215,6 +215,7 @@ public class Compiler { new Pass1UnwindBlockScopes(program).execute(); new Pass1Procedures(program).execute(); new PassNTypeInference(program).execute(); + new PassNFixIntermediateMemoryArea(program).execute(); new PassNTypeIdSimplification(program).execute(); new Pass1StructTypeSizeFix(program).execute(); new Pass1PrintfIntrinsicRewrite(program).execute(); @@ -549,7 +550,6 @@ public class Compiler { if(getLog().isVerboseSizeInfo()) { getLog().append(program.getSizeInfo()); } - new Pass3AssertNoTypeId(program).check(); new Pass3AssertRValues(program).check(); new Pass3AssertNoNumbers(program).check(); diff --git a/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java b/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java index 7808fa50a..890a0ca34 100644 --- a/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java +++ b/src/main/java/dk/camelot64/kickc/model/VariableBuilder.java @@ -54,12 +54,14 @@ public class VariableBuilder { /** * Create a variable builder for an intermediate variable + * + * * @param scope The scope to create the intermediate variable in * @param type The variable type * @param program The global program * @return The new intermediate variable */ - public static Variable createIntermediate(Scope scope, SymbolType type, Program program) { + public static Variable createIntermediate(Scope scope, SymbolType type, Program program) { VariableBuilder builder = new VariableBuilder(scope.allocateIntermediateVariableName(), scope, false, true, type, null, scope.getSegmentData(), program.getTargetPlatform().getVariableBuilderConfig()); return builder.build(); } @@ -139,6 +141,15 @@ public class VariableBuilder { return type instanceof SymbolTypePointer; } + /** + * Is the type is a var (currently unknown). + * + * @return True if the type is currently unknown + */ + public boolean isTypeVar() { + return SymbolType.VAR.equals(type) || SymbolType.VOID.equals(type); + } + /** * Is the variable a global variable * @@ -209,7 +220,7 @@ public class VariableBuilder { * @return true if the variable is an array declaration */ public boolean isArray() { - return isTypePointer() && ((SymbolTypePointer)type).getArraySpec()!=null; + return isTypePointer() && ((SymbolTypePointer) type).getArraySpec() != null; } /** @@ -303,7 +314,7 @@ public class VariableBuilder { return false; else { VariableBuilderConfig.Scope scope = VariableBuilderConfig.getScope(isScopeGlobal(), isScopeLocal(), isScopeIntermediate(), isScopeParameter(), isScopeMember()); - VariableBuilderConfig.Type type = VariableBuilderConfig.getType(isTypeInteger(), isArray(), isTypePointer(), isTypeStruct()); + VariableBuilderConfig.Type type = VariableBuilderConfig.getType(isTypeInteger(), isArray(), isTypePointer(), isTypeStruct(), isTypeVar()); VariableBuilderConfig.Setting setting = config.getSetting(scope, type); if(setting != null && VariableBuilderConfig.Optimization.MA.equals(setting.optimization)) return false; @@ -351,16 +362,12 @@ public class VariableBuilder { return Variable.MemoryArea.ZEROPAGE_MEMORY; else { VariableBuilderConfig.Scope scope = VariableBuilderConfig.getScope(isScopeGlobal(), isScopeLocal(), isScopeIntermediate(), isScopeParameter(), isScopeMember()); - if(scope.equals(VariableBuilderConfig.Scope.INTERMEDIATE)) { + VariableBuilderConfig.Type type = VariableBuilderConfig.getType(isTypeInteger(), isArray(), isTypePointer(), isTypeStruct(), isTypeVar()); + VariableBuilderConfig.Setting setting = config.getSetting(scope, type); + if(setting != null && VariableBuilderConfig.MemoryArea.MEM.equals(setting.memoryArea)) + return Variable.MemoryArea.MAIN_MEMORY; + else return Variable.MemoryArea.ZEROPAGE_MEMORY; - } else { - VariableBuilderConfig.Type type = VariableBuilderConfig.getType(isTypeInteger(), isArray(), isTypePointer(), isTypeStruct()); - VariableBuilderConfig.Setting setting = config.getSetting(scope, type); - if(setting != null && VariableBuilderConfig.MemoryArea.MEM.equals(setting.memoryArea)) - return Variable.MemoryArea.MAIN_MEMORY; - else - return Variable.MemoryArea.ZEROPAGE_MEMORY; - } } } diff --git a/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java b/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java index 8e7ab54ec..c7eb400e1 100644 --- a/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java +++ b/src/main/java/dk/camelot64/kickc/model/VariableBuilderConfig.java @@ -93,6 +93,8 @@ public class VariableBuilderConfig { config.addSetting("parameter_ssa", StatementSource.NONE); // Pointers are always on zeropage config.addSetting("pointer_zp", StatementSource.NONE); + // Pointers are always on zeropage + config.addSetting("intermediate_var_zp", StatementSource.NONE); } /** The different scopes. */ @@ -102,7 +104,7 @@ public class VariableBuilderConfig { /** The different types. */ public enum Type { - INTEGER, POINTER, STRUCT, ARRAY + INTEGER, POINTER, STRUCT, ARRAY, VAR } /** The optimizations. */ @@ -207,6 +209,9 @@ public class VariableBuilderConfig { } else if(paramElement.equals("parameter")) { paramElements.remove(0); return Arrays.asList(Scope.PARAMETER); + } else if(paramElement.equals("intermediate")) { + paramElements.remove(0); + return Arrays.asList(Scope.INTERMEDIATE); } else return Arrays.asList(Scope.values()); } @@ -232,6 +237,9 @@ public class VariableBuilderConfig { } else if(paramElement.equals("struct")) { paramElements.remove(0); return Arrays.asList(Type.STRUCT); + } else if(paramElement.equals("var")) { + paramElements.remove(0); + return Arrays.asList(Type.VAR); } else return Arrays.asList(Type.values()); } @@ -303,7 +311,7 @@ public class VariableBuilderConfig { throw new InternalError("Unknown scope!"); } - public static Type getType(boolean isTypeInteger, boolean isTypeArray, boolean isTypePointer, boolean isTypeStruct) { + public static Type getType(boolean isTypeInteger, boolean isTypeArray, boolean isTypePointer, boolean isTypeStruct, boolean isTypeVar) { if(isTypeInteger) return Type.INTEGER; if(isTypeArray) @@ -312,6 +320,8 @@ public class VariableBuilderConfig { return Type.POINTER; if(isTypeStruct) return Type.STRUCT; + if(isTypeVar) + return Type.VAR; throw new InternalError("Unknown type!"); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index 5d264fdd9..a5808c8f7 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -588,8 +588,9 @@ public class Pass4CodeGeneration { // Add any comments generateComments(asm, variable.getComments()); final String mainAsmName = AsmFormat.getAsmConstant(program, new ConstantSymbolPointer(mainVar.getRef()), 99, scopeRef); - if(!mainAsmName.equals(variable.getAsmName())) { - asm.addLabelDecl(variable.getAsmName(), mainAsmName); + final String asmSymbolName = AsmFormat.getAsmSymbolName(program, mainVar, scopeRef); + if(!mainAsmName.equals(asmSymbolName)) { + asm.addLabelDecl(asmSymbolName, mainAsmName); } else { // Add any alignment Integer declaredAlignment = variable.getMemoryAlignment(); diff --git a/src/main/java/dk/camelot64/kickc/passes/PassNFixIntermediateMemoryArea.java b/src/main/java/dk/camelot64/kickc/passes/PassNFixIntermediateMemoryArea.java new file mode 100644 index 000000000..49f99391f --- /dev/null +++ b/src/main/java/dk/camelot64/kickc/passes/PassNFixIntermediateMemoryArea.java @@ -0,0 +1,36 @@ +package dk.camelot64.kickc.passes; + +import dk.camelot64.kickc.model.Program; +import dk.camelot64.kickc.model.VariableBuilder; +import dk.camelot64.kickc.model.VariableBuilderConfig; +import dk.camelot64.kickc.model.symbols.Variable; + +/** + * Fix the memory area for intermediate variables + */ +public class PassNFixIntermediateMemoryArea extends Pass2SsaOptimization { + + public PassNFixIntermediateMemoryArea(Program program) { + super(program); + } + + @Override + public boolean step() { + boolean modified = false; + final VariableBuilderConfig variableBuilderConfig = getProgram().getTargetPlatform().getVariableBuilderConfig(); + for(Variable var : getScope().getAllVars(true)) { + if(var.isKindIntermediate()) { + final VariableBuilder builder = new VariableBuilder(var.getLocalName(), var.getScope(), false, true, var.getType(), null, var.getDataSegment(), variableBuilderConfig); + final Variable.MemoryArea memoryArea = builder.getMemoryArea(); + if(!memoryArea.equals(var.getMemoryArea())) { + // Update the variable memory area + getLog().append("Updating intermediate variable memory area to "+memoryArea.name() + " " + var.toString()); + var.setMemoryArea(memoryArea); + modified = true; + } + } + + } + return modified; + } +} diff --git a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java index 4a868695e..d0dea1761 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java +++ b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java @@ -970,6 +970,11 @@ public class TestProgramsFast extends TestPrograms { compileAndCompare("varmodel-ma_mem.c"); } + @Test + public void testVarModelMem1() throws IOException { + compileAndCompare("varmodel-mem-1.c"); + } + @Test public void testVarModelUnknown() throws IOException { assertError("varmodel-unknown.c", "Malformed var_model parameter"); diff --git a/src/test/kc/varmodel-mem-1.c b/src/test/kc/varmodel-mem-1.c new file mode 100644 index 000000000..c0f46d276 --- /dev/null +++ b/src/test/kc/varmodel-mem-1.c @@ -0,0 +1,17 @@ +// Test that memory model "mem" affects intermediates + +#pragma var_model(mem) + +void main() { + // A local pointer + char* const SCREEN = (char*)0x0400; + // A local counter + for( char i: 0..5 ) { + // Generates an intermediate + *(SCREEN+i) = sum(i,i)+sum(i,i); + } +} + +char sum(char a, char b) { + return a+b; +} \ No newline at end of file diff --git a/src/test/ref/ma_coalesce_problem.log b/src/test/ref/ma_coalesce_problem.log index d2e0df7fb..c62cc1500 100644 --- a/src/test/ref/ma_coalesce_problem.log +++ b/src/test/ref/ma_coalesce_problem.log @@ -1,4 +1,5 @@ Resolved forward reference SINTABLE to SINTABLE +Updating intermediate variable memory area to MAIN_MEMORY main::$0 CONTROL FLOW GRAPH SSA diff --git a/src/test/ref/varmodel-ma_mem-2.log b/src/test/ref/varmodel-ma_mem-2.log index 8435e09df..e2e5954e2 100644 --- a/src/test/ref/varmodel-ma_mem-2.log +++ b/src/test/ref/varmodel-ma_mem-2.log @@ -1,3 +1,4 @@ +Updating intermediate variable memory area to MAIN_MEMORY main::$0 CONTROL FLOW GRAPH SSA diff --git a/src/test/ref/varmodel-ma_mem-3.log b/src/test/ref/varmodel-ma_mem-3.log index ec01b024a..a40e46807 100644 --- a/src/test/ref/varmodel-ma_mem-3.log +++ b/src/test/ref/varmodel-ma_mem-3.log @@ -1,3 +1,4 @@ +Updating intermediate variable memory area to ZEROPAGE_MEMORY model_ma_mem::$0 CONTROL FLOW GRAPH SSA diff --git a/src/test/ref/varmodel-ma_mem-4.log b/src/test/ref/varmodel-ma_mem-4.log index a6998e216..74191c469 100644 --- a/src/test/ref/varmodel-ma_mem-4.log +++ b/src/test/ref/varmodel-ma_mem-4.log @@ -1,3 +1,4 @@ +Updating intermediate variable memory area to MAIN_MEMORY main::$0 Inlined call call __init CONTROL FLOW GRAPH SSA diff --git a/src/test/ref/varmodel-ma_mem-5.log b/src/test/ref/varmodel-ma_mem-5.log index 4f53f064c..74c80a1d1 100644 --- a/src/test/ref/varmodel-ma_mem-5.log +++ b/src/test/ref/varmodel-ma_mem-5.log @@ -1,3 +1,4 @@ +Updating intermediate variable memory area to MAIN_MEMORY main::$0 CONTROL FLOW GRAPH SSA @@ -82,15 +83,15 @@ Complete equivalence classes [ main::i ] [ main::$1 ] Allocated mem[1] [ main::i ] -Allocated zp[1]:2 [ main::$1 ] +Allocated mem[1] [ main::$1 ] REGISTER UPLIFT POTENTIAL REGISTERS Statement [0] main::i = 0 [ main::i ] ( [ main::i ] { } ) always clobbers reg byte a Statement [1] SCREEN[main::i] = '*' [ main::i ] ( [ main::i ] { } ) always clobbers reg byte a reg byte y Potential registers mem[1] [ main::i ] : mem[1] , -Potential registers zp[1]:2 [ main::$1 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers mem[1] [ main::$1 ] : mem[1] , reg byte a , reg byte x , reg byte y , REGISTER UPLIFT SCOPES -Uplift Scope [main] 11: zp[1]:2 [ main::$1 ] 9.2: mem[1] [ main::i ] +Uplift Scope [main] 11: mem[1] [ main::$1 ] 9.2: mem[1] [ main::i ] Uplift Scope [] Uplifting [main] best 300 combination reg byte a [ main::$1 ] mem[1] [ main::i ] diff --git a/src/test/ref/varmodel-mem-1.asm b/src/test/ref/varmodel-mem-1.asm new file mode 100644 index 000000000..f951fe6a1 --- /dev/null +++ b/src/test/ref/varmodel-mem-1.asm @@ -0,0 +1,49 @@ +// Test that memory model "mem" affects intermediates + // Commodore 64 PRG executable file +.file [name="varmodel-mem-1.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: { + // A local pointer + .label SCREEN = $400 + ldx #0 + // A local counter + __b1: + // sum(i,i) + txa + jsr sum + // sum(i,i) + sta __1 + txa + jsr sum + // sum(i,i) + // sum(i,i)+sum(i,i) + clc + adc __1 + // *(SCREEN+i) = sum(i,i)+sum(i,i) + // Generates an intermediate + sta SCREEN,x + // for( char i: 0..5 ) + inx + cpx #6 + bne __b1 + // } + rts + .segment Data + __1: .byte 0 +} +.segment Code +// sum(byte register(A) a, byte register(X) b) +sum: { + // a+b + stx.z $ff + clc + adc.z $ff + // } + rts +} diff --git a/src/test/ref/varmodel-mem-1.cfg b/src/test/ref/varmodel-mem-1.cfg new file mode 100644 index 000000000..835990beb --- /dev/null +++ b/src/test/ref/varmodel-mem-1.cfg @@ -0,0 +1,39 @@ + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@3 + [1] main::i#2 = phi( main/0, main::@3/main::i#1 ) + [2] sum::a#0 = main::i#2 + [3] sum::b#0 = main::i#2 + [4] call sum + [5] sum::return#0 = sum::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [6] main::$1 = sum::return#0 + [7] sum::a#1 = main::i#2 + [8] sum::b#1 = main::i#2 + [9] call sum + [10] sum::return#1 = sum::return#2 + to:main::@3 +main::@3: scope:[main] from main::@2 + [11] main::$2 = sum::return#1 + [12] main::$3 = main::$1 + main::$2 + [13] main::SCREEN[main::i#2] = main::$3 + [14] main::i#1 = ++ main::i#2 + [15] if(main::i#1!=6) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@3 + [16] return + to:@return + +byte sum(byte sum::a , byte sum::b) +sum: scope:[sum] from main::@1 main::@2 + [17] sum::b#2 = phi( main::@1/sum::b#0, main::@2/sum::b#1 ) + [17] sum::a#2 = phi( main::@1/sum::a#0, main::@2/sum::a#1 ) + [18] sum::return#2 = sum::a#2 + sum::b#2 + to:sum::@return +sum::@return: scope:[sum] from sum + [19] return + to:@return diff --git a/src/test/ref/varmodel-mem-1.log b/src/test/ref/varmodel-mem-1.log new file mode 100644 index 000000000..a0e0d2128 --- /dev/null +++ b/src/test/ref/varmodel-mem-1.log @@ -0,0 +1,511 @@ +Updating intermediate variable memory area to MAIN_MEMORY main::$1 +Updating intermediate variable memory area to MAIN_MEMORY main::$2 +Updating intermediate variable memory area to MAIN_MEMORY main::$3 +Updating intermediate variable memory area to MAIN_MEMORY main::$4 +Updating intermediate variable memory area to MAIN_MEMORY sum::$0 + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + main::i#0 = 0 + to:main::@1 +main::@1: scope:[main] from main main::@3 + main::i#2 = phi( main/main::i#0, main::@3/main::i#1 ) + main::$0 = main::SCREEN + main::i#2 + sum::a#0 = main::i#2 + sum::b#0 = main::i#2 + call sum + sum::return#0 = sum::return#3 + to:main::@2 +main::@2: scope:[main] from main::@1 + main::i#3 = phi( main::@1/main::i#2 ) + sum::return#4 = phi( main::@1/sum::return#0 ) + main::$1 = sum::return#4 + sum::a#1 = main::i#3 + sum::b#1 = main::i#3 + call sum + sum::return#1 = sum::return#3 + to:main::@3 +main::@3: scope:[main] from main::@2 + main::i#4 = phi( main::@2/main::i#3 ) + sum::return#5 = phi( main::@2/sum::return#1 ) + main::$2 = sum::return#5 + main::$3 = main::$1 + main::$2 + *main::$0 = main::$3 + main::i#1 = main::i#4 + rangenext(0,5) + main::$4 = main::i#1 != rangelast(0,5) + if(main::$4) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@3 + return + to:@return + +byte sum(byte sum::a , byte sum::b) +sum: scope:[sum] from main::@1 main::@2 + sum::b#2 = phi( main::@1/sum::b#0, main::@2/sum::b#1 ) + sum::a#2 = phi( main::@1/sum::a#0, main::@2/sum::a#1 ) + sum::$0 = sum::a#2 + sum::b#2 + sum::return#2 = sum::$0 + to:sum::@return +sum::@return: scope:[sum] from sum + sum::return#6 = phi( sum/sum::return#2 ) + sum::return#3 = sum::return#6 + 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() +byte*~ main::$0 +byte~ main::$1 +byte~ main::$2 +byte~ main::$3 +bool~ main::$4 +constant byte* const main::SCREEN = (byte*)$400 +byte main::i +byte main::i#0 +byte main::i#1 +byte main::i#2 +byte main::i#3 +byte main::i#4 +byte sum(byte sum::a , byte sum::b) +byte~ sum::$0 +byte sum::a +byte sum::a#0 +byte sum::a#1 +byte sum::a#2 +byte sum::b +byte sum::b#0 +byte sum::b#1 +byte sum::b#2 +byte sum::return +byte sum::return#0 +byte sum::return#1 +byte sum::return#2 +byte sum::return#3 +byte sum::return#4 +byte sum::return#5 +byte sum::return#6 + +Simplifying constant pointer cast (byte*) 1024 +Successful SSA optimization PassNCastSimplification +Alias sum::return#0 = sum::return#4 +Alias main::i#2 = main::i#3 main::i#4 +Alias sum::return#1 = sum::return#5 +Alias sum::return#2 = sum::$0 sum::return#6 sum::return#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition main::$4 [17] if(main::i#1!=rangelast(0,5)) goto main::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Resolved ranged next value [15] main::i#1 = ++ main::i#2 to ++ +Resolved ranged comparison value [17] if(main::i#1!=rangelast(0,5)) goto main::@1 to 6 +Converting *(pointer+n) to pointer[n] [14] *main::$0 = main::$3 -- main::SCREEN[main::i#2] +Successful SSA optimization Pass2InlineDerefIdx +Eliminating unused variable main::$0 and assignment [1] main::$0 = main::SCREEN + main::i#2 +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 +Adding number conversion cast (unumber) 6 in [14] if(main::i#1!=6) goto main::@1 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 6 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 6 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inlining constant with var siblings main::i#0 +Constant inlined main::i#0 = 0 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@4(between main::@3 and main::@1) +Adding NOP phi() at start of main +CALL GRAPH +Calls in [main] to sum:6 sum:13 + +Created 3 initial phi equivalence classes +Coalesced [4] sum::a#3 = sum::a#0 +Coalesced [5] sum::b#3 = sum::b#0 +Coalesced [11] sum::a#4 = sum::a#1 +Coalesced [12] sum::b#4 = sum::b#1 +Coalesced [21] main::i#5 = main::i#1 +Coalesced down to 3 phi equivalence classes +Culled Empty Block label main::@4 +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@3 + [1] main::i#2 = phi( main/0, main::@3/main::i#1 ) + [2] sum::a#0 = main::i#2 + [3] sum::b#0 = main::i#2 + [4] call sum + [5] sum::return#0 = sum::return#2 + to:main::@2 +main::@2: scope:[main] from main::@1 + [6] main::$1 = sum::return#0 + [7] sum::a#1 = main::i#2 + [8] sum::b#1 = main::i#2 + [9] call sum + [10] sum::return#1 = sum::return#2 + to:main::@3 +main::@3: scope:[main] from main::@2 + [11] main::$2 = sum::return#1 + [12] main::$3 = main::$1 + main::$2 + [13] main::SCREEN[main::i#2] = main::$3 + [14] main::i#1 = ++ main::i#2 + [15] if(main::i#1!=6) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@3 + [16] return + to:@return + +byte sum(byte sum::a , byte sum::b) +sum: scope:[sum] from main::@1 main::@2 + [17] sum::b#2 = phi( main::@1/sum::b#0, main::@2/sum::b#1 ) + [17] sum::a#2 = phi( main::@1/sum::a#0, main::@2/sum::a#1 ) + [18] sum::return#2 = sum::a#2 + sum::b#2 + to:sum::@return +sum::@return: scope:[sum] from sum + [19] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +byte~ main::$1 3.6666666666666665 +byte~ main::$2 22.0 +byte~ main::$3 22.0 +byte main::i +byte main::i#1 16.5 +byte main::i#2 5.9230769230769225 +byte sum(byte sum::a , byte sum::b) +byte sum::a +byte sum::a#0 11.0 +byte sum::a#1 11.0 +byte sum::a#2 123.0 +byte sum::b +byte sum::b#0 22.0 +byte sum::b#1 22.0 +byte sum::b#2 123.0 +byte sum::return +byte sum::return#0 22.0 +byte sum::return#1 22.0 +byte sum::return#2 30.75 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ sum::a#2 sum::a#0 sum::a#1 ] +[ sum::b#2 sum::b#0 sum::b#1 ] +Added variable sum::return#0 to live range equivalence class [ sum::return#0 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Added variable sum::return#1 to live range equivalence class [ sum::return#1 ] +Added variable main::$2 to live range equivalence class [ main::$2 ] +Added variable main::$3 to live range equivalence class [ main::$3 ] +Added variable sum::return#2 to live range equivalence class [ sum::return#2 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ sum::a#2 sum::a#0 sum::a#1 ] +[ sum::b#2 sum::b#0 sum::b#1 ] +[ sum::return#0 ] +[ main::$1 ] +[ sum::return#1 ] +[ main::$2 ] +[ main::$3 ] +[ sum::return#2 ] +Allocated mem[1] [ main::i#2 main::i#1 ] +Allocated mem[1] [ sum::a#2 sum::a#0 sum::a#1 ] +Allocated mem[1] [ sum::b#2 sum::b#0 sum::b#1 ] +Allocated mem[1] [ sum::return#0 ] +Allocated mem[1] [ main::$1 ] +Allocated mem[1] [ sum::return#1 ] +Allocated mem[1] [ main::$2 ] +Allocated mem[1] [ main::$3 ] +Allocated mem[1] [ sum::return#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [12] main::$3 = main::$1 + main::$2 [ main::i#2 main::$3 ] ( [ main::i#2 main::$3 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for mem[1] [ main::i#2 main::i#1 ] +Statement [18] sum::return#2 = sum::a#2 + sum::b#2 [ sum::return#2 ] ( sum:4 [ main::i#2 sum::return#2 ] { { sum::b#0 = sum::b#2 sum::a#2 sum::a#0 main::i#2 } { sum::return#0 = sum::return#2 } } sum:9 [ main::i#2 main::$1 sum::return#2 ] { { sum::b#1 = sum::b#2 sum::a#2 sum::a#1 main::i#2 } { sum::return#1 = sum::return#2 } } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for mem[1] [ main::$1 ] +Statement [12] main::$3 = main::$1 + main::$2 [ main::i#2 main::$3 ] ( [ main::i#2 main::$3 ] { } ) always clobbers reg byte a +Statement [18] sum::return#2 = sum::a#2 + sum::b#2 [ sum::return#2 ] ( sum:4 [ main::i#2 sum::return#2 ] { { sum::b#0 = sum::b#2 sum::a#2 sum::a#0 main::i#2 } { sum::return#0 = sum::return#2 } } sum:9 [ main::i#2 main::$1 sum::return#2 ] { { sum::b#1 = sum::b#2 sum::a#2 sum::a#1 main::i#2 } { sum::return#1 = sum::return#2 } } ) always clobbers reg byte a +Potential registers mem[1] [ main::i#2 main::i#1 ] : mem[1] , reg byte x , reg byte y , +Potential registers mem[1] [ sum::a#2 sum::a#0 sum::a#1 ] : mem[1] , reg byte a , reg byte x , reg byte y , +Potential registers mem[1] [ sum::b#2 sum::b#0 sum::b#1 ] : mem[1] , reg byte a , reg byte x , reg byte y , +Potential registers mem[1] [ sum::return#0 ] : mem[1] , reg byte a , reg byte x , reg byte y , +Potential registers mem[1] [ main::$1 ] : mem[1] , reg byte x , reg byte y , +Potential registers mem[1] [ sum::return#1 ] : mem[1] , reg byte a , reg byte x , reg byte y , +Potential registers mem[1] [ main::$2 ] : mem[1] , reg byte a , reg byte x , reg byte y , +Potential registers mem[1] [ main::$3 ] : mem[1] , reg byte a , reg byte x , reg byte y , +Potential registers mem[1] [ sum::return#2 ] : mem[1] , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [sum] 167: mem[1] [ sum::b#2 sum::b#0 sum::b#1 ] 145: mem[1] [ sum::a#2 sum::a#0 sum::a#1 ] 30.75: mem[1] [ sum::return#2 ] 22: mem[1] [ sum::return#0 ] 22: mem[1] [ sum::return#1 ] +Uplift Scope [main] 22.42: mem[1] [ main::i#2 main::i#1 ] 22: mem[1] [ main::$2 ] 22: mem[1] [ main::$3 ] 3.67: mem[1] [ main::$1 ] +Uplift Scope [] + +Uplifting [sum] best 980 combination reg byte x [ sum::b#2 sum::b#0 sum::b#1 ] reg byte a [ sum::a#2 sum::a#0 sum::a#1 ] reg byte a [ sum::return#2 ] reg byte a [ sum::return#0 ] mem[1] [ sum::return#1 ] +Limited combination testing to 100 combinations of 1024 possible. +Uplifting [main] best 540 combination reg byte x [ main::i#2 main::i#1 ] reg byte a [ main::$2 ] reg byte a [ main::$3 ] mem[1] [ main::$1 ] +Limited combination testing to 100 combinations of 144 possible. +Uplifting [] best 540 combination +Attempting to uplift remaining variables inmem[1] [ sum::return#1 ] +Uplifting [sum] best 460 combination reg byte a [ sum::return#1 ] +Attempting to uplift remaining variables inmem[1] [ main::$1 ] +Uplifting [main] best 460 combination mem[1] [ main::$1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test that memory model "mem" affects intermediates + // Upstart + // Commodore 64 PRG executable file +.file [name="varmodel-mem-1.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: { + // A local pointer + .label SCREEN = $400 + // [1] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp __b1 + // A local counter + // [1] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + __b1_from___b3: + // [1] phi main::i#2 = main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp __b1 + // main::@1 + __b1: + // [2] sum::a#0 = main::i#2 -- vbuaa=vbuxx + txa + // [3] sum::b#0 = main::i#2 + // [4] call sum + // [17] phi from main::@1 to sum [phi:main::@1->sum] + sum_from___b1: + // [17] phi sum::b#2 = sum::b#0 [phi:main::@1->sum#0] -- register_copy + // [17] phi sum::a#2 = sum::a#0 [phi:main::@1->sum#1] -- register_copy + jsr sum + // [5] sum::return#0 = sum::return#2 + jmp __b2 + // main::@2 + __b2: + // [6] main::$1 = sum::return#0 -- vbum1=vbuaa + sta __1 + // [7] sum::a#1 = main::i#2 -- vbuaa=vbuxx + txa + // [8] sum::b#1 = main::i#2 + // [9] call sum + // [17] phi from main::@2 to sum [phi:main::@2->sum] + sum_from___b2: + // [17] phi sum::b#2 = sum::b#1 [phi:main::@2->sum#0] -- register_copy + // [17] phi sum::a#2 = sum::a#1 [phi:main::@2->sum#1] -- register_copy + jsr sum + // [10] sum::return#1 = sum::return#2 + jmp __b3 + // main::@3 + __b3: + // [11] main::$2 = sum::return#1 + // [12] main::$3 = main::$1 + main::$2 -- vbuaa=vbum1_plus_vbuaa + clc + adc __1 + // [13] main::SCREEN[main::i#2] = main::$3 -- pbuc1_derefidx_vbuxx=vbuaa + // Generates an intermediate + sta SCREEN,x + // [14] main::i#1 = ++ main::i#2 -- vbuxx=_inc_vbuxx + inx + // [15] if(main::i#1!=6) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #6 + bne __b1_from___b3 + jmp __breturn + // main::@return + __breturn: + // [16] return + rts + .segment Data + __1: .byte 0 +} +.segment Code + // sum +// sum(byte register(A) a, byte register(X) b) +sum: { + // [18] sum::return#2 = sum::a#2 + sum::b#2 -- vbuaa=vbuaa_plus_vbuxx + stx.z $ff + clc + adc.z $ff + jmp __breturn + // sum::@return + __breturn: + // [19] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Removing instruction jmp __b3 +Removing instruction jmp __breturn +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing label __b1_from___b3 with __b1 +Removing instruction __b1_from___b3: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __b1_from_main: +Removing instruction sum_from___b1: +Removing instruction __b2: +Removing instruction sum_from___b2: +Removing instruction __b3: +Removing instruction __breturn: +Removing instruction __breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jmp __b1 +Succesful ASM optimization Pass5NextJumpElimination + +FINAL SYMBOL TABLE +void main() +byte~ main::$1 mem[1] 3.6666666666666665 +byte~ main::$2 reg byte a 22.0 +byte~ main::$3 reg byte a 22.0 +constant byte* const main::SCREEN = (byte*) 1024 +byte main::i +byte main::i#1 reg byte x 16.5 +byte main::i#2 reg byte x 5.9230769230769225 +byte sum(byte sum::a , byte sum::b) +byte sum::a +byte sum::a#0 reg byte a 11.0 +byte sum::a#1 reg byte a 11.0 +byte sum::a#2 reg byte a 123.0 +byte sum::b +byte sum::b#0 reg byte x 22.0 +byte sum::b#1 reg byte x 22.0 +byte sum::b#2 reg byte x 123.0 +byte sum::return +byte sum::return#0 reg byte a 22.0 +byte sum::return#1 reg byte a 22.0 +byte sum::return#2 reg byte a 30.75 + +reg byte x [ main::i#2 main::i#1 ] +reg byte a [ sum::a#2 sum::a#0 sum::a#1 ] +reg byte x [ sum::b#2 sum::b#0 sum::b#1 ] +reg byte a [ sum::return#0 ] +mem[1] [ main::$1 ] +reg byte a [ sum::return#1 ] +reg byte a [ main::$2 ] +reg byte a [ main::$3 ] +reg byte a [ sum::return#2 ] + + +FINAL ASSEMBLER +Score: 307 + + // File Comments +// Test that memory model "mem" affects intermediates + // Upstart + // Commodore 64 PRG executable file +.file [name="varmodel-mem-1.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: { + // A local pointer + .label SCREEN = $400 + // [1] phi from main to main::@1 [phi:main->main::@1] + // [1] phi main::i#2 = 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + // A local counter + // [1] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + // [1] phi main::i#2 = main::i#1 [phi:main::@3->main::@1#0] -- register_copy + // main::@1 + __b1: + // sum(i,i) + // [2] sum::a#0 = main::i#2 -- vbuaa=vbuxx + txa + // [3] sum::b#0 = main::i#2 + // [4] call sum + // [17] phi from main::@1 to sum [phi:main::@1->sum] + // [17] phi sum::b#2 = sum::b#0 [phi:main::@1->sum#0] -- register_copy + // [17] phi sum::a#2 = sum::a#0 [phi:main::@1->sum#1] -- register_copy + jsr sum + // sum(i,i) + // [5] sum::return#0 = sum::return#2 + // main::@2 + // [6] main::$1 = sum::return#0 -- vbum1=vbuaa + sta __1 + // [7] sum::a#1 = main::i#2 -- vbuaa=vbuxx + txa + // [8] sum::b#1 = main::i#2 + // [9] call sum + // [17] phi from main::@2 to sum [phi:main::@2->sum] + // [17] phi sum::b#2 = sum::b#1 [phi:main::@2->sum#0] -- register_copy + // [17] phi sum::a#2 = sum::a#1 [phi:main::@2->sum#1] -- register_copy + jsr sum + // sum(i,i) + // [10] sum::return#1 = sum::return#2 + // main::@3 + // [11] main::$2 = sum::return#1 + // sum(i,i)+sum(i,i) + // [12] main::$3 = main::$1 + main::$2 -- vbuaa=vbum1_plus_vbuaa + clc + adc __1 + // *(SCREEN+i) = sum(i,i)+sum(i,i) + // [13] main::SCREEN[main::i#2] = main::$3 -- pbuc1_derefidx_vbuxx=vbuaa + // Generates an intermediate + sta SCREEN,x + // for( char i: 0..5 ) + // [14] main::i#1 = ++ main::i#2 -- vbuxx=_inc_vbuxx + inx + // [15] if(main::i#1!=6) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #6 + bne __b1 + // main::@return + // } + // [16] return + rts + .segment Data + __1: .byte 0 +} +.segment Code + // sum +// sum(byte register(A) a, byte register(X) b) +sum: { + // a+b + // [18] sum::return#2 = sum::a#2 + sum::b#2 -- vbuaa=vbuaa_plus_vbuxx + stx.z $ff + clc + adc.z $ff + // sum::@return + // } + // [19] return + rts +} + // File Data + diff --git a/src/test/ref/varmodel-mem-1.sym b/src/test/ref/varmodel-mem-1.sym new file mode 100644 index 000000000..ef6843975 --- /dev/null +++ b/src/test/ref/varmodel-mem-1.sym @@ -0,0 +1,31 @@ +void main() +byte~ main::$1 mem[1] 3.6666666666666665 +byte~ main::$2 reg byte a 22.0 +byte~ main::$3 reg byte a 22.0 +constant byte* const main::SCREEN = (byte*) 1024 +byte main::i +byte main::i#1 reg byte x 16.5 +byte main::i#2 reg byte x 5.9230769230769225 +byte sum(byte sum::a , byte sum::b) +byte sum::a +byte sum::a#0 reg byte a 11.0 +byte sum::a#1 reg byte a 11.0 +byte sum::a#2 reg byte a 123.0 +byte sum::b +byte sum::b#0 reg byte x 22.0 +byte sum::b#1 reg byte x 22.0 +byte sum::b#2 reg byte x 123.0 +byte sum::return +byte sum::return#0 reg byte a 22.0 +byte sum::return#1 reg byte a 22.0 +byte sum::return#2 reg byte a 30.75 + +reg byte x [ main::i#2 main::i#1 ] +reg byte a [ sum::a#2 sum::a#0 sum::a#1 ] +reg byte x [ sum::b#2 sum::b#0 sum::b#1 ] +reg byte a [ sum::return#0 ] +mem[1] [ main::$1 ] +reg byte a [ sum::return#1 ] +reg byte a [ main::$2 ] +reg byte a [ main::$3 ] +reg byte a [ sum::return#2 ]