From 1f133e34e9e85b2548c11a6d4f94d52c282104d3 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Tue, 17 Aug 2021 07:48:58 +0200 Subject: [PATCH] Added very naive support for zeropage overflow to main memory upon exhaust. TODO - overflow low priority variables. #712 --- .../kickc/passes/Pass4CodeGeneration.java | 14 +- .../kickc/passes/Pass4RegistersFinalize.java | 7 + .../kickc/test/TestProgramsFast.java | 7 +- src/test/kc/zeropage-overflow.c | 25 + src/test/ref/zeropage-exhausted.asm | 47 + src/test/ref/zeropage-exhausted.cfg | 18 + src/test/ref/zeropage-exhausted.log | 302 +++++++ src/test/ref/zeropage-exhausted.sym | 11 + src/test/ref/zeropage-overflow.asm | 134 +++ src/test/ref/zeropage-overflow.cfg | 40 + src/test/ref/zeropage-overflow.log | 816 ++++++++++++++++++ src/test/ref/zeropage-overflow.sym | 41 + 12 files changed, 1451 insertions(+), 11 deletions(-) create mode 100644 src/test/kc/zeropage-overflow.c create mode 100644 src/test/ref/zeropage-exhausted.asm create mode 100644 src/test/ref/zeropage-exhausted.cfg create mode 100644 src/test/ref/zeropage-exhausted.log create mode 100644 src/test/ref/zeropage-exhausted.sym create mode 100644 src/test/ref/zeropage-overflow.asm create mode 100644 src/test/ref/zeropage-overflow.cfg create mode 100644 src/test/ref/zeropage-overflow.log create mode 100644 src/test/ref/zeropage-overflow.sym diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java index dbc724880..0ddf869ce 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4CodeGeneration.java @@ -564,22 +564,16 @@ public class Pass4CodeGeneration { // Add all memory variables Collection scopeVariables = scope.getAllVariables(false); for(Variable variable : scopeVariables) { - if(variable.isMemoryAreaMain()) { + Registers.Register allocation = variable.getAllocation(); + if(variable.getAllocation() instanceof Registers.RegisterMainMem) { + Registers.RegisterMainMem registerMainMem = (Registers.RegisterMainMem) allocation; // Skip PHI masters if(variable.isKindPhiMaster()) continue; // Skip if already added - if(added.contains(variable.getAsmName())) { + if(added.contains(variable.getAsmName())) continue; - } if(variable.isKindLoadStore() || variable.isKindPhiVersion() || variable.isKindIntermediate()) { - Registers.Register allocation = variable.getAllocation(); - if(allocation instanceof Registers.RegisterCpuByte) - continue; - if(!(allocation instanceof Registers.RegisterMainMem)) { - throw new InternalError("Expected main memory allocation " + variable.toString(program)); - } - Registers.RegisterMainMem registerMainMem = (Registers.RegisterMainMem) allocation; final Variable mainVar = program.getScope().getVariable(registerMainMem.getVariableRef()); if(registerMainMem.getAddress() == null) { // Generate into the data segment diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass4RegistersFinalize.java b/src/main/java/dk/camelot64/kickc/passes/Pass4RegistersFinalize.java index 72f2a6925..52101339c 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass4RegistersFinalize.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass4RegistersFinalize.java @@ -175,6 +175,13 @@ public class Pass4RegistersFinalize extends Pass2Base { register = new Registers.RegisterMainMem(variableRef, variable.getType().getSizeBytes(), null); } else { register = allocateNewRegisterZp(variable); + int zp = ((Registers.RegisterZpMem) register).getZp(); + int sizeBytes = variable.getType().getSizeBytes(); + if(zp + sizeBytes > 0x100) { + // Zero-page exhausted - move to main memory instead (TODO: prioritize!) + register = new Registers.RegisterMainMem(variableRef, variable.getType().getSizeBytes(), null); + getLog().append("Zero-page exhausted. Moving allocation to main memory "+variable.toString()); + } } equivalenceClass.setRegister(register); if(before == null || !before.equals(register.toString())) { diff --git a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java index c7760f1d8..84fc6eded 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java +++ b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java @@ -1816,9 +1816,14 @@ public class TestProgramsFast extends TestPrograms { compileAndCompare("array-16bit-lookup.c"); } + @Test + public void testZeropageOverflow() throws IOException { + compileAndCompare("zeropage-overflow.c"); + } + @Test public void testZeropageExhausted() throws IOException { - assertError("zeropage-exhausted.c", "Variables used in program do not fit on zeropage", false); + compileAndCompare("zeropage-exhausted.c"); } @Test diff --git a/src/test/kc/zeropage-overflow.c b/src/test/kc/zeropage-overflow.c new file mode 100644 index 000000000..cd696071a --- /dev/null +++ b/src/test/kc/zeropage-overflow.c @@ -0,0 +1,25 @@ +// Tests that variables overflow to main memory when zeropage is exhausted + +// Tell the compiler to use zeropage +#pragma var_model(ssa_zp) + +// Start by reserving most of zeropage (254 bytes) +#pragma zp_reserve(1..250) + +// And then allocate a bunch of variables +void main() { + int* const SCREEN = (int*)0x0400; + + int a, b, c, d, e, f, g, h; + + for(char i=0;i<10;i++) { + SCREEN[i] = a++; + SCREEN[i] = b++; + SCREEN[i] = c++; + SCREEN[i] = d++; + SCREEN[i] = e++; + SCREEN[i] = f++; + SCREEN[i] = g++; + SCREEN[i] = h++; + } +} diff --git a/src/test/ref/zeropage-exhausted.asm b/src/test/ref/zeropage-exhausted.asm new file mode 100644 index 000000000..16598cf23 --- /dev/null +++ b/src/test/ref/zeropage-exhausted.asm @@ -0,0 +1,47 @@ +// Tests warning when running out of zeropage-addresses for variables + // Commodore 64 PRG executable file +.file [name="zeropage-exhausted.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 +// And then allocate a 2-byte-variable +main: { + .label SCREEN = $400 + lda #<0 + sta i + sta i+1 + __b1: + // for(__zp int i=0;i<10;i++) + lda i+1 + bmi __b2 + cmp #>$a + bcc __b2 + bne !+ + lda i + cmp #<$a + bcc __b2 + !: + // } + rts + __b2: + // SCREEN[(char)i] = i + lda i + asl + tay + lda i + sta SCREEN,y + lda i+1 + sta SCREEN+1,y + // for(__zp int i=0;i<10;i++) + inc i + bne !+ + inc i+1 + !: + jmp __b1 + .segment Data + i: .word 0 +} diff --git a/src/test/ref/zeropage-exhausted.cfg b/src/test/ref/zeropage-exhausted.cfg new file mode 100644 index 000000000..49cd3a601 --- /dev/null +++ b/src/test/ref/zeropage-exhausted.cfg @@ -0,0 +1,18 @@ + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] main::i#2 = phi( main/0, main::@2/main::i#1 ) + [2] if(main::i#2<$a) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] main::$2 = (char)main::i#2 + [5] main::$1 = main::$2 << 1 + [6] main::SCREEN[main::$1] = main::i#2 + [7] main::i#1 = ++ main::i#2 + to:main::@1 diff --git a/src/test/ref/zeropage-exhausted.log b/src/test/ref/zeropage-exhausted.log new file mode 100644 index 000000000..f5298f246 --- /dev/null +++ b/src/test/ref/zeropage-exhausted.log @@ -0,0 +1,302 @@ + +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::@2 + main::i#2 = phi( main/main::i#0, main::@2/main::i#1 ) + main::$0 = main::i#2 < $a + if(main::$0) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + main::i#3 = phi( main::@1/main::i#2 ) + main::$2 = (char)main::i#3 + main::$1 = main::$2 * SIZEOF_INT + main::SCREEN[main::$1] = main::i#3 + main::i#1 = ++ main::i#3 + to:main::@1 +main::@return: scope:[main] from main::@1 + 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 SIZEOF_INT = 2 +void __start() +void main() +bool main::$0 +char main::$1 +char main::$2 +__constant int * const main::SCREEN = (int *)$400 +int main::i +int main::i#0 +int main::i#1 +int main::i#2 +int main::i#3 + +Adding number conversion cast (snumber) $a in main::$0 = main::i#2 < $a +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (int *) 1024 +Simplifying constant integer cast $a +Successful SSA optimization PassNCastSimplification +Finalized signed number type (signed char) $a +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias main::i#2 = main::i#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition main::$0 [3] if(main::i#2<$a) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +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 +Rewriting multiplication to use shift [3] main::$1 = main::$2 * SIZEOF_INT +Successful SSA optimization Pass2MultiplyToShiftRewriting +Inlining constant with var siblings main::i#0 +Constant inlined main::i#0 = 0 +Successful SSA optimization Pass2ConstantInlining +Eliminating unused constant SIZEOF_INT +Successful SSA optimization PassNEliminateUnusedVars +Adding NOP phi() at start of main +CALL GRAPH + +Created 1 initial phi equivalence classes +Coalesced [8] main::i#4 = main::i#1 +Coalesced down to 1 phi equivalence classes +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::@2 + [1] main::i#2 = phi( main/0, main::@2/main::i#1 ) + [2] if(main::i#2<$a) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] main::$2 = (char)main::i#2 + [5] main::$1 = main::$2 << 1 + [6] main::SCREEN[main::$1] = main::i#2 + [7] main::i#1 = ++ main::i#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +void main() +char main::$1 // 22.0 +char main::$2 // 22.0 +int main::i +int main::i#1 // 22.0 +int main::i#2 // 8.8 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Added variable main::$2 to live range equivalence class [ main::$2 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ main::$2 ] +[ main::$1 ] +Zero-page exhausted. Moving allocation to main memory main::i#2 +Allocated mem[2] [ main::i#2 main::i#1 ] +Zero-page exhausted. Moving allocation to main memory main::$2 +Allocated mem[1] [ main::$2 ] +Zero-page exhausted. Moving allocation to main memory main::$1 +Allocated mem[1] [ main::$1 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [2] if(main::i#2<$a) goto main::@2 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a +Statement [4] main::$2 = (char)main::i#2 [ main::i#2 main::$2 ] ( [ main::i#2 main::$2 ] { } ) always clobbers reg byte a +Statement [5] main::$1 = main::$2 << 1 [ main::i#2 main::$1 ] ( [ main::i#2 main::$1 ] { } ) always clobbers reg byte a +Statement [6] main::SCREEN[main::$1] = main::i#2 [ main::i#2 ] ( [ main::i#2 ] { } ) always clobbers reg byte a +Potential registers mem[2] [ main::i#2 main::i#1 ] : mem[2] , +Potential registers mem[1] [ main::$2 ] : mem[1] , 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] 30.8: mem[2] [ main::i#2 main::i#1 ] 22: mem[1] [ main::$2 ] 22: mem[1] [ main::$1 ] +Uplift Scope [] + +Uplifting [main] best 841 combination mem[2] [ main::i#2 main::i#1 ] reg byte a [ main::$2 ] reg byte a [ main::$1 ] +Uplifting [] best 841 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Tests warning when running out of zeropage-addresses for variables + // Upstart + // Commodore 64 PRG executable file +.file [name="zeropage-exhausted.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 +// And then allocate a 2-byte-variable +main: { + .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] -- vwsm1=vwsc1 + lda #<0 + sta i + lda #>0 + sta i+1 + jmp __b1 + // main::@1 + __b1: + // [2] if(main::i#2<$a) goto main::@2 -- vwsm1_lt_vwuc1_then_la1 + lda i+1 + bmi __b2 + cmp #>$a + bcc __b2 + bne !+ + lda i + cmp #<$a + bcc __b2 + !: + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // main::@2 + __b2: + // [4] main::$2 = (char)main::i#2 -- vbuaa=_byte_vwsm1 + lda i + // [5] main::$1 = main::$2 << 1 -- vbuaa=vbuaa_rol_1 + asl + // [6] main::SCREEN[main::$1] = main::i#2 -- pwsc1_derefidx_vbuaa=vwsm1 + tay + lda i + sta SCREEN,y + lda i+1 + sta SCREEN+1,y + // [7] main::i#1 = ++ main::i#2 -- vwsm1=_inc_vwsm1 + inc i + bne !+ + inc i+1 + !: + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + __b1_from___b2: + // [1] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp __b1 + .segment Data + i: .word 0 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction lda #>0 +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Removing instruction __b1_from_main: +Removing instruction __breturn: +Removing instruction __b1_from___b2: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +void main() +char main::$1 // reg byte a 22.0 +char main::$2 // reg byte a 22.0 +__constant int * const main::SCREEN = (int *) 1024 +int main::i +int main::i#1 // i mem[2] 22.0 +int main::i#2 // i mem[2] 8.8 + +mem[2] [ main::i#2 main::i#1 ] +reg byte a [ main::$2 ] +reg byte a [ main::$1 ] + + +FINAL ASSEMBLER +Score: 761 + + // File Comments +// Tests warning when running out of zeropage-addresses for variables + // Upstart + // Commodore 64 PRG executable file +.file [name="zeropage-exhausted.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 +// And then allocate a 2-byte-variable +main: { + .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] -- vwsm1=vwsc1 + lda #<0 + sta i + sta i+1 + // main::@1 + __b1: + // for(__zp int i=0;i<10;i++) + // [2] if(main::i#2<$a) goto main::@2 -- vwsm1_lt_vwuc1_then_la1 + lda i+1 + bmi __b2 + cmp #>$a + bcc __b2 + bne !+ + lda i + cmp #<$a + bcc __b2 + !: + // main::@return + // } + // [3] return + rts + // main::@2 + __b2: + // SCREEN[(char)i] = i + // [4] main::$2 = (char)main::i#2 -- vbuaa=_byte_vwsm1 + lda i + // [5] main::$1 = main::$2 << 1 -- vbuaa=vbuaa_rol_1 + asl + // [6] main::SCREEN[main::$1] = main::i#2 -- pwsc1_derefidx_vbuaa=vwsm1 + tay + lda i + sta SCREEN,y + lda i+1 + sta SCREEN+1,y + // for(__zp int i=0;i<10;i++) + // [7] main::i#1 = ++ main::i#2 -- vwsm1=_inc_vwsm1 + inc i + bne !+ + inc i+1 + !: + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [1] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp __b1 + .segment Data + i: .word 0 +} + // File Data + diff --git a/src/test/ref/zeropage-exhausted.sym b/src/test/ref/zeropage-exhausted.sym new file mode 100644 index 000000000..1020a857f --- /dev/null +++ b/src/test/ref/zeropage-exhausted.sym @@ -0,0 +1,11 @@ +void main() +char main::$1 // reg byte a 22.0 +char main::$2 // reg byte a 22.0 +__constant int * const main::SCREEN = (int *) 1024 +int main::i +int main::i#1 // i mem[2] 22.0 +int main::i#2 // i mem[2] 8.8 + +mem[2] [ main::i#2 main::i#1 ] +reg byte a [ main::$2 ] +reg byte a [ main::$1 ] diff --git a/src/test/ref/zeropage-overflow.asm b/src/test/ref/zeropage-overflow.asm new file mode 100644 index 000000000..decd73b72 --- /dev/null +++ b/src/test/ref/zeropage-overflow.asm @@ -0,0 +1,134 @@ +// Tests that variables overflow to main memory when zeropage is exhausted + // Commodore 64 PRG executable file +.file [name="zeropage-overflow.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 +// And then allocate a bunch of variables +main: { + .label SCREEN = $400 + .label a = $fb + .label b = $fd + lda #<0 + sta h + sta h+1 + sta g + sta g+1 + sta f + sta f+1 + sta e + sta e+1 + sta d + sta d+1 + sta c + sta c+1 + sta.z b + sta.z b+1 + sta.z a + sta.z a+1 + tay + __b1: + // for(char i=0;i<10;i++) + cpy #$a + bcc __b2 + // } + rts + __b2: + // SCREEN[i] = a++ + tya + asl + tax + lda.z a + sta SCREEN,x + lda.z a+1 + sta SCREEN+1,x + // SCREEN[i] = a++; + inc.z a + bne !+ + inc.z a+1 + !: + // SCREEN[i] = b++ + lda.z b + sta SCREEN,x + lda.z b+1 + sta SCREEN+1,x + // SCREEN[i] = b++; + inc.z b + bne !+ + inc.z b+1 + !: + // SCREEN[i] = c++ + lda c + sta SCREEN,x + lda c+1 + sta SCREEN+1,x + // SCREEN[i] = c++; + inc c + bne !+ + inc c+1 + !: + // SCREEN[i] = d++ + lda d + sta SCREEN,x + lda d+1 + sta SCREEN+1,x + // SCREEN[i] = d++; + inc d + bne !+ + inc d+1 + !: + // SCREEN[i] = e++ + lda e + sta SCREEN,x + lda e+1 + sta SCREEN+1,x + // SCREEN[i] = e++; + inc e + bne !+ + inc e+1 + !: + // SCREEN[i] = f++ + lda f + sta SCREEN,x + lda f+1 + sta SCREEN+1,x + // SCREEN[i] = f++; + inc f + bne !+ + inc f+1 + !: + // SCREEN[i] = g++ + lda g + sta SCREEN,x + lda g+1 + sta SCREEN+1,x + // SCREEN[i] = g++; + inc g + bne !+ + inc g+1 + !: + // SCREEN[i] = h++ + lda h + sta SCREEN,x + lda h+1 + sta SCREEN+1,x + // SCREEN[i] = h++; + inc h + bne !+ + inc h+1 + !: + // for(char i=0;i<10;i++) + iny + jmp __b1 + .segment Data + c: .word 0 + d: .word 0 + e: .word 0 + f: .word 0 + g: .word 0 + h: .word 0 +} diff --git a/src/test/ref/zeropage-overflow.cfg b/src/test/ref/zeropage-overflow.cfg new file mode 100644 index 000000000..148e880f1 --- /dev/null +++ b/src/test/ref/zeropage-overflow.cfg @@ -0,0 +1,40 @@ + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] main::h#2 = phi( main/0, main::@2/main::h#1 ) + [1] main::g#2 = phi( main/0, main::@2/main::g#1 ) + [1] main::f#2 = phi( main/0, main::@2/main::f#1 ) + [1] main::e#2 = phi( main/0, main::@2/main::e#1 ) + [1] main::d#2 = phi( main/0, main::@2/main::d#1 ) + [1] main::c#2 = phi( main/0, main::@2/main::c#1 ) + [1] main::b#2 = phi( main/0, main::@2/main::b#1 ) + [1] main::a#2 = phi( main/0, main::@2/main::a#1 ) + [1] main::i#2 = phi( main/0, main::@2/main::i#1 ) + [2] if(main::i#2<$a) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] main::$2 = main::i#2 << 1 + [5] main::SCREEN[main::$2] = main::a#2 + [6] main::a#1 = ++ main::a#2 + [7] main::SCREEN[main::$2] = main::b#2 + [8] main::b#1 = ++ main::b#2 + [9] main::SCREEN[main::$2] = main::c#2 + [10] main::c#1 = ++ main::c#2 + [11] main::SCREEN[main::$2] = main::d#2 + [12] main::d#1 = ++ main::d#2 + [13] main::SCREEN[main::$2] = main::e#2 + [14] main::e#1 = ++ main::e#2 + [15] main::SCREEN[main::$2] = main::f#2 + [16] main::f#1 = ++ main::f#2 + [17] main::SCREEN[main::$2] = main::g#2 + [18] main::g#1 = ++ main::g#2 + [19] main::SCREEN[main::$2] = main::h#2 + [20] main::h#1 = ++ main::h#2 + [21] main::i#1 = ++ main::i#2 + to:main::@1 diff --git a/src/test/ref/zeropage-overflow.log b/src/test/ref/zeropage-overflow.log new file mode 100644 index 000000000..da7dea59e --- /dev/null +++ b/src/test/ref/zeropage-overflow.log @@ -0,0 +1,816 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + main::a#0 = 0 + main::b#0 = 0 + main::c#0 = 0 + main::d#0 = 0 + main::e#0 = 0 + main::f#0 = 0 + main::g#0 = 0 + main::h#0 = 0 + main::i#0 = 0 + to:main::@1 +main::@1: scope:[main] from main main::@2 + main::h#3 = phi( main/main::h#0, main::@2/main::h#1 ) + main::g#3 = phi( main/main::g#0, main::@2/main::g#1 ) + main::f#3 = phi( main/main::f#0, main::@2/main::f#1 ) + main::e#3 = phi( main/main::e#0, main::@2/main::e#1 ) + main::d#3 = phi( main/main::d#0, main::@2/main::d#1 ) + main::c#3 = phi( main/main::c#0, main::@2/main::c#1 ) + main::b#3 = phi( main/main::b#0, main::@2/main::b#1 ) + main::a#3 = phi( main/main::a#0, main::@2/main::a#1 ) + main::i#2 = phi( main/main::i#0, main::@2/main::i#1 ) + main::$0 = main::i#2 < $a + if(main::$0) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + main::h#2 = phi( main::@1/main::h#3 ) + main::g#2 = phi( main::@1/main::g#3 ) + main::f#2 = phi( main::@1/main::f#3 ) + main::e#2 = phi( main::@1/main::e#3 ) + main::d#2 = phi( main::@1/main::d#3 ) + main::c#2 = phi( main::@1/main::c#3 ) + main::b#2 = phi( main::@1/main::b#3 ) + main::a#2 = phi( main::@1/main::a#3 ) + main::i#3 = phi( main::@1/main::i#2 ) + main::$1 = main::i#3 * SIZEOF_INT + main::SCREEN[main::$1] = main::a#2 + main::a#1 = ++ main::a#2 + main::$2 = main::i#3 * SIZEOF_INT + main::SCREEN[main::$2] = main::b#2 + main::b#1 = ++ main::b#2 + main::$3 = main::i#3 * SIZEOF_INT + main::SCREEN[main::$3] = main::c#2 + main::c#1 = ++ main::c#2 + main::$4 = main::i#3 * SIZEOF_INT + main::SCREEN[main::$4] = main::d#2 + main::d#1 = ++ main::d#2 + main::$5 = main::i#3 * SIZEOF_INT + main::SCREEN[main::$5] = main::e#2 + main::e#1 = ++ main::e#2 + main::$6 = main::i#3 * SIZEOF_INT + main::SCREEN[main::$6] = main::f#2 + main::f#1 = ++ main::f#2 + main::$7 = main::i#3 * SIZEOF_INT + main::SCREEN[main::$7] = main::g#2 + main::g#1 = ++ main::g#2 + main::$8 = main::i#3 * SIZEOF_INT + main::SCREEN[main::$8] = main::h#2 + main::h#1 = ++ main::h#2 + main::i#1 = ++ main::i#3 + to:main::@1 +main::@return: scope:[main] from main::@1 + 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 SIZEOF_INT = 2 +void __start() +void main() +bool main::$0 +char main::$1 +char main::$2 +char main::$3 +char main::$4 +char main::$5 +char main::$6 +char main::$7 +char main::$8 +__constant int * const main::SCREEN = (int *)$400 +int main::a +int main::a#0 +int main::a#1 +int main::a#2 +int main::a#3 +int main::b +int main::b#0 +int main::b#1 +int main::b#2 +int main::b#3 +int main::c +int main::c#0 +int main::c#1 +int main::c#2 +int main::c#3 +int main::d +int main::d#0 +int main::d#1 +int main::d#2 +int main::d#3 +int main::e +int main::e#0 +int main::e#1 +int main::e#2 +int main::e#3 +int main::f +int main::f#0 +int main::f#1 +int main::f#2 +int main::f#3 +int main::g +int main::g#0 +int main::g#1 +int main::g#2 +int main::g#3 +int main::h +int main::h#0 +int main::h#1 +int main::h#2 +int main::h#3 +char main::i +char main::i#0 +char main::i#1 +char main::i#2 +char main::i#3 + +Adding number conversion cast (unumber) $a in main::$0 = main::i#2 < $a +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (int *) 1024 +Simplifying constant integer cast $a +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (char) $a +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias main::i#2 = main::i#3 +Alias main::a#2 = main::a#3 +Alias main::b#2 = main::b#3 +Alias main::c#2 = main::c#3 +Alias main::d#2 = main::d#3 +Alias main::e#2 = main::e#3 +Alias main::f#2 = main::f#3 +Alias main::g#2 = main::g#3 +Alias main::h#2 = main::h#3 +Successful SSA optimization Pass2AliasElimination +Identified duplicate assignment right side [16] main::$2 = main::i#2 * SIZEOF_INT +Identified duplicate assignment right side [19] main::$3 = main::i#2 * SIZEOF_INT +Identified duplicate assignment right side [22] main::$4 = main::i#2 * SIZEOF_INT +Identified duplicate assignment right side [25] main::$5 = main::i#2 * SIZEOF_INT +Identified duplicate assignment right side [28] main::$6 = main::i#2 * SIZEOF_INT +Identified duplicate assignment right side [31] main::$7 = main::i#2 * SIZEOF_INT +Identified duplicate assignment right side [34] main::$8 = main::i#2 * SIZEOF_INT +Successful SSA optimization Pass2DuplicateRValueIdentification +Simple Condition main::$0 [11] if(main::i#2<$a) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant main::a#0 = 0 +Constant main::b#0 = 0 +Constant main::c#0 = 0 +Constant main::d#0 = 0 +Constant main::e#0 = 0 +Constant main::f#0 = 0 +Constant main::g#0 = 0 +Constant main::h#0 = 0 +Constant main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +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 main::$2 = main::$1 main::$3 main::$4 main::$5 main::$6 main::$7 main::$8 +Successful SSA optimization Pass2AliasElimination +Rewriting multiplication to use shift [2] main::$2 = main::i#2 * SIZEOF_INT +Successful SSA optimization Pass2MultiplyToShiftRewriting +Inlining constant with var siblings main::a#0 +Inlining constant with var siblings main::b#0 +Inlining constant with var siblings main::c#0 +Inlining constant with var siblings main::d#0 +Inlining constant with var siblings main::e#0 +Inlining constant with var siblings main::f#0 +Inlining constant with var siblings main::g#0 +Inlining constant with var siblings main::h#0 +Inlining constant with var siblings main::i#0 +Constant inlined main::a#0 = 0 +Constant inlined main::c#0 = 0 +Constant inlined main::b#0 = 0 +Constant inlined main::i#0 = 0 +Constant inlined main::h#0 = 0 +Constant inlined main::e#0 = 0 +Constant inlined main::d#0 = 0 +Constant inlined main::g#0 = 0 +Constant inlined main::f#0 = 0 +Successful SSA optimization Pass2ConstantInlining +Eliminating unused constant SIZEOF_INT +Successful SSA optimization PassNEliminateUnusedVars +Adding NOP phi() at start of main +CALL GRAPH + +Created 9 initial phi equivalence classes +Coalesced [22] main::i#4 = main::i#1 +Coalesced [23] main::a#4 = main::a#1 +Coalesced [24] main::b#4 = main::b#1 +Coalesced [25] main::c#4 = main::c#1 +Coalesced [26] main::d#4 = main::d#1 +Coalesced [27] main::e#4 = main::e#1 +Coalesced [28] main::f#4 = main::f#1 +Coalesced [29] main::g#4 = main::g#1 +Coalesced [30] main::h#4 = main::h#1 +Coalesced down to 9 phi equivalence classes +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::@2 + [1] main::h#2 = phi( main/0, main::@2/main::h#1 ) + [1] main::g#2 = phi( main/0, main::@2/main::g#1 ) + [1] main::f#2 = phi( main/0, main::@2/main::f#1 ) + [1] main::e#2 = phi( main/0, main::@2/main::e#1 ) + [1] main::d#2 = phi( main/0, main::@2/main::d#1 ) + [1] main::c#2 = phi( main/0, main::@2/main::c#1 ) + [1] main::b#2 = phi( main/0, main::@2/main::b#1 ) + [1] main::a#2 = phi( main/0, main::@2/main::a#1 ) + [1] main::i#2 = phi( main/0, main::@2/main::i#1 ) + [2] if(main::i#2<$a) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 + [4] main::$2 = main::i#2 << 1 + [5] main::SCREEN[main::$2] = main::a#2 + [6] main::a#1 = ++ main::a#2 + [7] main::SCREEN[main::$2] = main::b#2 + [8] main::b#1 = ++ main::b#2 + [9] main::SCREEN[main::$2] = main::c#2 + [10] main::c#1 = ++ main::c#2 + [11] main::SCREEN[main::$2] = main::d#2 + [12] main::d#1 = ++ main::d#2 + [13] main::SCREEN[main::$2] = main::e#2 + [14] main::e#1 = ++ main::e#2 + [15] main::SCREEN[main::$2] = main::f#2 + [16] main::f#1 = ++ main::f#2 + [17] main::SCREEN[main::$2] = main::g#2 + [18] main::g#1 = ++ main::g#2 + [19] main::SCREEN[main::$2] = main::h#2 + [20] main::h#1 = ++ main::h#2 + [21] main::i#1 = ++ main::i#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +void main() +char main::$2 // 6.6 +int main::a +int main::a#1 // 1.375 +int main::a#2 // 8.25 +int main::b +int main::b#1 // 1.5714285714285714 +int main::b#2 // 5.5 +int main::c +int main::c#1 // 1.8333333333333333 +int main::c#2 // 4.125 +int main::d +int main::d#1 // 2.2 +int main::d#2 // 3.3000000000000003 +int main::e +int main::e#1 // 2.75 +int main::e#2 // 2.75 +int main::f +int main::f#1 // 3.6666666666666665 +int main::f#2 // 2.357142857142857 +int main::g +int main::g#1 // 5.5 +int main::g#2 // 2.0625 +int main::h +int main::h#1 // 11.0 +int main::h#2 // 1.8333333333333335 +char main::i +char main::i#1 // 22.0 +char main::i#2 // 2.3157894736842106 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ main::a#2 main::a#1 ] +[ main::b#2 main::b#1 ] +[ main::c#2 main::c#1 ] +[ main::d#2 main::d#1 ] +[ main::e#2 main::e#1 ] +[ main::f#2 main::f#1 ] +[ main::g#2 main::g#1 ] +[ main::h#2 main::h#1 ] +Added variable main::$2 to live range equivalence class [ main::$2 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ main::a#2 main::a#1 ] +[ main::b#2 main::b#1 ] +[ main::c#2 main::c#1 ] +[ main::d#2 main::d#1 ] +[ main::e#2 main::e#1 ] +[ main::f#2 main::f#1 ] +[ main::g#2 main::g#1 ] +[ main::h#2 main::h#1 ] +[ main::$2 ] +Allocated zp[1]:251 [ main::i#2 main::i#1 ] +Allocated zp[2]:252 [ main::a#2 main::a#1 ] +Allocated zp[2]:254 [ main::b#2 main::b#1 ] +Zero-page exhausted. Moving allocation to main memory main::c#2 +Allocated mem[2] [ main::c#2 main::c#1 ] +Zero-page exhausted. Moving allocation to main memory main::d#2 +Allocated mem[2] [ main::d#2 main::d#1 ] +Zero-page exhausted. Moving allocation to main memory main::e#2 +Allocated mem[2] [ main::e#2 main::e#1 ] +Zero-page exhausted. Moving allocation to main memory main::f#2 +Allocated mem[2] [ main::f#2 main::f#1 ] +Zero-page exhausted. Moving allocation to main memory main::g#2 +Allocated mem[2] [ main::g#2 main::g#1 ] +Zero-page exhausted. Moving allocation to main memory main::h#2 +Allocated mem[2] [ main::h#2 main::h#1 ] +Zero-page exhausted. Moving allocation to main memory main::$2 +Allocated mem[1] [ main::$2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] main::$2 = main::i#2 << 1 [ main::i#2 main::a#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::$2 ] ( [ main::i#2 main::a#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::$2 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:251 [ main::i#2 main::i#1 ] +Statement [5] main::SCREEN[main::$2] = main::a#2 [ main::i#2 main::a#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::$2 ] ( [ main::i#2 main::a#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::$2 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for mem[1] [ main::$2 ] +Statement [7] main::SCREEN[main::$2] = main::b#2 [ main::i#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::$2 ] ( [ main::i#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::$2 ] { } ) always clobbers reg byte a +Statement [9] main::SCREEN[main::$2] = main::c#2 [ main::i#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::$2 ] ( [ main::i#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::$2 ] { } ) always clobbers reg byte a +Statement [11] main::SCREEN[main::$2] = main::d#2 [ main::i#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::$2 ] ( [ main::i#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::$2 ] { } ) always clobbers reg byte a +Statement [13] main::SCREEN[main::$2] = main::e#2 [ main::i#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::$2 ] ( [ main::i#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::$2 ] { } ) always clobbers reg byte a +Statement [15] main::SCREEN[main::$2] = main::f#2 [ main::i#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::$2 ] ( [ main::i#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::$2 ] { } ) always clobbers reg byte a +Statement [17] main::SCREEN[main::$2] = main::g#2 [ main::i#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::f#1 main::$2 ] ( [ main::i#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::f#1 main::$2 ] { } ) always clobbers reg byte a +Statement [19] main::SCREEN[main::$2] = main::h#2 [ main::i#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::f#1 main::g#1 ] ( [ main::i#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::f#1 main::g#1 ] { } ) always clobbers reg byte a +Statement [4] main::$2 = main::i#2 << 1 [ main::i#2 main::a#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::$2 ] ( [ main::i#2 main::a#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::$2 ] { } ) always clobbers reg byte a +Statement [5] main::SCREEN[main::$2] = main::a#2 [ main::i#2 main::a#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::$2 ] ( [ main::i#2 main::a#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::$2 ] { } ) always clobbers reg byte a +Statement [7] main::SCREEN[main::$2] = main::b#2 [ main::i#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::$2 ] ( [ main::i#2 main::b#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::$2 ] { } ) always clobbers reg byte a +Statement [9] main::SCREEN[main::$2] = main::c#2 [ main::i#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::$2 ] ( [ main::i#2 main::c#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::$2 ] { } ) always clobbers reg byte a +Statement [11] main::SCREEN[main::$2] = main::d#2 [ main::i#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::$2 ] ( [ main::i#2 main::d#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::$2 ] { } ) always clobbers reg byte a +Statement [13] main::SCREEN[main::$2] = main::e#2 [ main::i#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::$2 ] ( [ main::i#2 main::e#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::$2 ] { } ) always clobbers reg byte a +Statement [15] main::SCREEN[main::$2] = main::f#2 [ main::i#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::$2 ] ( [ main::i#2 main::f#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::$2 ] { } ) always clobbers reg byte a +Statement [17] main::SCREEN[main::$2] = main::g#2 [ main::i#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::f#1 main::$2 ] ( [ main::i#2 main::g#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::f#1 main::$2 ] { } ) always clobbers reg byte a +Statement [19] main::SCREEN[main::$2] = main::h#2 [ main::i#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::f#1 main::g#1 ] ( [ main::i#2 main::h#2 main::a#1 main::b#1 main::c#1 main::d#1 main::e#1 main::f#1 main::g#1 ] { } ) always clobbers reg byte a +Potential registers zp[1]:251 [ main::i#2 main::i#1 ] : zp[1]:251 , reg byte x , reg byte y , +Potential registers zp[2]:252 [ main::a#2 main::a#1 ] : zp[2]:252 , +Potential registers zp[2]:254 [ main::b#2 main::b#1 ] : zp[2]:254 , +Potential registers mem[2] [ main::c#2 main::c#1 ] : mem[2] , +Potential registers mem[2] [ main::d#2 main::d#1 ] : mem[2] , +Potential registers mem[2] [ main::e#2 main::e#1 ] : mem[2] , +Potential registers mem[2] [ main::f#2 main::f#1 ] : mem[2] , +Potential registers mem[2] [ main::g#2 main::g#1 ] : mem[2] , +Potential registers mem[2] [ main::h#2 main::h#1 ] : mem[2] , +Potential registers mem[1] [ main::$2 ] : mem[1] , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 24.32: zp[1]:251 [ main::i#2 main::i#1 ] 12.83: mem[2] [ main::h#2 main::h#1 ] 9.62: zp[2]:252 [ main::a#2 main::a#1 ] 7.56: mem[2] [ main::g#2 main::g#1 ] 7.07: zp[2]:254 [ main::b#2 main::b#1 ] 6.6: mem[1] [ main::$2 ] 6.02: mem[2] [ main::f#2 main::f#1 ] 5.96: mem[2] [ main::c#2 main::c#1 ] 5.5: mem[2] [ main::d#2 main::d#1 ] 5.5: mem[2] [ main::e#2 main::e#1 ] +Uplift Scope [] + +Uplifting [main] best 3681 combination reg byte y [ main::i#2 main::i#1 ] mem[2] [ main::h#2 main::h#1 ] zp[2]:252 [ main::a#2 main::a#1 ] mem[2] [ main::g#2 main::g#1 ] zp[2]:254 [ main::b#2 main::b#1 ] reg byte x [ main::$2 ] mem[2] [ main::f#2 main::f#1 ] mem[2] [ main::c#2 main::c#1 ] mem[2] [ main::d#2 main::d#1 ] mem[2] [ main::e#2 main::e#1 ] +Uplifting [] best 3681 combination +Allocated (was zp[2]:252) zp[2]:251 [ main::a#2 main::a#1 ] +Allocated (was zp[2]:254) zp[2]:253 [ main::b#2 main::b#1 ] + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Tests that variables overflow to main memory when zeropage is exhausted + // Upstart + // Commodore 64 PRG executable file +.file [name="zeropage-overflow.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 +// And then allocate a bunch of variables +main: { + .label SCREEN = $400 + .label a = $fb + .label b = $fd + // [1] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [1] phi main::h#2 = 0 [phi:main->main::@1#0] -- vwsm1=vwsc1 + lda #<0 + sta h + lda #>0 + sta h+1 + // [1] phi main::g#2 = 0 [phi:main->main::@1#1] -- vwsm1=vwsc1 + lda #<0 + sta g + lda #>0 + sta g+1 + // [1] phi main::f#2 = 0 [phi:main->main::@1#2] -- vwsm1=vwsc1 + lda #<0 + sta f + lda #>0 + sta f+1 + // [1] phi main::e#2 = 0 [phi:main->main::@1#3] -- vwsm1=vwsc1 + lda #<0 + sta e + lda #>0 + sta e+1 + // [1] phi main::d#2 = 0 [phi:main->main::@1#4] -- vwsm1=vwsc1 + lda #<0 + sta d + lda #>0 + sta d+1 + // [1] phi main::c#2 = 0 [phi:main->main::@1#5] -- vwsm1=vwsc1 + lda #<0 + sta c + lda #>0 + sta c+1 + // [1] phi main::b#2 = 0 [phi:main->main::@1#6] -- vwsz1=vwsc1 + lda #<0 + sta.z b + lda #>0 + sta.z b+1 + // [1] phi main::a#2 = 0 [phi:main->main::@1#7] -- vwsz1=vwsc1 + lda #<0 + sta.z a + lda #>0 + sta.z a+1 + // [1] phi main::i#2 = 0 [phi:main->main::@1#8] -- vbuyy=vbuc1 + ldy #0 + jmp __b1 + // main::@1 + __b1: + // [2] if(main::i#2<$a) goto main::@2 -- vbuyy_lt_vbuc1_then_la1 + cpy #$a + bcc __b2 + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // main::@2 + __b2: + // [4] main::$2 = main::i#2 << 1 -- vbuxx=vbuyy_rol_1 + tya + asl + tax + // [5] main::SCREEN[main::$2] = main::a#2 -- pwsc1_derefidx_vbuxx=vwsz1 + lda.z a + sta SCREEN,x + lda.z a+1 + sta SCREEN+1,x + // [6] main::a#1 = ++ main::a#2 -- vwsz1=_inc_vwsz1 + inc.z a + bne !+ + inc.z a+1 + !: + // [7] main::SCREEN[main::$2] = main::b#2 -- pwsc1_derefidx_vbuxx=vwsz1 + lda.z b + sta SCREEN,x + lda.z b+1 + sta SCREEN+1,x + // [8] main::b#1 = ++ main::b#2 -- vwsz1=_inc_vwsz1 + inc.z b + bne !+ + inc.z b+1 + !: + // [9] main::SCREEN[main::$2] = main::c#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda c + sta SCREEN,x + lda c+1 + sta SCREEN+1,x + // [10] main::c#1 = ++ main::c#2 -- vwsm1=_inc_vwsm1 + inc c + bne !+ + inc c+1 + !: + // [11] main::SCREEN[main::$2] = main::d#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda d + sta SCREEN,x + lda d+1 + sta SCREEN+1,x + // [12] main::d#1 = ++ main::d#2 -- vwsm1=_inc_vwsm1 + inc d + bne !+ + inc d+1 + !: + // [13] main::SCREEN[main::$2] = main::e#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda e + sta SCREEN,x + lda e+1 + sta SCREEN+1,x + // [14] main::e#1 = ++ main::e#2 -- vwsm1=_inc_vwsm1 + inc e + bne !+ + inc e+1 + !: + // [15] main::SCREEN[main::$2] = main::f#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda f + sta SCREEN,x + lda f+1 + sta SCREEN+1,x + // [16] main::f#1 = ++ main::f#2 -- vwsm1=_inc_vwsm1 + inc f + bne !+ + inc f+1 + !: + // [17] main::SCREEN[main::$2] = main::g#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda g + sta SCREEN,x + lda g+1 + sta SCREEN+1,x + // [18] main::g#1 = ++ main::g#2 -- vwsm1=_inc_vwsm1 + inc g + bne !+ + inc g+1 + !: + // [19] main::SCREEN[main::$2] = main::h#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda h + sta SCREEN,x + lda h+1 + sta SCREEN+1,x + // [20] main::h#1 = ++ main::h#2 -- vwsm1=_inc_vwsm1 + inc h + bne !+ + inc h+1 + !: + // [21] main::i#1 = ++ main::i#2 -- vbuyy=_inc_vbuyy + iny + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + __b1_from___b2: + // [1] phi main::h#2 = main::h#1 [phi:main::@2->main::@1#0] -- register_copy + // [1] phi main::g#2 = main::g#1 [phi:main::@2->main::@1#1] -- register_copy + // [1] phi main::f#2 = main::f#1 [phi:main::@2->main::@1#2] -- register_copy + // [1] phi main::e#2 = main::e#1 [phi:main::@2->main::@1#3] -- register_copy + // [1] phi main::d#2 = main::d#1 [phi:main::@2->main::@1#4] -- register_copy + // [1] phi main::c#2 = main::c#1 [phi:main::@2->main::@1#5] -- register_copy + // [1] phi main::b#2 = main::b#1 [phi:main::@2->main::@1#6] -- register_copy + // [1] phi main::a#2 = main::a#1 [phi:main::@2->main::@1#7] -- register_copy + // [1] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#8] -- register_copy + jmp __b1 + .segment Data + c: .word 0 + d: .word 0 + e: .word 0 + f: .word 0 + g: .word 0 + h: .word 0 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction lda #>0 +Removing instruction lda #<0 +Removing instruction lda #>0 +Removing instruction lda #<0 +Removing instruction lda #>0 +Removing instruction lda #<0 +Removing instruction lda #>0 +Removing instruction lda #<0 +Removing instruction lda #>0 +Removing instruction lda #<0 +Removing instruction lda #>0 +Removing instruction lda #<0 +Removing instruction lda #>0 +Removing instruction lda #<0 +Removing instruction lda #>0 +Replacing instruction ldy #0 with TAY +Succesful ASM optimization Pass5UnnecesaryLoadElimination +Removing instruction __b1_from_main: +Removing instruction __breturn: +Removing instruction __b1_from___b2: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +void main() +char main::$2 // reg byte x 6.6 +__constant int * const main::SCREEN = (int *) 1024 +int main::a +int main::a#1 // a zp[2]:251 1.375 +int main::a#2 // a zp[2]:251 8.25 +int main::b +int main::b#1 // b zp[2]:253 1.5714285714285714 +int main::b#2 // b zp[2]:253 5.5 +int main::c +int main::c#1 // c mem[2] 1.8333333333333333 +int main::c#2 // c mem[2] 4.125 +int main::d +int main::d#1 // d mem[2] 2.2 +int main::d#2 // d mem[2] 3.3000000000000003 +int main::e +int main::e#1 // e mem[2] 2.75 +int main::e#2 // e mem[2] 2.75 +int main::f +int main::f#1 // f mem[2] 3.6666666666666665 +int main::f#2 // f mem[2] 2.357142857142857 +int main::g +int main::g#1 // g mem[2] 5.5 +int main::g#2 // g mem[2] 2.0625 +int main::h +int main::h#1 // h mem[2] 11.0 +int main::h#2 // h mem[2] 1.8333333333333335 +char main::i +char main::i#1 // reg byte y 22.0 +char main::i#2 // reg byte y 2.3157894736842106 + +reg byte y [ main::i#2 main::i#1 ] +zp[2]:251 [ main::a#2 main::a#1 ] +zp[2]:253 [ main::b#2 main::b#1 ] +mem[2] [ main::c#2 main::c#1 ] +mem[2] [ main::d#2 main::d#1 ] +mem[2] [ main::e#2 main::e#1 ] +mem[2] [ main::f#2 main::f#1 ] +mem[2] [ main::g#2 main::g#1 ] +mem[2] [ main::h#2 main::h#1 ] +reg byte x [ main::$2 ] + + +FINAL ASSEMBLER +Score: 3321 + + // File Comments +// Tests that variables overflow to main memory when zeropage is exhausted + // Upstart + // Commodore 64 PRG executable file +.file [name="zeropage-overflow.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 +// And then allocate a bunch of variables +main: { + .label SCREEN = $400 + .label a = $fb + .label b = $fd + // [1] phi from main to main::@1 [phi:main->main::@1] + // [1] phi main::h#2 = 0 [phi:main->main::@1#0] -- vwsm1=vwsc1 + lda #<0 + sta h + sta h+1 + // [1] phi main::g#2 = 0 [phi:main->main::@1#1] -- vwsm1=vwsc1 + sta g + sta g+1 + // [1] phi main::f#2 = 0 [phi:main->main::@1#2] -- vwsm1=vwsc1 + sta f + sta f+1 + // [1] phi main::e#2 = 0 [phi:main->main::@1#3] -- vwsm1=vwsc1 + sta e + sta e+1 + // [1] phi main::d#2 = 0 [phi:main->main::@1#4] -- vwsm1=vwsc1 + sta d + sta d+1 + // [1] phi main::c#2 = 0 [phi:main->main::@1#5] -- vwsm1=vwsc1 + sta c + sta c+1 + // [1] phi main::b#2 = 0 [phi:main->main::@1#6] -- vwsz1=vwsc1 + sta.z b + sta.z b+1 + // [1] phi main::a#2 = 0 [phi:main->main::@1#7] -- vwsz1=vwsc1 + sta.z a + sta.z a+1 + // [1] phi main::i#2 = 0 [phi:main->main::@1#8] -- vbuyy=vbuc1 + tay + // main::@1 + __b1: + // for(char i=0;i<10;i++) + // [2] if(main::i#2<$a) goto main::@2 -- vbuyy_lt_vbuc1_then_la1 + cpy #$a + bcc __b2 + // main::@return + // } + // [3] return + rts + // main::@2 + __b2: + // SCREEN[i] = a++ + // [4] main::$2 = main::i#2 << 1 -- vbuxx=vbuyy_rol_1 + tya + asl + tax + // [5] main::SCREEN[main::$2] = main::a#2 -- pwsc1_derefidx_vbuxx=vwsz1 + lda.z a + sta SCREEN,x + lda.z a+1 + sta SCREEN+1,x + // SCREEN[i] = a++; + // [6] main::a#1 = ++ main::a#2 -- vwsz1=_inc_vwsz1 + inc.z a + bne !+ + inc.z a+1 + !: + // SCREEN[i] = b++ + // [7] main::SCREEN[main::$2] = main::b#2 -- pwsc1_derefidx_vbuxx=vwsz1 + lda.z b + sta SCREEN,x + lda.z b+1 + sta SCREEN+1,x + // SCREEN[i] = b++; + // [8] main::b#1 = ++ main::b#2 -- vwsz1=_inc_vwsz1 + inc.z b + bne !+ + inc.z b+1 + !: + // SCREEN[i] = c++ + // [9] main::SCREEN[main::$2] = main::c#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda c + sta SCREEN,x + lda c+1 + sta SCREEN+1,x + // SCREEN[i] = c++; + // [10] main::c#1 = ++ main::c#2 -- vwsm1=_inc_vwsm1 + inc c + bne !+ + inc c+1 + !: + // SCREEN[i] = d++ + // [11] main::SCREEN[main::$2] = main::d#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda d + sta SCREEN,x + lda d+1 + sta SCREEN+1,x + // SCREEN[i] = d++; + // [12] main::d#1 = ++ main::d#2 -- vwsm1=_inc_vwsm1 + inc d + bne !+ + inc d+1 + !: + // SCREEN[i] = e++ + // [13] main::SCREEN[main::$2] = main::e#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda e + sta SCREEN,x + lda e+1 + sta SCREEN+1,x + // SCREEN[i] = e++; + // [14] main::e#1 = ++ main::e#2 -- vwsm1=_inc_vwsm1 + inc e + bne !+ + inc e+1 + !: + // SCREEN[i] = f++ + // [15] main::SCREEN[main::$2] = main::f#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda f + sta SCREEN,x + lda f+1 + sta SCREEN+1,x + // SCREEN[i] = f++; + // [16] main::f#1 = ++ main::f#2 -- vwsm1=_inc_vwsm1 + inc f + bne !+ + inc f+1 + !: + // SCREEN[i] = g++ + // [17] main::SCREEN[main::$2] = main::g#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda g + sta SCREEN,x + lda g+1 + sta SCREEN+1,x + // SCREEN[i] = g++; + // [18] main::g#1 = ++ main::g#2 -- vwsm1=_inc_vwsm1 + inc g + bne !+ + inc g+1 + !: + // SCREEN[i] = h++ + // [19] main::SCREEN[main::$2] = main::h#2 -- pwsc1_derefidx_vbuxx=vwsm1 + lda h + sta SCREEN,x + lda h+1 + sta SCREEN+1,x + // SCREEN[i] = h++; + // [20] main::h#1 = ++ main::h#2 -- vwsm1=_inc_vwsm1 + inc h + bne !+ + inc h+1 + !: + // for(char i=0;i<10;i++) + // [21] main::i#1 = ++ main::i#2 -- vbuyy=_inc_vbuyy + iny + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [1] phi main::h#2 = main::h#1 [phi:main::@2->main::@1#0] -- register_copy + // [1] phi main::g#2 = main::g#1 [phi:main::@2->main::@1#1] -- register_copy + // [1] phi main::f#2 = main::f#1 [phi:main::@2->main::@1#2] -- register_copy + // [1] phi main::e#2 = main::e#1 [phi:main::@2->main::@1#3] -- register_copy + // [1] phi main::d#2 = main::d#1 [phi:main::@2->main::@1#4] -- register_copy + // [1] phi main::c#2 = main::c#1 [phi:main::@2->main::@1#5] -- register_copy + // [1] phi main::b#2 = main::b#1 [phi:main::@2->main::@1#6] -- register_copy + // [1] phi main::a#2 = main::a#1 [phi:main::@2->main::@1#7] -- register_copy + // [1] phi main::i#2 = main::i#1 [phi:main::@2->main::@1#8] -- register_copy + jmp __b1 + .segment Data + c: .word 0 + d: .word 0 + e: .word 0 + f: .word 0 + g: .word 0 + h: .word 0 +} + // File Data + diff --git a/src/test/ref/zeropage-overflow.sym b/src/test/ref/zeropage-overflow.sym new file mode 100644 index 000000000..81e9d9a01 --- /dev/null +++ b/src/test/ref/zeropage-overflow.sym @@ -0,0 +1,41 @@ +void main() +char main::$2 // reg byte x 6.6 +__constant int * const main::SCREEN = (int *) 1024 +int main::a +int main::a#1 // a zp[2]:251 1.375 +int main::a#2 // a zp[2]:251 8.25 +int main::b +int main::b#1 // b zp[2]:253 1.5714285714285714 +int main::b#2 // b zp[2]:253 5.5 +int main::c +int main::c#1 // c mem[2] 1.8333333333333333 +int main::c#2 // c mem[2] 4.125 +int main::d +int main::d#1 // d mem[2] 2.2 +int main::d#2 // d mem[2] 3.3000000000000003 +int main::e +int main::e#1 // e mem[2] 2.75 +int main::e#2 // e mem[2] 2.75 +int main::f +int main::f#1 // f mem[2] 3.6666666666666665 +int main::f#2 // f mem[2] 2.357142857142857 +int main::g +int main::g#1 // g mem[2] 5.5 +int main::g#2 // g mem[2] 2.0625 +int main::h +int main::h#1 // h mem[2] 11.0 +int main::h#2 // h mem[2] 1.8333333333333335 +char main::i +char main::i#1 // reg byte y 22.0 +char main::i#2 // reg byte y 2.3157894736842106 + +reg byte y [ main::i#2 main::i#1 ] +zp[2]:251 [ main::a#2 main::a#1 ] +zp[2]:253 [ main::b#2 main::b#1 ] +mem[2] [ main::c#2 main::c#1 ] +mem[2] [ main::d#2 main::d#1 ] +mem[2] [ main::e#2 main::e#1 ] +mem[2] [ main::f#2 main::f#1 ] +mem[2] [ main::g#2 main::g#1 ] +mem[2] [ main::h#2 main::h#1 ] +reg byte x [ main::$2 ]