From 3dff5d04fb17862135fbd8f36edecddcf8cd88b8 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Wed, 7 Jul 2021 08:47:44 +0200 Subject: [PATCH] Fixed exception "Block referenced, but not found in program" when encountering complex if(&&). Closes #676 --- .../kickc/model/symbols/Procedure.java | 4 +- .../Pass2ConditionalAndOrRewriting.java | 2 +- .../kickc/passes/Pass2SsaOptimization.java | 10 + .../kickc/test/TestProgramsFast.java | 15 + src/test/kc/block-error-1.c | 15 + src/test/kc/block-error-2.c | 12 + src/test/kc/shadow-variable-error-1.c | 14 + src/test/ref/block-error-1.asm | 34 ++ src/test/ref/block-error-1.cfg | 26 ++ src/test/ref/block-error-1.log | 389 ++++++++++++++++++ src/test/ref/block-error-1.sym | 11 + src/test/ref/block-error-2.asm | 32 ++ src/test/ref/block-error-2.cfg | 24 ++ src/test/ref/block-error-2.log | 357 ++++++++++++++++ src/test/ref/block-error-2.sym | 11 + 15 files changed, 953 insertions(+), 3 deletions(-) create mode 100644 src/test/kc/block-error-1.c create mode 100644 src/test/kc/block-error-2.c create mode 100644 src/test/kc/shadow-variable-error-1.c create mode 100644 src/test/ref/block-error-1.asm create mode 100644 src/test/ref/block-error-1.cfg create mode 100644 src/test/ref/block-error-1.log create mode 100644 src/test/ref/block-error-1.sym create mode 100644 src/test/ref/block-error-2.asm create mode 100644 src/test/ref/block-error-2.cfg create mode 100644 src/test/ref/block-error-2.log create mode 100644 src/test/ref/block-error-2.sym diff --git a/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java b/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java index cfd8980e3..d1bbb91c0 100644 --- a/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java +++ b/src/main/java/dk/camelot64/kickc/model/symbols/Procedure.java @@ -54,9 +54,9 @@ public class Procedure extends Scope { PHI_CALL("__phicall"), /** Parameters and return value over the stack. */ STACK_CALL("__stackcall"), - /** Parameters and return value handled through variables. */ + /** Parameters and return value handled through shared variables. */ VAR_CALL("__varcall"), - /** Intrinsic calling. */ + /** Intrinsic calling. Will be converted to intrinsic ASM late in the compile. */ INTRINSIC_CALL("__intrinsiccall"); private final String name; diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java index 68226e824..00332d8e3 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java @@ -126,7 +126,7 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { ControlFlowBlock destBlock = getGraph().getBlock(destLabel); LinkedHashMap replacements = new LinkedHashMap<>(); replacements.put(block.getLabel(), newBlockLabel.getRef()); - replaceLabels(destBlock, replacements); + replaceLabels(destBlock.getPhiBlock(), replacements); } diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java b/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java index 78c55bdcf..845f67a94 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2SsaOptimization.java @@ -61,6 +61,16 @@ public abstract class Pass2SsaOptimization extends Pass1Base implements PassStep visitor.visitBlock(block); } + /** + * Replace all usages of a label in a statement with another label. + * + * @param replacements Variables that have alias values. + */ + public void replaceLabels(Statement statement, final Map replacements) { + ControlFlowGraphBaseVisitor visitor = getLabelReplaceVisitor(replacements); + visitor.visitStatement(statement); + } + /** Creates a visitor that can replace labels. */ private ControlFlowGraphBaseVisitor getLabelReplaceVisitor(final Map replacements) { return new ControlFlowGraphBaseVisitor() { diff --git a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java index 76df2b851..dfd3e2154 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java +++ b/src/test/java/dk/camelot64/kickc/test/TestProgramsFast.java @@ -9,6 +9,21 @@ import java.io.IOException; */ public class TestProgramsFast extends TestPrograms { + //@Test + //public void testShadowVariableError1() throws IOException { + // compileAndCompare("shadow-variable-error-1.c"); + //} + + @Test + public void testBlockError2() throws IOException { + compileAndCompare("block-error-2.c"); + } + + @Test + public void testBlockError1() throws IOException { + compileAndCompare("block-error-1.c"); + } + @Test public void testMakeLong42() throws IOException { assertError("makelong4-2.c", "Wrong number of parameters in call. Expected 4."); diff --git a/src/test/kc/block-error-1.c b/src/test/kc/block-error-1.c new file mode 100644 index 000000000..bebdb5339 --- /dev/null +++ b/src/test/kc/block-error-1.c @@ -0,0 +1,15 @@ +// Demonstrates error "Block referenced, but not found in program." + +void main() { + for(char i=0;i!=8;i++) { + char move = 1; + char pos = 1; + char vacant = 1; + do { + pos += move; + if(pos) { + char vacant = 0; + } + } while(pos && vacant); + } +} diff --git a/src/test/kc/block-error-2.c b/src/test/kc/block-error-2.c new file mode 100644 index 000000000..47d11d7aa --- /dev/null +++ b/src/test/kc/block-error-2.c @@ -0,0 +1,12 @@ +// Demonstrates error "Sequence does not contain all blocks from the program." + +void main() { + char move = 1; + char pos = 1; + char vacant = 1; + do { + pos += move; + if(pos) + vacant = 0; + } while(pos && vacant); +} diff --git a/src/test/kc/shadow-variable-error-1.c b/src/test/kc/shadow-variable-error-1.c new file mode 100644 index 000000000..38ab5761c --- /dev/null +++ b/src/test/kc/shadow-variable-error-1.c @@ -0,0 +1,14 @@ +// Demonstrates error "Variable used before being defined." + +void main() { + for(char i=0;i!=8;i++) { + char move = 1; + char pos = 1; + char vacant = 1; + do { + pos += move; + if(pos) + char vacant = 0; + } while(pos && vacant); + } +} diff --git a/src/test/ref/block-error-1.asm b/src/test/ref/block-error-1.asm new file mode 100644 index 000000000..80a1077dc --- /dev/null +++ b/src/test/ref/block-error-1.asm @@ -0,0 +1,34 @@ +// Demonstrates error "Block referenced, but not found in program." + // Commodore 64 PRG executable file +.file [name="block-error-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: { + .const move = 1 + ldx #0 + __b1: + // for(char i=0;i!=8;i++) + cpx #8 + bne __b4 + // } + rts + __b4: + lda #1 + __b2: + // pos += move + clc + adc #move + // if(pos) + cmp #0 + // while(pos && vacant) + cmp #0 + bne __b2 + // for(char i=0;i!=8;i++) + inx + jmp __b1 +} diff --git a/src/test/ref/block-error-1.cfg b/src/test/ref/block-error-1.cfg new file mode 100644 index 000000000..73dbc405a --- /dev/null +++ b/src/test/ref/block-error-1.cfg @@ -0,0 +1,26 @@ + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@5 + [1] main::i#2 = phi( main/0, main::@5/main::i#1 ) + [2] if(main::i#2!=8) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 main::@3 + [4] main::pos#2 = phi( main::@1/1, main::@3/main::pos#1 ) + [5] main::pos#1 = main::pos#2 + main::move + [6] if(0==main::pos#1) goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@2 + [7] phi() + to:main::@3 +main::@3: scope:[main] from main::@2 main::@4 + [8] if(0!=main::pos#1) goto main::@2 + to:main::@5 +main::@5: scope:[main] from main::@3 + [9] main::i#1 = ++ main::i#2 + to:main::@1 diff --git a/src/test/ref/block-error-1.log b/src/test/ref/block-error-1.log new file mode 100644 index 000000000..1d9ff4633 --- /dev/null +++ b/src/test/ref/block-error-1.log @@ -0,0 +1,389 @@ + +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::@6 + main::i#2 = phi( main/main::i#0, main::@6/main::i#1 ) + main::$0 = main::i#2 != 8 + if(main::$0) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + main::i#7 = phi( main::@1/main::i#2 ) + main::pos#0 = 1 + to:main::@3 +main::@3: scope:[main] from main::@2 main::@4 + main::i#5 = phi( main::@2/main::i#7, main::@4/main::i#4 ) + main::pos#2 = phi( main::@2/main::pos#0, main::@4/main::pos#3 ) + main::pos#1 = main::pos#2 + main::move + main::$3 = 0 != main::pos#1 + main::$1 = ! main::$3 + if(main::$1) goto main::@4 + to:main::@5 +main::@4: scope:[main] from main::@3 main::@5 + main::i#4 = phi( main::@3/main::i#5, main::@5/main::i#6 ) + main::pos#3 = phi( main::@3/main::pos#1, main::@5/main::pos#4 ) + main::$2 = main::pos#3 && main::vacant + if(main::$2) goto main::@3 + to:main::@6 +main::@5: scope:[main] from main::@3 + main::i#6 = phi( main::@3/main::i#5 ) + main::pos#4 = phi( main::@3/main::pos#1 ) + to:main::@4 +main::@6: scope:[main] from main::@4 + main::i#3 = phi( main::@4/main::i#4 ) + 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 +void __start() +void main() +bool~ main::$0 +bool~ main::$1 +bool~ main::$2 +bool~ main::$3 +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 +constant byte main::move = 1 +byte main::pos +byte main::pos#0 +byte main::pos#1 +byte main::pos#2 +byte main::pos#3 +byte main::pos#4 +constant byte main::vacant = 1 + +Adding number conversion cast (unumber) 8 in main::$0 = main::i#2 != 8 +Adding number conversion cast (unumber) 0 in main::$3 = 0 != main::pos#1 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 8 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 8 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inversing boolean not [9] main::$1 = 0 == main::pos#1 from [8] main::$3 = 0 != main::pos#1 +Successful SSA optimization Pass2UnaryNotSimplification +Alias main::i#2 = main::i#7 +Alias main::pos#1 = main::pos#4 +Alias main::i#5 = main::i#6 +Alias main::i#3 = main::i#4 +Successful SSA optimization Pass2AliasElimination +Alias main::pos#1 = main::pos#3 +Alias main::i#3 = main::i#5 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values main::i#3 main::i#2 +Successful SSA optimization Pass2IdenticalPhiElimination +Simple Condition main::$0 [3] if(main::i#2!=8) goto main::@2 +Simple Condition main::$1 [8] if(0==main::pos#1) goto main::@4 +Successful SSA optimization Pass2ConditionalJumpSimplification +Rewriting && if()-condition to two if()s [9] main::$2 = main::pos#1 && main::vacant +Successful SSA optimization Pass2ConditionalAndOrRewriting +Warning! Adding boolean cast to non-boolean condition main::pos#1 +Warning! Adding boolean cast to non-boolean condition main::vacant +Constant right-side identified main::$5 = 0 != main::vacant +Successful SSA optimization Pass2ConstantRValueConsolidation +Constant main::i#0 = 0 +Constant main::pos#0 = 1 +Constant main::$5 = 0!=main::vacant +Successful SSA optimization Pass2ConstantIdentification +if() condition always true - replacing block destination if(main::$5) goto main::@3 +Successful SSA optimization Pass2ConstantIfs +Eliminating unused constant main::$5 +Successful SSA optimization PassNEliminateUnusedVars +Eliminating unused constant main::vacant +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) 0 in [5] main::$4 = 0 != main::pos#1 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simple Condition main::$4 [6] if(0!=main::pos#1) goto main::@7 +Successful SSA optimization Pass2ConditionalJumpSimplification +Inlining constant with var siblings main::i#0 +Inlining constant with var siblings main::pos#0 +Constant inlined main::i#0 = 0 +Constant inlined main::pos#0 = 1 +Successful SSA optimization Pass2ConstantInlining +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@2 +Adding NOP phi() at start of main::@5 +CALL GRAPH + +Created 2 initial phi equivalence classes +Coalesced [11] main::i#8 = main::i#1 +Coalesced [12] main::pos#5 = main::pos#1 +Coalesced down to 2 phi equivalence classes +Culled Empty Block label main::@2 +Culled Empty Block label main::@7 +Renumbering block main::@3 to main::@2 +Renumbering block main::@4 to main::@3 +Renumbering block main::@5 to main::@4 +Renumbering block main::@6 to main::@5 +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@4 + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@5 + [1] main::i#2 = phi( main/0, main::@5/main::i#1 ) + [2] if(main::i#2!=8) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [3] return + to:@return +main::@2: scope:[main] from main::@1 main::@3 + [4] main::pos#2 = phi( main::@1/1, main::@3/main::pos#1 ) + [5] main::pos#1 = main::pos#2 + main::move + [6] if(0==main::pos#1) goto main::@3 + to:main::@4 +main::@4: scope:[main] from main::@2 + [7] phi() + to:main::@3 +main::@3: scope:[main] from main::@2 main::@4 + [8] if(0!=main::pos#1) goto main::@2 + to:main::@5 +main::@5: scope:[main] from main::@3 + [9] main::i#1 = ++ main::i#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +void main() +byte main::i +byte main::i#1 22.0 +byte main::i#2 4.714285714285714 +byte main::pos +byte main::pos#1 101.0 +byte main::pos#2 202.0 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +[ main::pos#2 main::pos#1 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +[ main::pos#2 main::pos#1 ] +Allocated zp[1]:2 [ main::i#2 main::i#1 ] +Allocated zp[1]:3 [ main::pos#2 main::pos#1 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [5] main::pos#1 = main::pos#2 + main::move [ main::i#2 main::pos#1 ] ( [ main::i#2 main::pos#1 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:2 [ main::i#2 main::i#1 ] +Statement [5] main::pos#1 = main::pos#2 + main::move [ main::i#2 main::pos#1 ] ( [ main::i#2 main::pos#1 ] { } ) always clobbers reg byte a +Potential registers zp[1]:2 [ main::i#2 main::i#1 ] : zp[1]:2 , reg byte x , reg byte y , +Potential registers zp[1]:3 [ main::pos#2 main::pos#1 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 303: zp[1]:3 [ main::pos#2 main::pos#1 ] 26.71: zp[1]:2 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 2911 combination reg byte a [ main::pos#2 main::pos#1 ] reg byte x [ main::i#2 main::i#1 ] +Uplifting [] best 2911 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Demonstrates error "Block referenced, but not found in program." + // Upstart + // Commodore 64 PRG executable file +.file [name="block-error-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: { + .const move = 1 + // [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 + // main::@1 + __b1: + // [2] if(main::i#2!=8) goto main::@2 -- vbuxx_neq_vbuc1_then_la1 + cpx #8 + bne __b2_from___b1 + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // [4] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + __b2_from___b1: + // [4] phi main::pos#2 = 1 [phi:main::@1->main::@2#0] -- vbuaa=vbuc1 + lda #1 + jmp __b2 + // [4] phi from main::@3 to main::@2 [phi:main::@3->main::@2] + __b2_from___b3: + // [4] phi main::pos#2 = main::pos#1 [phi:main::@3->main::@2#0] -- register_copy + jmp __b2 + // main::@2 + __b2: + // [5] main::pos#1 = main::pos#2 + main::move -- vbuaa=vbuaa_plus_vbuc1 + clc + adc #move + // [6] if(0==main::pos#1) goto main::@3 -- 0_eq_vbuaa_then_la1 + cmp #0 + beq __b3 + // [7] phi from main::@2 to main::@4 [phi:main::@2->main::@4] + __b4_from___b2: + jmp __b4 + // main::@4 + __b4: + jmp __b3 + // main::@3 + __b3: + // [8] if(0!=main::pos#1) goto main::@2 -- 0_neq_vbuaa_then_la1 + cmp #0 + bne __b2_from___b3 + jmp __b5 + // main::@5 + __b5: + // [9] main::i#1 = ++ main::i#2 -- vbuxx=_inc_vbuxx + inx + // [1] phi from main::@5 to main::@1 [phi:main::@5->main::@1] + __b1_from___b5: + // [1] phi main::i#2 = main::i#1 [phi:main::@5->main::@1#0] -- register_copy + jmp __b1 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Removing instruction jmp __b2 +Removing instruction jmp __b4 +Removing instruction jmp __b3 +Removing instruction jmp __b5 +Succesful ASM optimization Pass5NextJumpElimination +Replacing label __b2_from___b3 with __b2 +Removing instruction __b2_from___b3: +Removing instruction __b4_from___b2: +Removing instruction __b4: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __b1_from_main: +Removing instruction __breturn: +Removing instruction __b5: +Removing instruction __b1_from___b5: +Succesful ASM optimization Pass5UnusedLabelElimination +Relabelling long label __b2_from___b1 to __b4 +Succesful ASM optimization Pass5RelabelLongLabels +Removing instruction jmp __b2 +Removing instruction beq __b3 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b3: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +void main() +byte main::i +byte main::i#1 reg byte x 22.0 +byte main::i#2 reg byte x 4.714285714285714 +constant byte main::move = 1 +byte main::pos +byte main::pos#1 reg byte a 101.0 +byte main::pos#2 reg byte a 202.0 + +reg byte x [ main::i#2 main::i#1 ] +reg byte a [ main::pos#2 main::pos#1 ] + + +FINAL ASSEMBLER +Score: 1371 + + // File Comments +// Demonstrates error "Block referenced, but not found in program." + // Upstart + // Commodore 64 PRG executable file +.file [name="block-error-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: { + .const move = 1 + // [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 + // main::@1 + __b1: + // for(char i=0;i!=8;i++) + // [2] if(main::i#2!=8) goto main::@2 -- vbuxx_neq_vbuc1_then_la1 + cpx #8 + bne __b4 + // main::@return + // } + // [3] return + rts + // [4] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + __b4: + // [4] phi main::pos#2 = 1 [phi:main::@1->main::@2#0] -- vbuaa=vbuc1 + lda #1 + // [4] phi from main::@3 to main::@2 [phi:main::@3->main::@2] + // [4] phi main::pos#2 = main::pos#1 [phi:main::@3->main::@2#0] -- register_copy + // main::@2 + __b2: + // pos += move + // [5] main::pos#1 = main::pos#2 + main::move -- vbuaa=vbuaa_plus_vbuc1 + clc + adc #move + // if(pos) + // [6] if(0==main::pos#1) goto main::@3 -- 0_eq_vbuaa_then_la1 + cmp #0 + // [7] phi from main::@2 to main::@4 [phi:main::@2->main::@4] + // main::@4 + // main::@3 + // while(pos && vacant) + // [8] if(0!=main::pos#1) goto main::@2 -- 0_neq_vbuaa_then_la1 + cmp #0 + bne __b2 + // main::@5 + // for(char i=0;i!=8;i++) + // [9] main::i#1 = ++ main::i#2 -- vbuxx=_inc_vbuxx + inx + // [1] phi from main::@5 to main::@1 [phi:main::@5->main::@1] + // [1] phi main::i#2 = main::i#1 [phi:main::@5->main::@1#0] -- register_copy + jmp __b1 +} + // File Data + diff --git a/src/test/ref/block-error-1.sym b/src/test/ref/block-error-1.sym new file mode 100644 index 000000000..85a8a0420 --- /dev/null +++ b/src/test/ref/block-error-1.sym @@ -0,0 +1,11 @@ +void main() +byte main::i +byte main::i#1 reg byte x 22.0 +byte main::i#2 reg byte x 4.714285714285714 +constant byte main::move = 1 +byte main::pos +byte main::pos#1 reg byte a 101.0 +byte main::pos#2 reg byte a 202.0 + +reg byte x [ main::i#2 main::i#1 ] +reg byte a [ main::pos#2 main::pos#1 ] diff --git a/src/test/ref/block-error-2.asm b/src/test/ref/block-error-2.asm new file mode 100644 index 000000000..b76ad49c1 --- /dev/null +++ b/src/test/ref/block-error-2.asm @@ -0,0 +1,32 @@ +// Demonstrates error "Sequence does not contain all blocks from the program." + // Commodore 64 PRG executable file +.file [name="block-error-2.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: { + .const move = 1 + ldx #1 + txa + __b1: + // pos += move + clc + adc #move + // if(pos) + cmp #0 + beq __b2 + ldx #0 + __b2: + // while(pos && vacant) + cmp #0 + beq __breturn + cpx #0 + bne __b1 + __breturn: + // } + rts +} diff --git a/src/test/ref/block-error-2.cfg b/src/test/ref/block-error-2.cfg new file mode 100644 index 000000000..cfbf594a9 --- /dev/null +++ b/src/test/ref/block-error-2.cfg @@ -0,0 +1,24 @@ + +void main() +main: scope:[main] from + [0] phi() + to:main::@1 +main::@1: scope:[main] from main main::@3 + [1] main::vacant#3 = phi( main/1, main::@3/main::vacant#2 ) + [1] main::pos#2 = phi( main/1, main::@3/main::pos#1 ) + [2] main::pos#1 = main::pos#2 + main::move + [3] if(0==main::pos#1) goto main::@4 + to:main::@2 +main::@4: scope:[main] from main::@1 + [4] phi() + to:main::@2 +main::@2: scope:[main] from main::@1 main::@4 + [5] main::vacant#2 = phi( main::@4/main::vacant#3, main::@1/0 ) + [6] if(0==main::pos#1) goto main::@return + to:main::@3 +main::@3: scope:[main] from main::@2 + [7] if(0!=main::vacant#2) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@2 main::@3 + [8] return + to:@return diff --git a/src/test/ref/block-error-2.log b/src/test/ref/block-error-2.log new file mode 100644 index 000000000..25f81ba15 --- /dev/null +++ b/src/test/ref/block-error-2.log @@ -0,0 +1,357 @@ + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start + main::pos#0 = 1 + main::vacant#0 = 1 + to:main::@1 +main::@1: scope:[main] from main main::@2 + main::vacant#3 = phi( main/main::vacant#0, main::@2/main::vacant#2 ) + main::pos#2 = phi( main/main::pos#0, main::@2/main::pos#3 ) + main::pos#1 = main::pos#2 + main::move + main::$2 = 0 != main::pos#1 + main::$0 = ! main::$2 + if(main::$0) goto main::@2 + to:main::@3 +main::@2: scope:[main] from main::@1 main::@3 + main::vacant#2 = phi( main::@1/main::vacant#3, main::@3/main::vacant#1 ) + main::pos#3 = phi( main::@1/main::pos#1, main::@3/main::pos#4 ) + main::$1 = main::pos#3 && main::vacant#2 + if(main::$1) goto main::@1 + to:main::@return +main::@3: scope:[main] from main::@1 + main::pos#4 = phi( main::@1/main::pos#1 ) + main::vacant#1 = 0 + to:main::@2 +main::@return: scope:[main] from main::@2 + 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() +bool~ main::$0 +bool~ main::$1 +bool~ main::$2 +constant byte main::move = 1 +byte main::pos +byte main::pos#0 +byte main::pos#1 +byte main::pos#2 +byte main::pos#3 +byte main::pos#4 +byte main::vacant +byte main::vacant#0 +byte main::vacant#1 +byte main::vacant#2 +byte main::vacant#3 + +Adding number conversion cast (unumber) 0 in main::$2 = 0 != main::pos#1 +Adding number conversion cast (unumber) 0 in main::vacant#1 = 0 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast main::vacant#1 = (unumber)0 +Successful SSA optimization Pass2InlineCast +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inversing boolean not [5] main::$0 = 0 == main::pos#1 from [4] main::$2 = 0 != main::pos#1 +Successful SSA optimization Pass2UnaryNotSimplification +Alias main::pos#1 = main::pos#4 +Successful SSA optimization Pass2AliasElimination +Alias main::pos#1 = main::pos#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition main::$0 [5] if(0==main::pos#1) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Rewriting && if()-condition to two if()s [7] main::$1 = main::pos#1 && main::vacant#2 +Successful SSA optimization Pass2ConditionalAndOrRewriting +Warning! Adding boolean cast to non-boolean condition main::pos#1 +Warning! Adding boolean cast to non-boolean condition main::vacant#2 +Constant main::pos#0 = 1 +Constant main::vacant#0 = 1 +Constant main::vacant#1 = 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 +Adding number conversion cast (unumber) 0 in [4] main::$3 = 0 != main::pos#1 +Adding number conversion cast (unumber) 0 in [9] main::$4 = 0 != main::vacant#2 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant integer cast 0 +Simplifying constant integer cast 0 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) 0 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Simple Condition main::$3 [5] if(0!=main::pos#1) goto main::@4 +Simple Condition main::$4 [8] if(0!=main::vacant#2) goto main::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Negating conditional jump and destination [5] if(0==main::pos#1) goto main::@return +Successful SSA optimization Pass2ConditionalJumpSequenceImprovement +Inlining constant with var siblings main::pos#0 +Inlining constant with var siblings main::vacant#0 +Inlining constant with var siblings main::vacant#1 +Constant inlined main::vacant#0 = 1 +Constant inlined main::vacant#1 = 0 +Constant inlined main::pos#0 = 1 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@5(between main::@4 and main::@1) +Added new block during phi lifting main::@6(between main::@1 and main::@2) +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@3 +CALL GRAPH + +Created 3 initial phi equivalence classes +Coalesced [9] main::pos#5 = main::pos#1 +Coalesced [10] main::vacant#4 = main::vacant#2 +Coalesced (already) [11] main::vacant#5 = main::vacant#3 +Coalesced down to 2 phi equivalence classes +Culled Empty Block label main::@3 +Culled Empty Block label main::@5 +Renumbering block main::@4 to main::@3 +Renumbering block main::@6 to main::@4 +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@4 + +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::vacant#3 = phi( main/1, main::@3/main::vacant#2 ) + [1] main::pos#2 = phi( main/1, main::@3/main::pos#1 ) + [2] main::pos#1 = main::pos#2 + main::move + [3] if(0==main::pos#1) goto main::@4 + to:main::@2 +main::@4: scope:[main] from main::@1 + [4] phi() + to:main::@2 +main::@2: scope:[main] from main::@1 main::@4 + [5] main::vacant#2 = phi( main::@4/main::vacant#3, main::@1/0 ) + [6] if(0==main::pos#1) goto main::@return + to:main::@3 +main::@3: scope:[main] from main::@2 + [7] if(0!=main::vacant#2) goto main::@1 + to:main::@return +main::@return: scope:[main] from main::@2 main::@3 + [8] return + to:@return + + +VARIABLE REGISTER WEIGHTS +void main() +byte main::pos +byte main::pos#1 7.333333333333333 +byte main::pos#2 22.0 +byte main::vacant +byte main::vacant#2 11.0 +byte main::vacant#3 5.5 + +Initial phi equivalence classes +[ main::pos#2 main::pos#1 ] +[ main::vacant#3 main::vacant#2 ] +Complete equivalence classes +[ main::pos#2 main::pos#1 ] +[ main::vacant#3 main::vacant#2 ] +Allocated zp[1]:2 [ main::pos#2 main::pos#1 ] +Allocated zp[1]:3 [ main::vacant#3 main::vacant#2 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [2] main::pos#1 = main::pos#2 + main::move [ main::vacant#3 main::pos#1 ] ( [ main::vacant#3 main::pos#1 ] { } ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp[1]:3 [ main::vacant#3 main::vacant#2 ] +Statement [2] main::pos#1 = main::pos#2 + main::move [ main::vacant#3 main::pos#1 ] ( [ main::vacant#3 main::pos#1 ] { } ) always clobbers reg byte a +Potential registers zp[1]:2 [ main::pos#2 main::pos#1 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ main::vacant#3 main::vacant#2 ] : zp[1]:3 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 29.33: zp[1]:2 [ main::pos#2 main::pos#1 ] 16.5: zp[1]:3 [ main::vacant#3 main::vacant#2 ] +Uplift Scope [] + +Uplifting [main] best 451 combination reg byte a [ main::pos#2 main::pos#1 ] reg byte x [ main::vacant#3 main::vacant#2 ] +Uplifting [] best 451 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Demonstrates error "Sequence does not contain all blocks from the program." + // Upstart + // Commodore 64 PRG executable file +.file [name="block-error-2.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: { + .const move = 1 + // [1] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [1] phi main::vacant#3 = 1 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #1 + // [1] phi main::pos#2 = 1 [phi:main->main::@1#1] -- vbuaa=vbuc1 + lda #1 + jmp __b1 + // [1] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + __b1_from___b3: + // [1] phi main::vacant#3 = main::vacant#2 [phi:main::@3->main::@1#0] -- register_copy + // [1] phi main::pos#2 = main::pos#1 [phi:main::@3->main::@1#1] -- register_copy + jmp __b1 + // main::@1 + __b1: + // [2] main::pos#1 = main::pos#2 + main::move -- vbuaa=vbuaa_plus_vbuc1 + clc + adc #move + // [3] if(0==main::pos#1) goto main::@4 -- 0_eq_vbuaa_then_la1 + cmp #0 + beq __b4_from___b1 + // [5] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + __b2_from___b1: + // [5] phi main::vacant#2 = 0 [phi:main::@1->main::@2#0] -- vbuxx=vbuc1 + ldx #0 + jmp __b2 + // [4] phi from main::@1 to main::@4 [phi:main::@1->main::@4] + __b4_from___b1: + jmp __b4 + // main::@4 + __b4: + // [5] phi from main::@4 to main::@2 [phi:main::@4->main::@2] + __b2_from___b4: + // [5] phi main::vacant#2 = main::vacant#3 [phi:main::@4->main::@2#0] -- register_copy + jmp __b2 + // main::@2 + __b2: + // [6] if(0==main::pos#1) goto main::@return -- 0_eq_vbuaa_then_la1 + cmp #0 + beq __breturn + jmp __b3 + // main::@3 + __b3: + // [7] if(0!=main::vacant#2) goto main::@1 -- 0_neq_vbuxx_then_la1 + cpx #0 + bne __b1_from___b3 + jmp __breturn + // main::@return + __breturn: + // [8] return + rts +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __b4 +Removing instruction jmp __b2 +Removing instruction jmp __b3 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Replacing instruction lda #1 with TXA +Replacing label __b4_from___b1 with __b2 +Replacing label __b1_from___b3 with __b1 +Removing instruction __b1_from___b3: +Removing instruction __b4_from___b1: +Removing instruction __b4: +Removing instruction __b2_from___b4: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction __b1_from_main: +Removing instruction __b2_from___b1: +Removing instruction __b3: +Succesful ASM optimization Pass5UnusedLabelElimination +Removing instruction jmp __b1 +Removing instruction jmp __b2 +Succesful ASM optimization Pass5NextJumpElimination + +FINAL SYMBOL TABLE +void main() +constant byte main::move = 1 +byte main::pos +byte main::pos#1 reg byte a 7.333333333333333 +byte main::pos#2 reg byte a 22.0 +byte main::vacant +byte main::vacant#2 reg byte x 11.0 +byte main::vacant#3 reg byte x 5.5 + +reg byte a [ main::pos#2 main::pos#1 ] +reg byte x [ main::vacant#3 main::vacant#2 ] + + +FINAL ASSEMBLER +Score: 241 + + // File Comments +// Demonstrates error "Sequence does not contain all blocks from the program." + // Upstart + // Commodore 64 PRG executable file +.file [name="block-error-2.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: { + .const move = 1 + // [1] phi from main to main::@1 [phi:main->main::@1] + // [1] phi main::vacant#3 = 1 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #1 + // [1] phi main::pos#2 = 1 [phi:main->main::@1#1] -- vbuaa=vbuc1 + txa + // [1] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + // [1] phi main::vacant#3 = main::vacant#2 [phi:main::@3->main::@1#0] -- register_copy + // [1] phi main::pos#2 = main::pos#1 [phi:main::@3->main::@1#1] -- register_copy + // main::@1 + __b1: + // pos += move + // [2] main::pos#1 = main::pos#2 + main::move -- vbuaa=vbuaa_plus_vbuc1 + clc + adc #move + // if(pos) + // [3] if(0==main::pos#1) goto main::@4 -- 0_eq_vbuaa_then_la1 + cmp #0 + beq __b2 + // [5] phi from main::@1 to main::@2 [phi:main::@1->main::@2] + // [5] phi main::vacant#2 = 0 [phi:main::@1->main::@2#0] -- vbuxx=vbuc1 + ldx #0 + // [4] phi from main::@1 to main::@4 [phi:main::@1->main::@4] + // main::@4 + // [5] phi from main::@4 to main::@2 [phi:main::@4->main::@2] + // [5] phi main::vacant#2 = main::vacant#3 [phi:main::@4->main::@2#0] -- register_copy + // main::@2 + __b2: + // while(pos && vacant) + // [6] if(0==main::pos#1) goto main::@return -- 0_eq_vbuaa_then_la1 + cmp #0 + beq __breturn + // main::@3 + // [7] if(0!=main::vacant#2) goto main::@1 -- 0_neq_vbuxx_then_la1 + cpx #0 + bne __b1 + // main::@return + __breturn: + // } + // [8] return + rts +} + // File Data + diff --git a/src/test/ref/block-error-2.sym b/src/test/ref/block-error-2.sym new file mode 100644 index 000000000..98d2c768f --- /dev/null +++ b/src/test/ref/block-error-2.sym @@ -0,0 +1,11 @@ +void main() +constant byte main::move = 1 +byte main::pos +byte main::pos#1 reg byte a 7.333333333333333 +byte main::pos#2 reg byte a 22.0 +byte main::vacant +byte main::vacant#2 reg byte x 11.0 +byte main::vacant#3 reg byte x 5.5 + +reg byte a [ main::pos#2 main::pos#1 ] +reg byte x [ main::vacant#3 main::vacant#2 ]