diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 5699cb209..30a768a41 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 { public TestPrograms() { } + @Test + public void testStrengthReduction1() throws IOException, URISyntaxException { + compileAndCompare("strength-reduction-1.c"); + } + @Test public void testProblemBoolCompare2() throws IOException, URISyntaxException { compileAndCompare("problem-bool-compare-2.c"); diff --git a/src/test/kc/strength-reduction-1.c b/src/test/kc/strength-reduction-1.c new file mode 100644 index 000000000..5f0bdd0f8 --- /dev/null +++ b/src/test/kc/strength-reduction-1.c @@ -0,0 +1,10 @@ +// Test loop invariant computation detection +// http://www.cs.toronto.edu/~pekhimenko/courses/cscd70-w18/docs/Lecture%205%20[LICM%20and%20Strength%20Reduction]%2002.08.2018.pdf +char * SCREEN = 0x0400; +void main() { + char x = *SCREEN; + for(char c=0;c<40;c++) { + // x+5 is a loop invariant computation + SCREEN[c] = x+5; + } +} \ No newline at end of file diff --git a/src/test/ref/strength-reduction-1.asm b/src/test/ref/strength-reduction-1.asm new file mode 100644 index 000000000..b8058fa39 --- /dev/null +++ b/src/test/ref/strength-reduction-1.asm @@ -0,0 +1,28 @@ +// Test loop invariant computation detection +// http://www.cs.toronto.edu/~pekhimenko/courses/cscd70-w18/docs/Lecture%205%20[LICM%20and%20Strength%20Reduction]%2002.08.2018.pdf +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + // x = *SCREEN + ldx SCREEN + ldy #0 + __b1: + // for(char c=0;c<40;c++) + cpy #$28 + bcc __b2 + // } + rts + __b2: + // x+5 + txa + clc + adc #5 + // SCREEN[c] = x+5 + // x+5 is a loop invariant computation + sta SCREEN,y + // for(char c=0;c<40;c++) + iny + jmp __b1 +} diff --git a/src/test/ref/strength-reduction-1.cfg b/src/test/ref/strength-reduction-1.cfg new file mode 100644 index 000000000..921cef07d --- /dev/null +++ b/src/test/ref/strength-reduction-1.cfg @@ -0,0 +1,17 @@ + +void main() +main: scope:[main] from + [0] main::x#0 = *SCREEN + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] main::c#2 = phi( main/0, main::@2/main::c#1 ) + [2] if(main::c#2<$28) 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::$1 = main::x#0 + 5 + [5] SCREEN[main::c#2] = main::$1 + [6] main::c#1 = ++ main::c#2 + to:main::@1 diff --git a/src/test/ref/strength-reduction-1.log b/src/test/ref/strength-reduction-1.log new file mode 100644 index 000000000..10aacf3cc --- /dev/null +++ b/src/test/ref/strength-reduction-1.log @@ -0,0 +1,266 @@ +Inlined call call __init + +CONTROL FLOW GRAPH SSA + +void main() +main: scope:[main] from __start::@1 + main::x#0 = *SCREEN + main::c#0 = 0 + to:main::@1 +main::@1: scope:[main] from main main::@2 + main::x#2 = phi( main/main::x#0, main::@2/main::x#1 ) + main::c#2 = phi( main/main::c#0, main::@2/main::c#1 ) + main::$0 = main::c#2 < $28 + if(main::$0) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + main::c#3 = phi( main::@1/main::c#2 ) + main::x#1 = phi( main::@1/main::x#2 ) + main::$1 = main::x#1 + 5 + SCREEN[main::c#3] = main::$1 + main::c#1 = ++ main::c#3 + to:main::@1 +main::@return: scope:[main] from main::@1 + return + to:@return + +void __start() +__start: scope:[__start] from + to:__start::__init1 +__start::__init1: scope:[__start] from __start + to:__start::@1 +__start::@1: scope:[__start] from __start::__init1 + call main + to:__start::@2 +__start::@2: scope:[__start] from __start::@1 + to:__start::@return +__start::@return: scope:[__start] from __start::@2 + return + to:@return + +SYMBOL TABLE SSA +const byte* SCREEN = (byte*)$400 +void __start() +void main() +bool~ main::$0 +number~ main::$1 +byte main::c +byte main::c#0 +byte main::c#1 +byte main::c#2 +byte main::c#3 +byte main::x +byte main::x#0 +byte main::x#1 +byte main::x#2 + +Adding number conversion cast (unumber) $28 in main::$0 = main::c#2 < $28 +Adding number conversion cast (unumber) 5 in main::$1 = main::x#1 + 5 +Adding number conversion cast (unumber) main::$1 in main::$1 = main::x#1 + (unumber)5 +Successful SSA optimization PassNAddNumberTypeConversions +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast $28 +Simplifying constant integer cast 5 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type $28 +Finalized unsigned number type 5 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Inferred type updated to byte in main::$1 = main::x#1 + 5 +Alias main::x#1 = main::x#2 +Alias main::c#2 = main::c#3 +Successful SSA optimization Pass2AliasElimination +Identical Phi Values main::x#1 main::x#0 +Successful SSA optimization Pass2IdenticalPhiElimination +Simple Condition main::$0 [4] if(main::c#2<$28) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant main::c#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Removing unused procedure __start +Removing unused procedure block __start +Removing unused procedure block __start::__init1 +Removing unused procedure block __start::@1 +Removing unused procedure block __start::@2 +Removing unused procedure block __start::@return +Successful SSA optimization PassNEliminateEmptyStart +Inlining constant with var siblings main::c#0 +Constant inlined main::c#0 = 0 +Successful SSA optimization Pass2ConstantInlining +CALL GRAPH + +Created 1 initial phi equivalence classes +Coalesced [7] main::c#4 = main::c#1 +Coalesced down to 1 phi equivalence classes + +FINAL CONTROL FLOW GRAPH + +void main() +main: scope:[main] from + [0] main::x#0 = *SCREEN + to:main::@1 +main::@1: scope:[main] from main main::@2 + [1] main::c#2 = phi( main/0, main::@2/main::c#1 ) + [2] if(main::c#2<$28) 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::$1 = main::x#0 + 5 + [5] SCREEN[main::c#2] = main::$1 + [6] main::c#1 = ++ main::c#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +void main() +byte~ main::$1 22.0 +byte main::c +byte main::c#1 22.0 +byte main::c#2 11.0 +byte main::x +byte main::x#0 2.1666666666666665 + +Initial phi equivalence classes +[ main::c#2 main::c#1 ] +Added variable main::x#0 to live range equivalence class [ main::x#0 ] +Added variable main::$1 to live range equivalence class [ main::$1 ] +Complete equivalence classes +[ main::c#2 main::c#1 ] +[ main::x#0 ] +[ main::$1 ] +Allocated zp[1]:2 [ main::c#2 main::c#1 ] +Allocated zp[1]:3 [ main::x#0 ] +Allocated zp[1]:4 [ main::$1 ] +REGISTER UPLIFT POTENTIAL REGISTERS +Potential registers zp[1]:2 [ main::c#2 main::c#1 ] : zp[1]:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:3 [ main::x#0 ] : zp[1]:3 , reg byte a , reg byte x , reg byte y , +Potential registers zp[1]:4 [ main::$1 ] : zp[1]:4 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 33: zp[1]:2 [ main::c#2 main::c#1 ] 22: zp[1]:4 [ main::$1 ] 2.17: zp[1]:3 [ main::x#0 ] +Uplift Scope [] + +Uplifting [main] best 295 combination reg byte y [ main::c#2 main::c#1 ] reg byte a [ main::$1 ] reg byte x [ main::x#0 ] +Uplifting [] best 295 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test loop invariant computation detection +// http://www.cs.toronto.edu/~pekhimenko/courses/cscd70-w18/docs/Lecture%205%20[LICM%20and%20Strength%20Reduction]%2002.08.2018.pdf + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // main +main: { + // [0] main::x#0 = *SCREEN -- vbuxx=_deref_pbuc1 + ldx SCREEN + // [1] phi from main to main::@1 [phi:main->main::@1] + __b1_from_main: + // [1] phi main::c#2 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1 + ldy #0 + jmp __b1 + // main::@1 + __b1: + // [2] if(main::c#2<$28) goto main::@2 -- vbuyy_lt_vbuc1_then_la1 + cpy #$28 + bcc __b2 + jmp __breturn + // main::@return + __breturn: + // [3] return + rts + // main::@2 + __b2: + // [4] main::$1 = main::x#0 + 5 -- vbuaa=vbuxx_plus_vbuc1 + txa + clc + adc #5 + // [5] SCREEN[main::c#2] = main::$1 -- pbuc1_derefidx_vbuyy=vbuaa + // x+5 is a loop invariant computation + sta SCREEN,y + // [6] main::c#1 = ++ main::c#2 -- vbuyy=_inc_vbuyy + iny + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + __b1_from___b2: + // [1] phi main::c#2 = main::c#1 [phi:main::@2->main::@1#0] -- register_copy + jmp __b1 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp __b1 +Removing instruction jmp __breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction __b1_from_main: +Removing instruction __breturn: +Removing instruction __b1_from___b2: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +const byte* SCREEN = (byte*) 1024 +void main() +byte~ main::$1 reg byte a 22.0 +byte main::c +byte main::c#1 reg byte y 22.0 +byte main::c#2 reg byte y 11.0 +byte main::x +byte main::x#0 reg byte x 2.1666666666666665 + +reg byte y [ main::c#2 main::c#1 ] +reg byte x [ main::x#0 ] +reg byte a [ main::$1 ] + + +FINAL ASSEMBLER +Score: 235 + + // File Comments +// Test loop invariant computation detection +// http://www.cs.toronto.edu/~pekhimenko/courses/cscd70-w18/docs/Lecture%205%20[LICM%20and%20Strength%20Reduction]%2002.08.2018.pdf + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // main +main: { + // x = *SCREEN + // [0] main::x#0 = *SCREEN -- vbuxx=_deref_pbuc1 + ldx SCREEN + // [1] phi from main to main::@1 [phi:main->main::@1] + // [1] phi main::c#2 = 0 [phi:main->main::@1#0] -- vbuyy=vbuc1 + ldy #0 + // main::@1 + __b1: + // for(char c=0;c<40;c++) + // [2] if(main::c#2<$28) goto main::@2 -- vbuyy_lt_vbuc1_then_la1 + cpy #$28 + bcc __b2 + // main::@return + // } + // [3] return + rts + // main::@2 + __b2: + // x+5 + // [4] main::$1 = main::x#0 + 5 -- vbuaa=vbuxx_plus_vbuc1 + txa + clc + adc #5 + // SCREEN[c] = x+5 + // [5] SCREEN[main::c#2] = main::$1 -- pbuc1_derefidx_vbuyy=vbuaa + // x+5 is a loop invariant computation + sta SCREEN,y + // for(char c=0;c<40;c++) + // [6] main::c#1 = ++ main::c#2 -- vbuyy=_inc_vbuyy + iny + // [1] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [1] phi main::c#2 = main::c#1 [phi:main::@2->main::@1#0] -- register_copy + jmp __b1 +} + // File Data + diff --git a/src/test/ref/strength-reduction-1.sym b/src/test/ref/strength-reduction-1.sym new file mode 100644 index 000000000..bcbd2e112 --- /dev/null +++ b/src/test/ref/strength-reduction-1.sym @@ -0,0 +1,12 @@ +const byte* SCREEN = (byte*) 1024 +void main() +byte~ main::$1 reg byte a 22.0 +byte main::c +byte main::c#1 reg byte y 22.0 +byte main::c#2 reg byte y 11.0 +byte main::x +byte main::x#0 reg byte x 2.1666666666666665 + +reg byte y [ main::c#2 main::c#1 ] +reg byte x [ main::x#0 ] +reg byte a [ main::$1 ]