From ff716c0343d5daa62c5786048b009aa621351ba9 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Mon, 18 Mar 2019 18:24:03 +0100 Subject: [PATCH] Implemented handling of deafult successor PHI blocks in --- .../Pass2ConditionalAndOrRewriting.java | 15 +- .../dk/camelot64/kickc/test/TestPrograms.java | 5 + src/test/kc/complex-conditional-problem.kc | 14 + src/test/ref/complex-conditional-problem.asm | 22 + src/test/ref/complex-conditional-problem.cfg | 26 ++ src/test/ref/complex-conditional-problem.log | 390 ++++++++++++++++++ src/test/ref/complex-conditional-problem.sym | 17 + 7 files changed, 486 insertions(+), 3 deletions(-) create mode 100644 src/test/kc/complex-conditional-problem.kc create mode 100644 src/test/ref/complex-conditional-problem.asm create mode 100644 src/test/ref/complex-conditional-problem.cfg create mode 100644 src/test/ref/complex-conditional-problem.log create mode 100644 src/test/ref/complex-conditional-problem.sym diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java b/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java index e32936584..69cd3f670 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass2ConditionalAndOrRewriting.java @@ -7,6 +7,7 @@ import dk.camelot64.kickc.model.operators.Operators; import dk.camelot64.kickc.model.statements.Statement; import dk.camelot64.kickc.model.statements.StatementAssignment; import dk.camelot64.kickc.model.statements.StatementConditionalJump; +import dk.camelot64.kickc.model.statements.StatementPhiBlock; import dk.camelot64.kickc.model.symbols.Label; import dk.camelot64.kickc.model.symbols.Scope; import dk.camelot64.kickc.model.values.*; @@ -109,7 +110,7 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { } /** - * Rewrite logical || condition if(c1&&c2) { xx } to if(c1) goto x else if(c2) goto x else goto end, x:{ xx } end: + * Rewrite logical || condition if(c1||c2) { xx } to if(c1) goto x else if(c2) goto x else goto end, x:{ xx } end: * @param block The block containing the current if() * @param conditional The if()-statement * @param conditionAssignment The assignment defining the condition variable. @@ -134,14 +135,22 @@ public class Pass2ConditionalAndOrRewriting extends Pass2SsaOptimization { // Remove any unrolling from the original conditional as only the new one leaves the loop conditional.setDeclaredUnroll(false); - // TODO: Fix phi-values inside the destination phi-blocks to reflect the new control flow! Use replaceLabels(block, replacement) ControlFlowBlock conditionalDestBlock = getGraph().getBlock(conditional.getDestination()); if(conditionalDestBlock.hasPhiBlock()) { throw new RuntimeException("TODO: Fix phi-values inside the conditional destination phi-block!"); } + // Update the default destination PHI block to reflect the last of the conditions ControlFlowBlock defaultDestBlock = getGraph().getBlock(newBlock.getDefaultSuccessor()); if(defaultDestBlock.hasPhiBlock()) { - throw new RuntimeException("TODO: Fix phi-values inside the default destination phi-block!"); + StatementPhiBlock defaultDestPhiBlock = defaultDestBlock.getPhiBlock(); + for(StatementPhiBlock.PhiVariable phiVariable : defaultDestPhiBlock.getPhiVariables()) { + for(StatementPhiBlock.PhiRValue phiRValue : phiVariable.getValues()) { + if(phiRValue.getPredecessor().equals(block.getLabel())) { + // Found phi-variable with current block as predecessor - change the predecessor + phiRValue.setPredecessor(newBlock.getLabel()); + } + } + } } diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index daa1ecf26..a51b8ffb2 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -44,6 +44,11 @@ public class TestPrograms { AsmFragmentTemplateUsages.logUsages(log, false, false, false, false, false, false); } + @Test + public void testComplexConditionalProblem() throws IOException, URISyntaxException { + compileAndCompare("complex-conditional-problem"); + } + @Test public void testConstSignedPromotion() throws IOException, URISyntaxException { compileAndCompare("const-signed-promotion"); diff --git a/src/test/kc/complex-conditional-problem.kc b/src/test/kc/complex-conditional-problem.kc new file mode 100644 index 000000000..d0bd2ae7d --- /dev/null +++ b/src/test/kc/complex-conditional-problem.kc @@ -0,0 +1,14 @@ +// Test to provoke Exception when using complex || condition + +const byte* RASTER = $d012; +const byte* SCREEN = $0400; + +void main() { + while(true) { + byte key = *RASTER; + if (key > $20 || key < $40) { + key = 0; + } + *SCREEN = key; + } +} diff --git a/src/test/ref/complex-conditional-problem.asm b/src/test/ref/complex-conditional-problem.asm new file mode 100644 index 000000000..c9e1d07d2 --- /dev/null +++ b/src/test/ref/complex-conditional-problem.asm @@ -0,0 +1,22 @@ +// Test to provoke Exception when using complex || condition +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label RASTER = $d012 + .label SCREEN = $400 +main: { + b2: + lda RASTER + cmp #$20 + beq !+ + bcs b7 + !: + cmp #$40 + bcc b7 + b4: + sta SCREEN + jmp b2 + b7: + lda #0 + jmp b4 +} diff --git a/src/test/ref/complex-conditional-problem.cfg b/src/test/ref/complex-conditional-problem.cfg new file mode 100644 index 000000000..66c43004f --- /dev/null +++ b/src/test/ref/complex-conditional-problem.cfg @@ -0,0 +1,26 @@ +@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::@2 +main::@2: scope:[main] from main main::@4 + [5] (byte) main::key#0 ← *((const byte*) RASTER#0) + [6] if((byte) main::key#0>(byte/signed byte/word/signed word/dword/signed dword) $20) goto main::@7 + to:main::@9 +main::@9: scope:[main] from main::@2 + [7] if((byte) main::key#0<(byte/signed byte/word/signed word/dword/signed dword) $40) goto main::@7 + to:main::@4 +main::@4: scope:[main] from main::@7 main::@9 + [8] (byte) main::key#2 ← phi( main::@9/(byte) main::key#0 main::@7/(byte/signed byte/word/signed word/dword/signed dword) 0 ) + [9] *((const byte*) SCREEN#0) ← (byte) main::key#2 + to:main::@2 +main::@7: scope:[main] from main::@2 main::@9 + [10] phi() + to:main::@4 diff --git a/src/test/ref/complex-conditional-problem.log b/src/test/ref/complex-conditional-problem.log new file mode 100644 index 000000000..e67191f5a --- /dev/null +++ b/src/test/ref/complex-conditional-problem.log @@ -0,0 +1,390 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte*) RASTER#0 ← ((byte*)) (word/dword/signed dword) $d012 + (byte*) SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400 + to:@1 +main: scope:[main] from @1 + to:main::@1 +main::@1: scope:[main] from main main::@4 + if(true) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte) main::key#0 ← *((byte*) RASTER#0) + (bool~) main::$0 ← (byte) main::key#0 > (byte/signed byte/word/signed word/dword/signed dword) $20 + (bool~) main::$1 ← (byte) main::key#0 < (byte/signed byte/word/signed word/dword/signed dword) $40 + (bool~) main::$2 ← (bool~) main::$0 || (bool~) main::$1 + (bool~) main::$3 ← ! (bool~) main::$2 + if((bool~) main::$3) goto main::@4 + to:main::@7 +main::@4: scope:[main] from main::@2 main::@7 + (byte) main::key#2 ← phi( main::@2/(byte) main::key#0 main::@7/(byte) main::key#1 ) + *((byte*) SCREEN#0) ← (byte) main::key#2 + to:main::@1 +main::@7: scope:[main] from main::@2 + (byte) main::key#1 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + to:main::@4 +main::@return: scope:[main] from main::@1 + 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 +(byte*) RASTER +(byte*) RASTER#0 +(byte*) SCREEN +(byte*) SCREEN#0 +(void()) main() +(bool~) main::$0 +(bool~) main::$1 +(bool~) main::$2 +(bool~) main::$3 +(label) main::@1 +(label) main::@2 +(label) main::@4 +(label) main::@7 +(label) main::@return +(byte) main::key +(byte) main::key#0 +(byte) main::key#1 +(byte) main::key#2 + +Culled Empty Block (label) @2 +Successful SSA optimization Pass2CullEmptyBlocks +Rewriting ! if()-condition to reversed if() [7] (bool~) main::$3 ← ! (bool~) main::$2 +Successful SSA optimization Pass2ConditionalAndOrRewriting +Rewriting || if()-condition to two if()s [6] (bool~) main::$2 ← (bool~) main::$0 || (bool~) main::$1 +Successful SSA optimization Pass2ConditionalAndOrRewriting +Constant (const byte*) RASTER#0 = ((byte*))$d012 +Constant (const byte*) SCREEN#0 = ((byte*))$400 +Constant (const byte) main::key#1 = 0 +Successful SSA optimization Pass2ConstantIdentification +if() condition always true - replacing block destination [0] if(true) goto main::@2 +Successful SSA optimization Pass2ConstantIfs +Removing unused block main::@return +Successful SSA optimization Pass2EliminateUnusedBlocks +Culled Empty Block (label) main::@1 +Successful SSA optimization Pass2CullEmptyBlocks +Simple Condition (bool~) main::$0 [3] if((byte) main::key#0>(byte/signed byte/word/signed word/dword/signed dword) $20) goto main::@7 +Simple Condition (bool~) main::$1 [7] if((byte) main::key#0<(byte/signed byte/word/signed word/dword/signed dword) $40) goto main::@7 +Successful SSA optimization Pass2ConditionalJumpSimplification +Inlining constant with var siblings (const byte) main::key#1 +Constant inlined main::key#1 = (byte/signed byte/word/signed word/dword/signed dword) 0 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@10(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 @end +Adding NOP phi() at start of main +Adding NOP phi() at start of main::@7 +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Coalesced [8] main::key#3 ← main::key#0 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) main::@10 +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 +Adding NOP phi() at start of main::@7 + +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::@2 +main::@2: scope:[main] from main main::@4 + [5] (byte) main::key#0 ← *((const byte*) RASTER#0) + [6] if((byte) main::key#0>(byte/signed byte/word/signed word/dword/signed dword) $20) goto main::@7 + to:main::@9 +main::@9: scope:[main] from main::@2 + [7] if((byte) main::key#0<(byte/signed byte/word/signed word/dword/signed dword) $40) goto main::@7 + to:main::@4 +main::@4: scope:[main] from main::@7 main::@9 + [8] (byte) main::key#2 ← phi( main::@9/(byte) main::key#0 main::@7/(byte/signed byte/word/signed word/dword/signed dword) 0 ) + [9] *((const byte*) SCREEN#0) ← (byte) main::key#2 + to:main::@2 +main::@7: scope:[main] from main::@2 main::@9 + [10] phi() + to:main::@4 + + +VARIABLE REGISTER WEIGHTS +(byte*) RASTER +(byte*) SCREEN +(void()) main() +(byte) main::key +(byte) main::key#0 14.666666666666666 +(byte) main::key#2 22.0 + +Initial phi equivalence classes +[ main::key#2 main::key#0 ] +Complete equivalence classes +[ main::key#2 main::key#0 ] +Allocated zp ZP_BYTE:2 [ main::key#2 main::key#0 ] + +INITIAL ASM +//SEG0 File Comments +// Test to provoke Exception when using complex || condition +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .label RASTER = $d012 + .label SCREEN = $400 +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG9 @end +bend: +//SEG10 main +main: { + .label key = 2 + jmp b2 + //SEG11 main::@2 + b2: + //SEG12 [5] (byte) main::key#0 ← *((const byte*) RASTER#0) -- vbuz1=_deref_pbuc1 + lda RASTER + sta key + //SEG13 [6] if((byte) main::key#0>(byte/signed byte/word/signed word/dword/signed dword) $20) goto main::@7 -- vbuz1_gt_vbuc1_then_la1 + lda key + cmp #$20 + beq !+ + bcs b7_from_b2 + !: + jmp b9 + //SEG14 main::@9 + b9: + //SEG15 [7] if((byte) main::key#0<(byte/signed byte/word/signed word/dword/signed dword) $40) goto main::@7 -- vbuz1_lt_vbuc1_then_la1 + lda key + cmp #$40 + bcc b7_from_b9 + //SEG16 [8] phi from main::@9 to main::@4 [phi:main::@9->main::@4] + b4_from_b9: + //SEG17 [8] phi (byte) main::key#2 = (byte) main::key#0 [phi:main::@9->main::@4#0] -- register_copy + jmp b4 + //SEG18 main::@4 + b4: + //SEG19 [9] *((const byte*) SCREEN#0) ← (byte) main::key#2 -- _deref_pbuc1=vbuz1 + lda key + sta SCREEN + jmp b2 + //SEG20 [10] phi from main::@2 main::@9 to main::@7 [phi:main::@2/main::@9->main::@7] + b7_from_b2: + b7_from_b9: + jmp b7 + //SEG21 main::@7 + b7: + //SEG22 [8] phi from main::@7 to main::@4 [phi:main::@7->main::@4] + b4_from_b7: + //SEG23 [8] phi (byte) main::key#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main::@7->main::@4#0] -- vbuz1=vbuc1 + lda #0 + sta key + jmp b4 +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Potential registers zp ZP_BYTE:2 [ main::key#2 main::key#0 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 36.67: zp ZP_BYTE:2 [ main::key#2 main::key#0 ] +Uplift Scope [] + +Uplifting [main] best 407 combination reg byte a [ main::key#2 main::key#0 ] +Uplifting [] best 407 combination + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Test to provoke Exception when using complex || condition +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .label RASTER = $d012 + .label SCREEN = $400 +//SEG3 @begin +bbegin: +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +b1_from_bbegin: + jmp b1 +//SEG5 @1 +b1: +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +main_from_b1: + jsr main +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG9 @end +bend: +//SEG10 main +main: { + jmp b2 + //SEG11 main::@2 + b2: + //SEG12 [5] (byte) main::key#0 ← *((const byte*) RASTER#0) -- vbuaa=_deref_pbuc1 + lda RASTER + //SEG13 [6] if((byte) main::key#0>(byte/signed byte/word/signed word/dword/signed dword) $20) goto main::@7 -- vbuaa_gt_vbuc1_then_la1 + cmp #$20 + beq !+ + bcs b7_from_b2 + !: + jmp b9 + //SEG14 main::@9 + b9: + //SEG15 [7] if((byte) main::key#0<(byte/signed byte/word/signed word/dword/signed dword) $40) goto main::@7 -- vbuaa_lt_vbuc1_then_la1 + cmp #$40 + bcc b7_from_b9 + //SEG16 [8] phi from main::@9 to main::@4 [phi:main::@9->main::@4] + b4_from_b9: + //SEG17 [8] phi (byte) main::key#2 = (byte) main::key#0 [phi:main::@9->main::@4#0] -- register_copy + jmp b4 + //SEG18 main::@4 + b4: + //SEG19 [9] *((const byte*) SCREEN#0) ← (byte) main::key#2 -- _deref_pbuc1=vbuaa + sta SCREEN + jmp b2 + //SEG20 [10] phi from main::@2 main::@9 to main::@7 [phi:main::@2/main::@9->main::@7] + b7_from_b2: + b7_from_b9: + jmp b7 + //SEG21 main::@7 + b7: + //SEG22 [8] phi from main::@7 to main::@4 [phi:main::@7->main::@4] + b4_from_b7: + //SEG23 [8] phi (byte) main::key#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main::@7->main::@4#0] -- vbuaa=vbuc1 + lda #0 + jmp b4 +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b2 +Removing instruction jmp b9 +Removing instruction jmp b4 +Removing instruction jmp b7 +Succesful ASM optimization Pass5NextJumpElimination +Replacing label b7_from_b2 with b7 +Replacing label b7_from_b9 with b7 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b4_from_b9: +Removing instruction b7_from_b2: +Removing instruction b7_from_b9: +Removing instruction b4_from_b7: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b9: +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 +(byte*) RASTER +(const byte*) RASTER#0 RASTER = ((byte*))(word/dword/signed dword) $d012 +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400 +(void()) main() +(label) main::@2 +(label) main::@4 +(label) main::@7 +(label) main::@9 +(byte) main::key +(byte) main::key#0 reg byte a 14.666666666666666 +(byte) main::key#2 reg byte a 22.0 + +reg byte a [ main::key#2 main::key#0 ] + + +FINAL ASSEMBLER +Score: 275 + +//SEG0 File Comments +// Test to provoke Exception when using complex || condition +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .label RASTER = $d012 + .label SCREEN = $400 +//SEG3 @begin +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +//SEG5 @1 +//SEG6 [2] call main +//SEG7 [4] phi from @1 to main [phi:@1->main] +//SEG8 [3] phi from @1 to @end [phi:@1->@end] +//SEG9 @end +//SEG10 main +main: { + //SEG11 main::@2 + b2: + //SEG12 [5] (byte) main::key#0 ← *((const byte*) RASTER#0) -- vbuaa=_deref_pbuc1 + lda RASTER + //SEG13 [6] if((byte) main::key#0>(byte/signed byte/word/signed word/dword/signed dword) $20) goto main::@7 -- vbuaa_gt_vbuc1_then_la1 + cmp #$20 + beq !+ + bcs b7 + !: + //SEG14 main::@9 + //SEG15 [7] if((byte) main::key#0<(byte/signed byte/word/signed word/dword/signed dword) $40) goto main::@7 -- vbuaa_lt_vbuc1_then_la1 + cmp #$40 + bcc b7 + //SEG16 [8] phi from main::@9 to main::@4 [phi:main::@9->main::@4] + //SEG17 [8] phi (byte) main::key#2 = (byte) main::key#0 [phi:main::@9->main::@4#0] -- register_copy + //SEG18 main::@4 + b4: + //SEG19 [9] *((const byte*) SCREEN#0) ← (byte) main::key#2 -- _deref_pbuc1=vbuaa + sta SCREEN + jmp b2 + //SEG20 [10] phi from main::@2 main::@9 to main::@7 [phi:main::@2/main::@9->main::@7] + //SEG21 main::@7 + b7: + //SEG22 [8] phi from main::@7 to main::@4 [phi:main::@7->main::@4] + //SEG23 [8] phi (byte) main::key#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main::@7->main::@4#0] -- vbuaa=vbuc1 + lda #0 + jmp b4 +} + diff --git a/src/test/ref/complex-conditional-problem.sym b/src/test/ref/complex-conditional-problem.sym new file mode 100644 index 000000000..c65be03cd --- /dev/null +++ b/src/test/ref/complex-conditional-problem.sym @@ -0,0 +1,17 @@ +(label) @1 +(label) @begin +(label) @end +(byte*) RASTER +(const byte*) RASTER#0 RASTER = ((byte*))(word/dword/signed dword) $d012 +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400 +(void()) main() +(label) main::@2 +(label) main::@4 +(label) main::@7 +(label) main::@9 +(byte) main::key +(byte) main::key#0 reg byte a 14.666666666666666 +(byte) main::key#2 reg byte a 22.0 + +reg byte a [ main::key#2 main::key#0 ]