From 65dffdbdea8a82c2f5cec8027f6bf0d84c9776a6 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Sun, 18 Aug 2019 21:55:21 +0200 Subject: [PATCH] Added upport for continue inside switch statement. Closes #170 --- .../Pass0GenerateStatementSequence.java | 6 +- .../dk/camelot64/kickc/test/TestPrograms.java | 5 + src/test/kc/switch-0.kc | 8 +- src/test/kc/switch-1.kc | 22 + src/test/ref/switch-0.asm | 7 +- src/test/ref/switch-0.log | 48 +- src/test/ref/switch-1.asm | 32 + src/test/ref/switch-1.cfg | 36 ++ src/test/ref/switch-1.log | 549 ++++++++++++++++++ src/test/ref/switch-1.sym | 19 + 10 files changed, 702 insertions(+), 30 deletions(-) create mode 100644 src/test/kc/switch-1.kc create mode 100644 src/test/ref/switch-1.asm create mode 100644 src/test/ref/switch-1.cfg create mode 100644 src/test/ref/switch-1.log create mode 100644 src/test/ref/switch-1.sym diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 313aa25c0..2569ead26 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -974,10 +974,13 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { @Override public Object visitStmtSwitch(KickCParser.StmtSwitchContext ctx) { + Loop containingLoop = loopStack.peek(); // Create a block scope - to keep all statements of the loop inside it BlockScope blockScope = getCurrentScope().addBlockScope(); scopeStack.push(blockScope); - loopStack.push(new Loop(blockScope)); + Loop switchLoop = new Loop(blockScope); + switchLoop.setContinueLabel(containingLoop.getOrCreateContinueLabel()); + loopStack.push(switchLoop); List comments = ensureUnusedComments(getCommentsSymbol(ctx)); // TODO: Add comments to next stmt // Evaluate the switch-expression @@ -1012,7 +1015,6 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { if(ctx.switchCases().stmtSeq() != null) { this.visit(ctx.switchCases().stmtSeq()); } - // TODO: Do something to handle continue! addLoopBreakLabel(loopStack.pop(), ctx); scopeStack.pop(); return null; diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index c9868090d..05dabaa07 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -71,6 +71,11 @@ public class TestPrograms { // compileAndCompare("loophead-problem"); //} + @Test + public void testSwitch1() throws IOException, URISyntaxException { + compileAndCompare("switch-1"); + } + @Test public void testSwitch0() throws IOException, URISyntaxException { compileAndCompare("switch-0"); diff --git a/src/test/kc/switch-0.kc b/src/test/kc/switch-0.kc index 0ea2755bc..eb1d44b08 100644 --- a/src/test/kc/switch-0.kc +++ b/src/test/kc/switch-0.kc @@ -7,20 +7,20 @@ void main() { for(char i:0..5) { // Test switching on a simple char switch(i) { - // A simple case with a break case 1: + // A simple case with a break SCREEN[i] = '1'; break; - // A case with no body case 2: - // A case with fall-through + // A case with no body case 3: + // A case with fall-through SCREEN[i] = '3'; case 4: SCREEN[i] = '4'; break; - // No case for 0 & 5 default: + // No case for 0 & 5 SCREEN[i] = 'd'; } } diff --git a/src/test/kc/switch-1.kc b/src/test/kc/switch-1.kc new file mode 100644 index 000000000..5c3760bae --- /dev/null +++ b/src/test/kc/switch-1.kc @@ -0,0 +1,22 @@ +// Tests simple switch()-statement - including a continue statement for the enclosing loop +// Expected output 'a1aa1a' (numbers should be inverted) + +void main() { + const char* SCREEN = 0x0400; + for(char i:0..5) { + // Test switching on a simple char + switch(i) { + case 1: + case 4: + SCREEN[i] = '1'; + break; + default: + // No case for 0 & 5 + SCREEN[i] = 'a'; + // Continue skips inverting the char + continue; + } + // Invert the screen character + SCREEN[i] |= 0x80; + } +} diff --git a/src/test/ref/switch-0.asm b/src/test/ref/switch-0.asm index e266cdf25..9b0948454 100644 --- a/src/test/ref/switch-0.asm +++ b/src/test/ref/switch-0.asm @@ -7,6 +7,7 @@ main: { .label SCREEN = $400 ldx #0 b5: + // No case for 0 & 5 lda #'d' sta SCREEN,x b6: @@ -15,13 +16,11 @@ main: { bne b1 rts b1: - // A simple case with a break cpx #1 beq b2 - // A case with no body cpx #2 beq b3 - // A case with fall-through + // A case with no body cpx #3 beq b3 cpx #4 @@ -32,10 +31,12 @@ main: { sta SCREEN,x jmp b6 b3: + // A case with fall-through lda #'3' sta SCREEN,x jmp b4 b2: + // A simple case with a break lda #'1' sta SCREEN,x jmp b6 diff --git a/src/test/ref/switch-0.log b/src/test/ref/switch-0.log index 823def1d7..d32a6328c 100644 --- a/src/test/ref/switch-0.log +++ b/src/test/ref/switch-0.log @@ -1,9 +1,10 @@ Culled Empty Block (label) main::@3 Culled Empty Block (label) main::@14 Culled Empty Block (label) main::@15 +Culled Empty Block (label) main::@7 Culled Empty Block (label) main::@8 Culled Empty Block (label) main::@9 -Culled Empty Block (label) main::@10 +Culled Empty Block (label) main::@16 CONTROL FLOW GRAPH SSA @begin: scope:[] from @@ -12,14 +13,14 @@ main: scope:[main] from @1 (byte*) main::SCREEN#0 ← ((byte*)) (number) $400 (byte) main::i#0 ← (byte) 0 to:main::@1 -main::@1: scope:[main] from main main::@7 - (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@7/(byte) main::i#1 ) +main::@1: scope:[main] from main main::@10 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@10/(byte) main::i#1 ) if((byte) main::i#2==(number) 1) goto main::@2 to:main::@11 main::@2: scope:[main] from main::@1 (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) *((byte*) main::SCREEN#0 + (byte) main::i#3) ← (byte) '1' - to:main::@7 + to:main::@10 main::@11: scope:[main] from main::@1 (byte) main::i#4 ← phi( main::@1/(byte) main::i#2 ) if((byte) main::i#4==(number) 2) goto main::@4 @@ -39,18 +40,18 @@ main::@13: scope:[main] from main::@12 main::@5: scope:[main] from main::@13 main::@4 (byte) main::i#8 ← phi( main::@13/(byte) main::i#7 main::@4/(byte) main::i#6 ) *((byte*) main::SCREEN#0 + (byte) main::i#8) ← (byte) '4' - to:main::@7 + to:main::@10 main::@6: scope:[main] from main::@13 (byte) main::i#9 ← phi( main::@13/(byte) main::i#7 ) *((byte*) main::SCREEN#0 + (byte) main::i#9) ← (byte) 'd' - to:main::@7 -main::@7: scope:[main] from main::@2 main::@5 main::@6 + to:main::@10 +main::@10: scope:[main] from main::@2 main::@5 main::@6 (byte) main::i#10 ← phi( main::@2/(byte) main::i#3 main::@5/(byte) main::i#8 main::@6/(byte) main::i#9 ) (byte) main::i#1 ← (byte) main::i#10 + rangenext(0,5) (bool~) main::$0 ← (byte) main::i#1 != rangelast(0,5) if((bool~) main::$0) goto main::@1 to:main::@return -main::@return: scope:[main] from main::@7 +main::@return: scope:[main] from main::@10 return to:@return @1: scope:[] from @begin @@ -68,6 +69,7 @@ SYMBOL TABLE SSA (void()) main() (bool~) main::$0 (label) main::@1 +(label) main::@10 (label) main::@11 (label) main::@12 (label) main::@13 @@ -75,7 +77,6 @@ SYMBOL TABLE SSA (label) main::@4 (label) main::@5 (label) main::@6 -(label) main::@7 (label) main::@return (byte*) main::SCREEN (byte*) main::SCREEN#0 @@ -175,7 +176,7 @@ Successful SSA optimization Pass2AliasElimination Inlining constant with var siblings (const byte) main::i#0 Constant inlined main::i#0 = (byte) 0 Successful SSA optimization Pass2ConstantInlining -Added new block during phi lifting main::@16(between main::@13 and main::@6) +Added new block during phi lifting main::@17(between main::@13 and main::@6) Adding NOP phi() at start of @begin Adding NOP phi() at start of @1 Adding NOP phi() at start of @2 @@ -199,11 +200,11 @@ Culled Empty Block (label) main::@1_1 Culled Empty Block (label) main::@11_1 Culled Empty Block (label) main::@12_1 Culled Empty Block (label) main::@13_1 -Culled Empty Block (label) main::@16 +Culled Empty Block (label) main::@17 Renumbering block main::@4 to main::@3 Renumbering block main::@5 to main::@4 Renumbering block main::@6 to main::@5 -Renumbering block main::@7 to main::@6 +Renumbering block main::@10 to main::@6 Renumbering block main::@11 to main::@7 Renumbering block main::@12 to main::@8 Renumbering block main::@13 to main::@9 @@ -313,6 +314,7 @@ main: { // main::@5 b5: // [6] *((const byte*) main::SCREEN#0 + (byte) main::i#29) ← (byte) 'd' -- pbuc1_derefidx_vbuz1=vbuc2 + // No case for 0 & 5 lda #'d' ldy.z i sta SCREEN,y @@ -338,7 +340,6 @@ main: { // main::@1 b1: // [11] if((byte) main::i#1==(byte) 1) goto main::@2 -- vbuz1_eq_vbuc1_then_la1 - // A simple case with a break lda #1 cmp.z i beq b2 @@ -346,7 +347,6 @@ main: { // main::@7 b7: // [12] if((byte) main::i#1==(byte) 2) goto main::@3 -- vbuz1_eq_vbuc1_then_la1 - // A case with no body lda #2 cmp.z i beq b3 @@ -354,7 +354,7 @@ main: { // main::@8 b8: // [13] if((byte) main::i#1==(byte) 3) goto main::@3 -- vbuz1_eq_vbuc1_then_la1 - // A case with fall-through + // A case with no body lda #3 cmp.z i beq b3 @@ -379,6 +379,7 @@ main: { // main::@3 b3: // [16] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '3' -- pbuc1_derefidx_vbuz1=vbuc2 + // A case with fall-through lda #'3' ldy.z i sta SCREEN,y @@ -386,6 +387,7 @@ main: { // main::@2 b2: // [17] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '1' -- pbuc1_derefidx_vbuz1=vbuc2 + // A simple case with a break lda #'1' ldy.z i sta SCREEN,y @@ -448,6 +450,7 @@ main: { // main::@5 b5: // [6] *((const byte*) main::SCREEN#0 + (byte) main::i#29) ← (byte) 'd' -- pbuc1_derefidx_vbuxx=vbuc2 + // No case for 0 & 5 lda #'d' sta SCREEN,x // [7] phi from main::@2 main::@4 main::@5 to main::@6 [phi:main::@2/main::@4/main::@5->main::@6] @@ -471,21 +474,19 @@ main: { // main::@1 b1: // [11] if((byte) main::i#1==(byte) 1) goto main::@2 -- vbuxx_eq_vbuc1_then_la1 - // A simple case with a break cpx #1 beq b2 jmp b7 // main::@7 b7: // [12] if((byte) main::i#1==(byte) 2) goto main::@3 -- vbuxx_eq_vbuc1_then_la1 - // A case with no body cpx #2 beq b3 jmp b8 // main::@8 b8: // [13] if((byte) main::i#1==(byte) 3) goto main::@3 -- vbuxx_eq_vbuc1_then_la1 - // A case with fall-through + // A case with no body cpx #3 beq b3 jmp b9 @@ -507,12 +508,14 @@ main: { // main::@3 b3: // [16] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '3' -- pbuc1_derefidx_vbuxx=vbuc2 + // A case with fall-through lda #'3' sta SCREEN,x jmp b4 // main::@2 b2: // [17] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '1' -- pbuc1_derefidx_vbuxx=vbuc2 + // A simple case with a break lda #'1' sta SCREEN,x jmp b6_from_b2 @@ -606,6 +609,7 @@ main: { b5: // SCREEN[i] = 'd' // [6] *((const byte*) main::SCREEN#0 + (byte) main::i#29) ← (byte) 'd' -- pbuc1_derefidx_vbuxx=vbuc2 + // No case for 0 & 5 lda #'d' sta SCREEN,x // [7] phi from main::@2 main::@4 main::@5 to main::@6 [phi:main::@2/main::@4/main::@5->main::@6] @@ -625,23 +629,23 @@ main: { // main::@1 b1: // case 1: + // // A simple case with a break // SCREEN[i] = '1'; // break; // [11] if((byte) main::i#1==(byte) 1) goto main::@2 -- vbuxx_eq_vbuc1_then_la1 - // A simple case with a break cpx #1 beq b2 // main::@7 // case 2: // [12] if((byte) main::i#1==(byte) 2) goto main::@3 -- vbuxx_eq_vbuc1_then_la1 - // A case with no body cpx #2 beq b3 // main::@8 // case 3: + // // A case with fall-through // SCREEN[i] = '3'; // [13] if((byte) main::i#1==(byte) 3) goto main::@3 -- vbuxx_eq_vbuc1_then_la1 - // A case with fall-through + // A case with no body cpx #3 beq b3 // main::@9 @@ -665,6 +669,7 @@ main: { b3: // SCREEN[i] = '3' // [16] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '3' -- pbuc1_derefidx_vbuxx=vbuc2 + // A case with fall-through lda #'3' sta SCREEN,x jmp b4 @@ -672,6 +677,7 @@ main: { b2: // SCREEN[i] = '1' // [17] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '1' -- pbuc1_derefidx_vbuxx=vbuc2 + // A simple case with a break lda #'1' sta SCREEN,x jmp b6 diff --git a/src/test/ref/switch-1.asm b/src/test/ref/switch-1.asm new file mode 100644 index 000000000..11aad4891 --- /dev/null +++ b/src/test/ref/switch-1.asm @@ -0,0 +1,32 @@ +// Tests simple switch()-statement - including a continue statement for the enclosing loop +// Expected output 'a1aa1a' (numbers should be inverted) +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +main: { + .label SCREEN = $400 + ldx #0 + b3: + // No case for 0 & 5 + lda #'a' + sta SCREEN,x + b5: + inx + cpx #6 + bne b1 + rts + b1: + cpx #1 + beq b2 + cpx #4 + beq b2 + jmp b3 + b2: + lda #'1' + sta SCREEN,x + // Invert the screen character + lda #$80 + ora SCREEN,x + sta SCREEN,x + jmp b5 +} diff --git a/src/test/ref/switch-1.cfg b/src/test/ref/switch-1.cfg new file mode 100644 index 000000000..fc4356319 --- /dev/null +++ b/src/test/ref/switch-1.cfg @@ -0,0 +1,36 @@ +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@3 +main::@3: scope:[main] from main main::@6 + [5] (byte) main::i#14 ← phi( main::@6/(byte) main::i#1 main/(byte) 0 ) + [6] *((const byte*) main::SCREEN#0 + (byte) main::i#14) ← (byte) 'a' + to:main::@5 +main::@5: scope:[main] from main::@3 main::@4 + [7] (byte) main::i#12 ← phi( main::@3/(byte) main::i#14 main::@4/(byte) main::i#1 ) + [8] (byte) main::i#1 ← ++ (byte) main::i#12 + [9] if((byte) main::i#1!=(byte) 6) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@5 + [10] return + to:@return +main::@1: scope:[main] from main::@5 + [11] if((byte) main::i#1==(byte) 1) goto main::@2 + to:main::@6 +main::@6: scope:[main] from main::@1 + [12] if((byte) main::i#1==(byte) 4) goto main::@2 + to:main::@3 +main::@2: scope:[main] from main::@1 main::@6 + [13] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '1' + to:main::@4 +main::@4: scope:[main] from main::@2 + [14] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← *((const byte*) main::SCREEN#0 + (byte) main::i#1) | (byte) $80 + to:main::@5 diff --git a/src/test/ref/switch-1.log b/src/test/ref/switch-1.log new file mode 100644 index 000000000..e4c4dc6b4 --- /dev/null +++ b/src/test/ref/switch-1.log @@ -0,0 +1,549 @@ +Culled Empty Block (label) main::@2 +Culled Empty Block (label) main::@10 +Culled Empty Block (label) main::@11 +Culled Empty Block (label) main::@6 +Culled Empty Block (label) main::@7 +Culled Empty Block (label) main::@12 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + to:@1 +main: scope:[main] from @1 + (byte*) main::SCREEN#0 ← ((byte*)) (number) $400 + (byte) main::i#0 ← (byte) 0 + to:main::@1 +main::@1: scope:[main] from main main::@8 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@8/(byte) main::i#1 ) + if((byte) main::i#2==(number) 1) goto main::@3 + to:main::@9 +main::@9: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + if((byte) main::i#3==(number) 4) goto main::@3 + to:main::@4 +main::@3: scope:[main] from main::@1 main::@9 + (byte) main::i#4 ← phi( main::@1/(byte) main::i#2 main::@9/(byte) main::i#3 ) + *((byte*) main::SCREEN#0 + (byte) main::i#4) ← (byte) '1' + to:main::@5 +main::@4: scope:[main] from main::@9 + (byte) main::i#5 ← phi( main::@9/(byte) main::i#3 ) + *((byte*) main::SCREEN#0 + (byte) main::i#5) ← (byte) 'a' + to:main::@8 +main::@5: scope:[main] from main::@3 + (byte) main::i#6 ← phi( main::@3/(byte) main::i#4 ) + *((byte*) main::SCREEN#0 + (byte) main::i#6) ← *((byte*) main::SCREEN#0 + (byte) main::i#6) | (number) $80 + to:main::@8 +main::@8: scope:[main] from main::@4 main::@5 + (byte) main::i#7 ← phi( main::@4/(byte) main::i#5 main::@5/(byte) main::i#6 ) + (byte) main::i#1 ← (byte) main::i#7 + rangenext(0,5) + (bool~) main::$0 ← (byte) main::i#1 != rangelast(0,5) + if((bool~) main::$0) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@8 + return + to:@return +@1: scope:[] from @begin + call main + to:@2 +@2: scope:[] from @1 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(void()) main() +(bool~) main::$0 +(label) main::@1 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@8 +(label) main::@9 +(label) main::@return +(byte*) main::SCREEN +(byte*) main::SCREEN#0 +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(byte) main::i#4 +(byte) main::i#5 +(byte) main::i#6 +(byte) main::i#7 + +Adding number conversion cast (unumber) 1 in if((byte) main::i#2==(number) 1) goto main::@3 +Adding number conversion cast (unumber) 4 in if((byte) main::i#3==(number) 4) goto main::@3 +Adding number conversion cast (unumber) $80 in *((byte*) main::SCREEN#0 + (byte) main::i#6) ← *((byte*) main::SCREEN#0 + (byte) main::i#6) | (number) $80 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) main::SCREEN#0 ← (byte*)(number) $400 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 1 +Simplifying constant integer cast 4 +Simplifying constant integer cast $80 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 1 +Finalized unsigned number type (byte) 4 +Finalized unsigned number type (byte) $80 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) main::i#2 = (byte) main::i#3 (byte) main::i#5 +Alias (byte) main::i#4 = (byte) main::i#6 +Successful SSA optimization Pass2AliasElimination +Alias (byte) main::i#2 = (byte) main::i#4 +Successful SSA optimization Pass2AliasElimination +Alias (byte) main::i#2 = (byte) main::i#7 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$0 [15] if((byte) main::i#1!=rangelast(0,5)) goto main::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) main::SCREEN#0 = (byte*) 1024 +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Resolved ranged next value [13] main::i#1 ← ++ main::i#2 to ++ +Resolved ranged comparison value [15] if(main::i#1!=rangelast(0,5)) goto main::@1 to (number) 6 +Successful SSA optimization Pass2LoopHeadConstantIdentification +Adding number conversion cast (unumber) 6 in if((byte) main::i#1!=(number) 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 +Alias (byte) main::i#1 = (byte) main::i#2 +Alias (byte) main::i#10 = (byte) main::i#8 +Alias (byte) main::i#11 = (byte) main::i#9 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (byte) main::i#13 (const byte) main::i#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Removing PHI-reference to removed block (main::@1_1) in block main::@3 +if() condition always false - eliminating [16] if((const byte) main::i#0==(byte) 1) goto main::@3 +Successful SSA optimization Pass2ConstantIfs +Successful SSA optimization Pass2LoopHeadConstantIdentification +Alias (byte) main::i#1 = (byte) main::i#10 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values (byte) main::i#15 (const byte) main::i#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Removing PHI-reference to removed block (main::@9_1) in block main::@3 +if() condition always false - eliminating [14] if((const byte) main::i#0==(byte) 4) goto main::@3 +Successful SSA optimization Pass2ConstantIfs +Alias (byte) main::i#1 = (byte) main::i#11 +Successful SSA optimization Pass2AliasElimination +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@13(between main::@9 and main::@4) +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @2 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@1_1 +Adding NOP phi() at start of main::@9_1 +CALL GRAPH +Calls in [] to main:2 + +Created 2 initial phi equivalence classes +Coalesced [10] main::i#17 ← main::i#14 +Coalesced [17] main::i#16 ← main::i#1 +Coalesced (already) [20] main::i#18 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) @2 +Culled Empty Block (label) main::@1_1 +Culled Empty Block (label) main::@9_1 +Culled Empty Block (label) main::@13 +Renumbering block main::@3 to main::@2 +Renumbering block main::@4 to main::@3 +Renumbering block main::@5 to main::@4 +Renumbering block main::@8 to main::@5 +Renumbering block main::@9 to main::@6 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +Adding NOP phi() at start of main + +FINAL CONTROL FLOW GRAPH +@begin: scope:[] from + [0] phi() + to:@1 +@1: scope:[] from @begin + [1] phi() + [2] call main + to:@end +@end: scope:[] from @1 + [3] phi() +main: scope:[main] from @1 + [4] phi() + to:main::@3 +main::@3: scope:[main] from main main::@6 + [5] (byte) main::i#14 ← phi( main::@6/(byte) main::i#1 main/(byte) 0 ) + [6] *((const byte*) main::SCREEN#0 + (byte) main::i#14) ← (byte) 'a' + to:main::@5 +main::@5: scope:[main] from main::@3 main::@4 + [7] (byte) main::i#12 ← phi( main::@3/(byte) main::i#14 main::@4/(byte) main::i#1 ) + [8] (byte) main::i#1 ← ++ (byte) main::i#12 + [9] if((byte) main::i#1!=(byte) 6) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@5 + [10] return + to:@return +main::@1: scope:[main] from main::@5 + [11] if((byte) main::i#1==(byte) 1) goto main::@2 + to:main::@6 +main::@6: scope:[main] from main::@1 + [12] if((byte) main::i#1==(byte) 4) goto main::@2 + to:main::@3 +main::@2: scope:[main] from main::@1 main::@6 + [13] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '1' + to:main::@4 +main::@4: scope:[main] from main::@2 + [14] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← *((const byte*) main::SCREEN#0 + (byte) main::i#1) | (byte) $80 + to:main::@5 + + +VARIABLE REGISTER WEIGHTS +(void()) main() +(byte*) main::SCREEN +(byte) main::i +(byte) main::i#1 151.5 +(byte) main::i#12 213.0 +(byte) main::i#14 61.5 + +Initial phi equivalence classes +[ main::i#12 main::i#14 main::i#1 ] +Complete equivalence classes +[ main::i#12 main::i#14 main::i#1 ] +Allocated zp ZP_BYTE:2 [ main::i#12 main::i#14 main::i#1 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// Tests simple switch()-statement - including a continue statement for the enclosing loop +// Expected output 'a1aa1a' (numbers should be inverted) + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label SCREEN = $400 + .label i = 2 + // [5] phi from main to main::@3 [phi:main->main::@3] + b3_from_main: + // [5] phi (byte) main::i#14 = (byte) 0 [phi:main->main::@3#0] -- vbuz1=vbuc1 + lda #0 + sta.z i + jmp b3 + // main::@3 + b3: + // [6] *((const byte*) main::SCREEN#0 + (byte) main::i#14) ← (byte) 'a' -- pbuc1_derefidx_vbuz1=vbuc2 + // No case for 0 & 5 + lda #'a' + ldy.z i + sta SCREEN,y + // [7] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5] + b5_from_b3: + b5_from_b4: + // [7] phi (byte) main::i#12 = (byte) main::i#14 [phi:main::@3/main::@4->main::@5#0] -- register_copy + jmp b5 + // main::@5 + b5: + // [8] (byte) main::i#1 ← ++ (byte) main::i#12 -- vbuz1=_inc_vbuz1 + inc.z i + // [9] if((byte) main::i#1!=(byte) 6) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #6 + cmp.z i + bne b1 + jmp breturn + // main::@return + breturn: + // [10] return + rts + // main::@1 + b1: + // [11] if((byte) main::i#1==(byte) 1) goto main::@2 -- vbuz1_eq_vbuc1_then_la1 + lda #1 + cmp.z i + beq b2 + jmp b6 + // main::@6 + b6: + // [12] if((byte) main::i#1==(byte) 4) goto main::@2 -- vbuz1_eq_vbuc1_then_la1 + lda #4 + cmp.z i + beq b2 + // [5] phi from main::@6 to main::@3 [phi:main::@6->main::@3] + b3_from_b6: + // [5] phi (byte) main::i#14 = (byte) main::i#1 [phi:main::@6->main::@3#0] -- register_copy + jmp b3 + // main::@2 + b2: + // [13] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '1' -- pbuc1_derefidx_vbuz1=vbuc2 + lda #'1' + ldy.z i + sta SCREEN,y + jmp b4 + // main::@4 + b4: + // [14] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← *((const byte*) main::SCREEN#0 + (byte) main::i#1) | (byte) $80 -- pbuc1_derefidx_vbuz1=pbuc1_derefidx_vbuz1_bor_vbuc2 + // Invert the screen character + lda #$80 + ldy.z i + ora SCREEN,y + sta SCREEN,y + jmp b5_from_b4 +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] *((const byte*) main::SCREEN#0 + (byte) main::i#14) ← (byte) 'a' [ main::i#14 ] ( main:2 [ main::i#14 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#12 main::i#14 main::i#1 ] +Statement [13] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '1' [ main::i#1 ] ( main:2 [ main::i#1 ] ) always clobbers reg byte a +Statement [14] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← *((const byte*) main::SCREEN#0 + (byte) main::i#1) | (byte) $80 [ main::i#1 ] ( main:2 [ main::i#1 ] ) always clobbers reg byte a +Statement [6] *((const byte*) main::SCREEN#0 + (byte) main::i#14) ← (byte) 'a' [ main::i#14 ] ( main:2 [ main::i#14 ] ) always clobbers reg byte a +Statement [13] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '1' [ main::i#1 ] ( main:2 [ main::i#1 ] ) always clobbers reg byte a +Statement [14] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← *((const byte*) main::SCREEN#0 + (byte) main::i#1) | (byte) $80 [ main::i#1 ] ( main:2 [ main::i#1 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#12 main::i#14 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 426: zp ZP_BYTE:2 [ main::i#12 main::i#14 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 5068 combination reg byte x [ main::i#12 main::i#14 main::i#1 ] +Uplifting [] best 5068 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Tests simple switch()-statement - including a continue statement for the enclosing loop +// Expected output 'a1aa1a' (numbers should be inverted) + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + // @begin +bbegin: + // [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 + // @1 +b1: + // [2] call main + // [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main + // [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend + // @end +bend: + // main +main: { + .label SCREEN = $400 + // [5] phi from main to main::@3 [phi:main->main::@3] + b3_from_main: + // [5] phi (byte) main::i#14 = (byte) 0 [phi:main->main::@3#0] -- vbuxx=vbuc1 + ldx #0 + jmp b3 + // main::@3 + b3: + // [6] *((const byte*) main::SCREEN#0 + (byte) main::i#14) ← (byte) 'a' -- pbuc1_derefidx_vbuxx=vbuc2 + // No case for 0 & 5 + lda #'a' + sta SCREEN,x + // [7] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5] + b5_from_b3: + b5_from_b4: + // [7] phi (byte) main::i#12 = (byte) main::i#14 [phi:main::@3/main::@4->main::@5#0] -- register_copy + jmp b5 + // main::@5 + b5: + // [8] (byte) main::i#1 ← ++ (byte) main::i#12 -- vbuxx=_inc_vbuxx + inx + // [9] if((byte) main::i#1!=(byte) 6) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #6 + bne b1 + jmp breturn + // main::@return + breturn: + // [10] return + rts + // main::@1 + b1: + // [11] if((byte) main::i#1==(byte) 1) goto main::@2 -- vbuxx_eq_vbuc1_then_la1 + cpx #1 + beq b2 + jmp b6 + // main::@6 + b6: + // [12] if((byte) main::i#1==(byte) 4) goto main::@2 -- vbuxx_eq_vbuc1_then_la1 + cpx #4 + beq b2 + // [5] phi from main::@6 to main::@3 [phi:main::@6->main::@3] + b3_from_b6: + // [5] phi (byte) main::i#14 = (byte) main::i#1 [phi:main::@6->main::@3#0] -- register_copy + jmp b3 + // main::@2 + b2: + // [13] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '1' -- pbuc1_derefidx_vbuxx=vbuc2 + lda #'1' + sta SCREEN,x + jmp b4 + // main::@4 + b4: + // [14] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← *((const byte*) main::SCREEN#0 + (byte) main::i#1) | (byte) $80 -- pbuc1_derefidx_vbuxx=pbuc1_derefidx_vbuxx_bor_vbuc2 + // Invert the screen character + lda #$80 + ora SCREEN,x + sta SCREEN,x + jmp b5_from_b4 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b3 +Removing instruction jmp b5 +Removing instruction jmp breturn +Removing instruction jmp b6 +Removing instruction jmp b4 +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b5_from_b4 with b5 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b5_from_b3: +Removing instruction b5_from_b4: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b3_from_main: +Removing instruction breturn: +Removing instruction b6: +Removing instruction b3_from_b6: +Removing instruction b4: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@6 +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = (byte*) 1024 +(byte) main::i +(byte) main::i#1 reg byte x 151.5 +(byte) main::i#12 reg byte x 213.0 +(byte) main::i#14 reg byte x 61.5 + +reg byte x [ main::i#12 main::i#14 main::i#1 ] + + +FINAL ASSEMBLER +Score: 3826 + + // File Comments +// Tests simple switch()-statement - including a continue statement for the enclosing loop +// Expected output 'a1aa1a' (numbers should be inverted) + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + // @begin + // [1] phi from @begin to @1 [phi:@begin->@1] + // @1 + // [2] call main + // [4] phi from @1 to main [phi:@1->main] + // [3] phi from @1 to @end [phi:@1->@end] + // @end + // main +main: { + .label SCREEN = $400 + // [5] phi from main to main::@3 [phi:main->main::@3] + // [5] phi (byte) main::i#14 = (byte) 0 [phi:main->main::@3#0] -- vbuxx=vbuc1 + ldx #0 + // main::@3 + b3: + // SCREEN[i] = 'a' + // [6] *((const byte*) main::SCREEN#0 + (byte) main::i#14) ← (byte) 'a' -- pbuc1_derefidx_vbuxx=vbuc2 + // No case for 0 & 5 + lda #'a' + sta SCREEN,x + // [7] phi from main::@3 main::@4 to main::@5 [phi:main::@3/main::@4->main::@5] + // [7] phi (byte) main::i#12 = (byte) main::i#14 [phi:main::@3/main::@4->main::@5#0] -- register_copy + // main::@5 + b5: + // for(char i:0..5) + // [8] (byte) main::i#1 ← ++ (byte) main::i#12 -- vbuxx=_inc_vbuxx + inx + // [9] if((byte) main::i#1!=(byte) 6) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #6 + bne b1 + // main::@return + // } + // [10] return + rts + // main::@1 + b1: + // case 1: + // [11] if((byte) main::i#1==(byte) 1) goto main::@2 -- vbuxx_eq_vbuc1_then_la1 + cpx #1 + beq b2 + // main::@6 + // case 4: + // SCREEN[i] = '1'; + // break; + // [12] if((byte) main::i#1==(byte) 4) goto main::@2 -- vbuxx_eq_vbuc1_then_la1 + cpx #4 + beq b2 + // [5] phi from main::@6 to main::@3 [phi:main::@6->main::@3] + // [5] phi (byte) main::i#14 = (byte) main::i#1 [phi:main::@6->main::@3#0] -- register_copy + jmp b3 + // main::@2 + b2: + // SCREEN[i] = '1' + // [13] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← (byte) '1' -- pbuc1_derefidx_vbuxx=vbuc2 + lda #'1' + sta SCREEN,x + // main::@4 + // SCREEN[i] |= 0x80 + // [14] *((const byte*) main::SCREEN#0 + (byte) main::i#1) ← *((const byte*) main::SCREEN#0 + (byte) main::i#1) | (byte) $80 -- pbuc1_derefidx_vbuxx=pbuc1_derefidx_vbuxx_bor_vbuc2 + // Invert the screen character + lda #$80 + ora SCREEN,x + sta SCREEN,x + jmp b5 +} + // File Data + diff --git a/src/test/ref/switch-1.sym b/src/test/ref/switch-1.sym new file mode 100644 index 000000000..64c07338b --- /dev/null +++ b/src/test/ref/switch-1.sym @@ -0,0 +1,19 @@ +(label) @1 +(label) @begin +(label) @end +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@4 +(label) main::@5 +(label) main::@6 +(label) main::@return +(byte*) main::SCREEN +(const byte*) main::SCREEN#0 SCREEN = (byte*) 1024 +(byte) main::i +(byte) main::i#1 reg byte x 151.5 +(byte) main::i#12 reg byte x 213.0 +(byte) main::i#14 reg byte x 61.5 + +reg byte x [ main::i#12 main::i#14 main::i#1 ]