From 764e0c1069a3a2ffc8cd2281a6a3f27db4c72079 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Thu, 1 Aug 2019 21:15:33 +0200 Subject: [PATCH] Added some basic loop tests. --- .../Pass0GenerateStatementSequence.java | 16 +- .../dk/camelot64/kickc/test/TestPrograms.java | 15 + src/test/kc/loop-for-sideeffect.kc | 12 + src/test/kc/loop-while-min.kc | 13 + src/test/kc/loop-while-sideeffect.kc | 12 + src/test/ref/loop-for-sideeffect.asm | 23 + src/test/ref/loop-for-sideeffect.cfg | 27 ++ src/test/ref/loop-for-sideeffect.log | 398 ++++++++++++++++++ src/test/ref/loop-for-sideeffect.sym | 17 + src/test/ref/loop-while-min.asm | 17 + src/test/ref/loop-while-min.cfg | 23 + src/test/ref/loop-while-min.log | 344 +++++++++++++++ src/test/ref/loop-while-min.sym | 14 + src/test/ref/loop-while-sideeffect.asm | 22 + src/test/ref/loop-while-sideeffect.cfg | 27 ++ src/test/ref/loop-while-sideeffect.log | 397 +++++++++++++++++ src/test/ref/loop-while-sideeffect.sym | 17 + 17 files changed, 1387 insertions(+), 7 deletions(-) create mode 100644 src/test/kc/loop-for-sideeffect.kc create mode 100644 src/test/kc/loop-while-min.kc create mode 100644 src/test/kc/loop-while-sideeffect.kc create mode 100644 src/test/ref/loop-for-sideeffect.asm create mode 100644 src/test/ref/loop-for-sideeffect.cfg create mode 100644 src/test/ref/loop-for-sideeffect.log create mode 100644 src/test/ref/loop-for-sideeffect.sym create mode 100644 src/test/ref/loop-while-min.asm create mode 100644 src/test/ref/loop-while-min.cfg create mode 100644 src/test/ref/loop-while-min.log create mode 100644 src/test/ref/loop-while-min.sym create mode 100644 src/test/ref/loop-while-sideeffect.asm create mode 100644 src/test/ref/loop-while-sideeffect.cfg create mode 100644 src/test/ref/loop-while-sideeffect.log create mode 100644 src/test/ref/loop-while-sideeffect.sym diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 9daf3a9ee..7bd1583b5 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -953,15 +953,17 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { addLoopBody(stmtForCtx.stmt()); addLoopContinueLabel(loopStack.peek(), ctx); // Add increment - if(ctx.commaExpr(1) != null) { - PrePostModifierHandler.addPreModifiers(this, ctx.commaExpr(1), StatementSource.forClassic(ctx)); - this.visit(ctx.commaExpr(1)); - PrePostModifierHandler.addPostModifiers(this, ctx.commaExpr(1), StatementSource.forClassic(ctx)); + KickCParser.CommaExprContext incrementCtx = ctx.commaExpr(1); + if(incrementCtx != null) { + PrePostModifierHandler.addPreModifiers(this, incrementCtx, StatementSource.forClassic(ctx)); + this.visit(incrementCtx); + PrePostModifierHandler.addPostModifiers(this, incrementCtx, StatementSource.forClassic(ctx)); } // Add condition - PrePostModifierHandler.addPreModifiers(this, ctx.commaExpr(0), StatementSource.forClassic(ctx)); - RValue rValue = (RValue) this.visit(ctx.commaExpr(0)); - PrePostModifierHandler.addPostModifiers(this, ctx.commaExpr(0), StatementSource.forClassic(ctx)); + KickCParser.CommaExprContext conditionCtx = ctx.commaExpr(0); + PrePostModifierHandler.addPreModifiers(this, conditionCtx, StatementSource.forClassic(ctx)); + RValue rValue = (RValue) this.visit(conditionCtx); + PrePostModifierHandler.addPostModifiers(this, conditionCtx, StatementSource.forClassic(ctx)); // Add jump if condition was met StatementConditionalJump doJmpStmt = new StatementConditionalJump(rValue, repeatLabel.getRef(), StatementSource.forClassic(ctx), Comment.NO_COMMENTS); sequence.addStatement(doJmpStmt); diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index e15d52fb4..5221b705a 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -2491,6 +2491,21 @@ public class TestPrograms { compileAndCompare("ifmin"); } + @Test + public void testLoopWhileMin() throws IOException, URISyntaxException { + compileAndCompare("loop-while-min"); + } + + @Test + public void testLoopWhileSideeffect() throws IOException, URISyntaxException { + compileAndCompare("loop-while-sideeffect"); + } + + @Test + public void testLoopForSideeffect() throws IOException, URISyntaxException { + compileAndCompare("loop-for-sideeffect"); + } + @Test public void testForClassicMin() throws IOException, URISyntaxException { compileAndCompare("forclassicmin"); diff --git a/src/test/kc/loop-for-sideeffect.kc b/src/test/kc/loop-for-sideeffect.kc new file mode 100644 index 000000000..c0c3a9402 --- /dev/null +++ b/src/test/kc/loop-for-sideeffect.kc @@ -0,0 +1,12 @@ +// Test a for()-loop where the condition has a side-effect +// Currently not standard C compliant (since the condition is not evaluated before the body) + +char* SCREEN = 0x0400; + +void main(void) { + char i; + for(i=7;i++<7;) + SCREEN[i] = i; + // The condition-evaluation should increment i - even if when it is not met - x should end up in 0x0408 + (SCREEN)[i] = 'x'; +} \ No newline at end of file diff --git a/src/test/kc/loop-while-min.kc b/src/test/kc/loop-while-min.kc new file mode 100644 index 000000000..8568894ff --- /dev/null +++ b/src/test/kc/loop-while-min.kc @@ -0,0 +1,13 @@ + +// Minimal classic while() loop + +char* SCREEN = 0x0400; + +void main() { + char i = 0; + while(i!=100) { + SCREEN[i] = i; + i++; + } +} + diff --git a/src/test/kc/loop-while-sideeffect.kc b/src/test/kc/loop-while-sideeffect.kc new file mode 100644 index 000000000..41881df50 --- /dev/null +++ b/src/test/kc/loop-while-sideeffect.kc @@ -0,0 +1,12 @@ +// Test a while()-loop where the condition has a side-effect + +char* SCREEN = 0x0400; + +void main(void) { + char i = 7; + while(i++!=7) { + SCREEN[i] = i; + } + // The condition-evaluation should increment i - even if when it is not met + (SCREEN)[i] = 'x'; +} \ No newline at end of file diff --git a/src/test/ref/loop-for-sideeffect.asm b/src/test/ref/loop-for-sideeffect.asm new file mode 100644 index 000000000..a03abf473 --- /dev/null +++ b/src/test/ref/loop-for-sideeffect.asm @@ -0,0 +1,23 @@ +// Test a for()-loop where the condition has a side-effect +// Currently not standard C compliant (since the condition is not evaluated before the body) +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + lda #7 + b1: + tax + sta SCREEN,x + tax + inx + cmp #7 + bcc b3 + // The condition-evaluation should increment i - even if when it is not met - x should end up in 0x0408 + lda #'x' + sta SCREEN,x + rts + b3: + txa + jmp b1 +} diff --git a/src/test/ref/loop-for-sideeffect.cfg b/src/test/ref/loop-for-sideeffect.cfg new file mode 100644 index 000000000..86206b4d5 --- /dev/null +++ b/src/test/ref/loop-for-sideeffect.cfg @@ -0,0 +1,27 @@ +@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::@1 +main::@1: scope:[main] from main main::@3 + [5] (byte) main::i#3 ← phi( main/(byte) 7 main::@3/(byte~) main::i#5 ) + [6] *((const byte*) SCREEN#0 + (byte) main::i#3) ← (byte) main::i#3 + [7] (byte) main::i#2 ← ++ (byte) main::i#3 + [8] if((byte) main::i#3<(byte) 7) goto main::@3 + to:main::@2 +main::@2: scope:[main] from main::@1 + [9] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte) 'x' + to:main::@return +main::@return: scope:[main] from main::@2 + [10] return + to:@return +main::@3: scope:[main] from main::@1 + [11] (byte~) main::i#5 ← (byte) main::i#2 + to:main::@1 diff --git a/src/test/ref/loop-for-sideeffect.log b/src/test/ref/loop-for-sideeffect.log new file mode 100644 index 000000000..ef33c4c58 --- /dev/null +++ b/src/test/ref/loop-for-sideeffect.log @@ -0,0 +1,398 @@ +Identified constant variable (byte*) SCREEN + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte*) SCREEN#0 ← ((byte*)) (number) $400 + to:@1 +main: scope:[main] from @1 + (byte) main::i#0 ← (byte) 0 + (byte) main::i#1 ← (number) 7 + to:main::@1 +main::@1: scope:[main] from main main::@1 + (byte) main::i#3 ← phi( main/(byte) main::i#1 main::@1/(byte) main::i#2 ) + *((byte*) SCREEN#0 + (byte) main::i#3) ← (byte) main::i#3 + (bool~) main::$0 ← (byte) main::i#3 < (number) 7 + (byte) main::i#2 ← ++ (byte) main::i#3 + if((bool~) main::$0) goto main::@1 + to:main::@2 +main::@2: scope:[main] from main::@1 + (byte) main::i#4 ← phi( main::@1/(byte) main::i#2 ) + *((byte*) SCREEN#0 + (byte) main::i#4) ← (byte) 'x' + to:main::@return +main::@return: scope:[main] from main::@2 + 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*) SCREEN +(byte*) SCREEN#0 +(void()) main() +(bool~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(byte) main::i#4 + +Adding number conversion cast (unumber) 7 in (byte) main::i#1 ← (number) 7 +Adding number conversion cast (unumber) 7 in (bool~) main::$0 ← (byte) main::i#3 < (number) 7 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400 +Inlining cast (byte) main::i#1 ← (unumber)(number) 7 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 7 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) 7 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) main::i#2 = (byte) main::i#4 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$0 [7] if((byte) main::i#3<(byte) 7) goto main::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) SCREEN#0 = (byte*) 1024 +Constant (const byte) main::i#0 = 0 +Constant (const byte) main::i#1 = 7 +Successful SSA optimization Pass2ConstantIdentification +Eliminating unused constant (const byte) main::i#0 +Successful SSA optimization PassNEliminateUnusedVars +Inlining constant with var siblings (const byte) main::i#1 +Constant inlined main::i#1 = (byte) 7 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@3(between main::@1 and main::@1) +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 +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Not coalescing [12] main::i#5 ← main::i#2 +Coalesced down to 2 phi equivalence classes +Culled Empty Block (label) @2 +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::@1 +main::@1: scope:[main] from main main::@3 + [5] (byte) main::i#3 ← phi( main/(byte) 7 main::@3/(byte~) main::i#5 ) + [6] *((const byte*) SCREEN#0 + (byte) main::i#3) ← (byte) main::i#3 + [7] (byte) main::i#2 ← ++ (byte) main::i#3 + [8] if((byte) main::i#3<(byte) 7) goto main::@3 + to:main::@2 +main::@2: scope:[main] from main::@1 + [9] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte) 'x' + to:main::@return +main::@return: scope:[main] from main::@2 + [10] return + to:@return +main::@3: scope:[main] from main::@1 + [11] (byte~) main::i#5 ← (byte) main::i#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +(byte*) SCREEN +(void()) main() +(byte) main::i +(byte) main::i#2 12.0 +(byte) main::i#3 18.333333333333332 +(byte~) main::i#5 22.0 + +Initial phi equivalence classes +[ main::i#3 main::i#5 ] +Added variable main::i#2 to zero page equivalence class [ main::i#2 ] +Complete equivalence classes +[ main::i#3 main::i#5 ] +[ main::i#2 ] +Allocated zp ZP_BYTE:2 [ main::i#3 main::i#5 ] +Allocated zp ZP_BYTE:3 [ main::i#2 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// Test a for()-loop where the condition has a side-effect +// Currently not standard C compliant (since the condition is not evaluated before the body) + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @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 i = 3 + .label i_3 = 2 + .label i_5 = 2 + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#3 = (byte) 7 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #7 + sta i_3 + jmp b1 + // main::@1 + b1: + // [6] *((const byte*) SCREEN#0 + (byte) main::i#3) ← (byte) main::i#3 -- pbuc1_derefidx_vbuz1=vbuz1 + ldy i_3 + tya + sta SCREEN,y + // [7] (byte) main::i#2 ← ++ (byte) main::i#3 -- vbuz1=_inc_vbuz2 + ldy i_3 + iny + sty i + // [8] if((byte) main::i#3<(byte) 7) goto main::@3 -- vbuz1_lt_vbuc1_then_la1 + lda i_3 + cmp #7 + bcc b3 + jmp b2 + // main::@2 + b2: + // [9] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte) 'x' -- pbuc1_derefidx_vbuz1=vbuc2 + // The condition-evaluation should increment i - even if when it is not met - x should end up in 0x0408 + lda #'x' + ldy i + sta SCREEN,y + jmp breturn + // main::@return + breturn: + // [10] return + rts + // main::@3 + b3: + // [11] (byte~) main::i#5 ← (byte) main::i#2 -- vbuz1=vbuz2 + lda i + sta i_5 + // [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + // [5] phi (byte) main::i#3 = (byte~) main::i#5 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [9] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte) 'x' [ ] ( main:2 [ ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#3 main::i#5 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:3 [ main::i#2 ] : zp ZP_BYTE:3 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 40.33: zp ZP_BYTE:2 [ main::i#3 main::i#5 ] 12: zp ZP_BYTE:3 [ main::i#2 ] +Uplift Scope [] + +Uplifting [main] best 313 combination reg byte a [ main::i#3 main::i#5 ] reg byte x [ main::i#2 ] +Uplifting [] best 313 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test a for()-loop where the condition has a side-effect +// Currently not standard C compliant (since the condition is not evaluated before the body) + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @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: { + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#3 = (byte) 7 [phi:main->main::@1#0] -- vbuaa=vbuc1 + lda #7 + jmp b1 + // main::@1 + b1: + // [6] *((const byte*) SCREEN#0 + (byte) main::i#3) ← (byte) main::i#3 -- pbuc1_derefidx_vbuaa=vbuaa + tax + sta SCREEN,x + // [7] (byte) main::i#2 ← ++ (byte) main::i#3 -- vbuxx=_inc_vbuaa + tax + inx + // [8] if((byte) main::i#3<(byte) 7) goto main::@3 -- vbuaa_lt_vbuc1_then_la1 + cmp #7 + bcc b3 + jmp b2 + // main::@2 + b2: + // [9] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte) 'x' -- pbuc1_derefidx_vbuxx=vbuc2 + // The condition-evaluation should increment i - even if when it is not met - x should end up in 0x0408 + lda #'x' + sta SCREEN,x + jmp breturn + // main::@return + breturn: + // [10] return + rts + // main::@3 + b3: + // [11] (byte~) main::i#5 ← (byte) main::i#2 -- vbuaa=vbuxx + txa + // [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + // [5] phi (byte) main::i#3 = (byte~) main::i#5 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp b2 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction b2: +Removing instruction breturn: +Removing instruction b1_from_b3: +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*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#2 reg byte x 12.0 +(byte) main::i#3 reg byte a 18.333333333333332 +(byte~) main::i#5 reg byte a 22.0 + +reg byte a [ main::i#3 main::i#5 ] +reg byte x [ main::i#2 ] + + +FINAL ASSEMBLER +Score: 238 + + // File Comments +// Test a for()-loop where the condition has a side-effect +// Currently not standard C compliant (since the condition is not evaluated before the body) + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @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: { + // [5] phi from main to main::@1 [phi:main->main::@1] + // [5] phi (byte) main::i#3 = (byte) 7 [phi:main->main::@1#0] -- vbuaa=vbuc1 + lda #7 + // main::@1 + b1: + // SCREEN[i] = i + // [6] *((const byte*) SCREEN#0 + (byte) main::i#3) ← (byte) main::i#3 -- pbuc1_derefidx_vbuaa=vbuaa + tax + sta SCREEN,x + // for(i=7;i++<7;) + // [7] (byte) main::i#2 ← ++ (byte) main::i#3 -- vbuxx=_inc_vbuaa + tax + inx + // [8] if((byte) main::i#3<(byte) 7) goto main::@3 -- vbuaa_lt_vbuc1_then_la1 + cmp #7 + bcc b3 + // main::@2 + // (SCREEN)[i] = 'x' + // [9] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte) 'x' -- pbuc1_derefidx_vbuxx=vbuc2 + // The condition-evaluation should increment i - even if when it is not met - x should end up in 0x0408 + lda #'x' + sta SCREEN,x + // main::@return + // } + // [10] return + rts + // main::@3 + b3: + // [11] (byte~) main::i#5 ← (byte) main::i#2 -- vbuaa=vbuxx + txa + // [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + // [5] phi (byte) main::i#3 = (byte~) main::i#5 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 +} + // File Data + diff --git a/src/test/ref/loop-for-sideeffect.sym b/src/test/ref/loop-for-sideeffect.sym new file mode 100644 index 000000000..a0d5fa8dc --- /dev/null +++ b/src/test/ref/loop-for-sideeffect.sym @@ -0,0 +1,17 @@ +(label) @1 +(label) @begin +(label) @end +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#2 reg byte x 12.0 +(byte) main::i#3 reg byte a 18.333333333333332 +(byte~) main::i#5 reg byte a 22.0 + +reg byte a [ main::i#3 main::i#5 ] +reg byte x [ main::i#2 ] diff --git a/src/test/ref/loop-while-min.asm b/src/test/ref/loop-while-min.asm new file mode 100644 index 000000000..669d67109 --- /dev/null +++ b/src/test/ref/loop-while-min.asm @@ -0,0 +1,17 @@ +// Minimal classic while() loop +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + ldx #0 + b1: + cpx #$64 + bne b2 + rts + b2: + txa + sta SCREEN,x + inx + jmp b1 +} diff --git a/src/test/ref/loop-while-min.cfg b/src/test/ref/loop-while-min.cfg new file mode 100644 index 000000000..b93d67ece --- /dev/null +++ b/src/test/ref/loop-while-min.cfg @@ -0,0 +1,23 @@ +@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::@1 +main::@1: scope:[main] from main main::@2 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [6] if((byte) main::i#2!=(byte) $64) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte) main::i#2 + [9] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 diff --git a/src/test/ref/loop-while-min.log b/src/test/ref/loop-while-min.log new file mode 100644 index 000000000..d5892f35a --- /dev/null +++ b/src/test/ref/loop-while-min.log @@ -0,0 +1,344 @@ +Identified constant variable (byte*) SCREEN +Culled Empty Block (label) main::@4 +Culled Empty Block (label) main::@3 +Culled Empty Block (label) main::@5 +Culled Empty Block (label) main::@6 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte*) SCREEN#0 ← ((byte*)) (number) $400 + to:@1 +main: scope:[main] from @1 + (byte) main::i#0 ← (number) 0 + to:main::@1 +main::@1: scope:[main] from main main::@2 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@2/(byte) main::i#1 ) + (bool~) main::$0 ← (byte) main::i#2 != (number) $64 + if((bool~) main::$0) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#2 ) + *((byte*) SCREEN#0 + (byte) main::i#3) ← (byte) main::i#3 + (byte) main::i#1 ← ++ (byte) main::i#3 + to:main::@1 +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*) SCREEN +(byte*) SCREEN#0 +(void()) main() +(bool~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 + +Adding number conversion cast (unumber) 0 in (byte) main::i#0 ← (number) 0 +Adding number conversion cast (unumber) $64 in (bool~) main::$0 ← (byte) main::i#2 != (number) $64 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400 +Inlining cast (byte) main::i#0 ← (unumber)(number) 0 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 0 +Simplifying constant integer cast $64 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 0 +Finalized unsigned number type (byte) $64 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) main::i#2 = (byte) main::i#3 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$0 [4] if((byte) main::i#2!=(byte) $64) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) SCREEN#0 = (byte*) 1024 +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte) 0 +Successful SSA optimization Pass2ConstantInlining +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 +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Coalesced [11] main::i#4 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) @2 +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::@1 +main::@1: scope:[main] from main main::@2 + [5] (byte) main::i#2 ← phi( main/(byte) 0 main::@2/(byte) main::i#1 ) + [6] if((byte) main::i#2!=(byte) $64) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [7] return + to:@return +main::@2: scope:[main] from main::@1 + [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte) main::i#2 + [9] (byte) main::i#1 ← ++ (byte) main::i#2 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +(byte*) SCREEN +(void()) main() +(byte) main::i +(byte) main::i#1 22.0 +(byte) main::i#2 18.333333333333332 + +Initial phi equivalence classes +[ main::i#2 main::i#1 ] +Complete equivalence classes +[ main::i#2 main::i#1 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#1 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// Minimal classic while() loop + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @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 i = 2 + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta i + jmp b1 + // main::@1 + b1: + // [6] if((byte) main::i#2!=(byte) $64) goto main::@2 -- vbuz1_neq_vbuc1_then_la1 + lda #$64 + cmp i + bne b2 + jmp breturn + // main::@return + breturn: + // [7] return + rts + // main::@2 + b2: + // [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuz1=vbuz1 + ldy i + tya + sta SCREEN,y + // [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 40.33: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 263 combination reg byte x [ main::i#2 main::i#1 ] +Uplifting [] best 263 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Minimal classic while() loop + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @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: { + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + // main::@1 + b1: + // [6] if((byte) main::i#2!=(byte) $64) goto main::@2 -- vbuxx_neq_vbuc1_then_la1 + cpx #$64 + bne b2 + jmp breturn + // main::@return + breturn: + // [7] return + rts + // main::@2 + b2: + // [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuxx=vbuxx + txa + sta SCREEN,x + // [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction breturn: +Removing instruction b1_from_b2: +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*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 22.0 +(byte) main::i#2 reg byte x 18.333333333333332 + +reg byte x [ main::i#2 main::i#1 ] + + +FINAL ASSEMBLER +Score: 191 + + // File Comments +// Minimal classic while() loop + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @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: { + // [5] phi from main to main::@1 [phi:main->main::@1] + // [5] phi (byte) main::i#2 = (byte) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + // main::@1 + b1: + // while(i!=100) + // [6] if((byte) main::i#2!=(byte) $64) goto main::@2 -- vbuxx_neq_vbuc1_then_la1 + cpx #$64 + bne b2 + // main::@return + // } + // [7] return + rts + // main::@2 + b2: + // SCREEN[i] = i + // [8] *((const byte*) SCREEN#0 + (byte) main::i#2) ← (byte) main::i#2 -- pbuc1_derefidx_vbuxx=vbuxx + txa + sta SCREEN,x + // i++; + // [9] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} + // File Data + diff --git a/src/test/ref/loop-while-min.sym b/src/test/ref/loop-while-min.sym new file mode 100644 index 000000000..19dc59f31 --- /dev/null +++ b/src/test/ref/loop-while-min.sym @@ -0,0 +1,14 @@ +(label) @1 +(label) @begin +(label) @end +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 22.0 +(byte) main::i#2 reg byte x 18.333333333333332 + +reg byte x [ main::i#2 main::i#1 ] diff --git a/src/test/ref/loop-while-sideeffect.asm b/src/test/ref/loop-while-sideeffect.asm new file mode 100644 index 000000000..36f77af8f --- /dev/null +++ b/src/test/ref/loop-while-sideeffect.asm @@ -0,0 +1,22 @@ +// Test a while()-loop where the condition has a side-effect +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + lda #7 + b1: + tax + inx + cmp #7 + bne b2 + // The condition-evaluation should increment i - even if when it is not met + lda #'x' + sta SCREEN,x + rts + b2: + txa + sta SCREEN,x + txa + jmp b1 +} diff --git a/src/test/ref/loop-while-sideeffect.cfg b/src/test/ref/loop-while-sideeffect.cfg new file mode 100644 index 000000000..35a51cd3c --- /dev/null +++ b/src/test/ref/loop-while-sideeffect.cfg @@ -0,0 +1,27 @@ +@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::@1 +main::@1: scope:[main] from main main::@2 + [5] (byte) main::i#2 ← phi( main/(byte) 7 main::@2/(byte~) main::i#5 ) + [6] (byte) main::i#1 ← ++ (byte) main::i#2 + [7] if((byte) main::i#2!=(byte) 7) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 + [8] *((const byte*) SCREEN#0 + (byte) main::i#1) ← (byte) 'x' + to:main::@return +main::@return: scope:[main] from main::@3 + [9] return + to:@return +main::@2: scope:[main] from main::@1 + [10] *((const byte*) SCREEN#0 + (byte) main::i#1) ← (byte) main::i#1 + [11] (byte~) main::i#5 ← (byte) main::i#1 + to:main::@1 diff --git a/src/test/ref/loop-while-sideeffect.log b/src/test/ref/loop-while-sideeffect.log new file mode 100644 index 000000000..c39b878a5 --- /dev/null +++ b/src/test/ref/loop-while-sideeffect.log @@ -0,0 +1,397 @@ +Identified constant variable (byte*) SCREEN +Culled Empty Block (label) main::@4 +Culled Empty Block (label) main::@5 +Culled Empty Block (label) main::@6 + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte*) SCREEN#0 ← ((byte*)) (number) $400 + to:@1 +main: scope:[main] from @1 + (byte) main::i#0 ← (number) 7 + to:main::@1 +main::@1: scope:[main] from main main::@2 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@2/(byte) main::i#3 ) + (bool~) main::$0 ← (byte) main::i#2 != (number) 7 + (byte) main::i#1 ← ++ (byte) main::i#2 + if((bool~) main::$0) goto main::@2 + to:main::@3 +main::@2: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#1 ) + *((byte*) SCREEN#0 + (byte) main::i#3) ← (byte) main::i#3 + to:main::@1 +main::@3: scope:[main] from main::@1 + (byte) main::i#4 ← phi( main::@1/(byte) main::i#1 ) + *((byte*) SCREEN#0 + (byte) main::i#4) ← (byte) 'x' + to:main::@return +main::@return: scope:[main] from main::@3 + 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*) SCREEN +(byte*) SCREEN#0 +(void()) main() +(bool~) main::$0 +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#0 +(byte) main::i#1 +(byte) main::i#2 +(byte) main::i#3 +(byte) main::i#4 + +Adding number conversion cast (unumber) 7 in (byte) main::i#0 ← (number) 7 +Adding number conversion cast (unumber) 7 in (bool~) main::$0 ← (byte) main::i#2 != (number) 7 +Successful SSA optimization PassNAddNumberTypeConversions +Inlining cast (byte*) SCREEN#0 ← (byte*)(number) $400 +Inlining cast (byte) main::i#0 ← (unumber)(number) 7 +Successful SSA optimization Pass2InlineCast +Simplifying constant pointer cast (byte*) 1024 +Simplifying constant integer cast 7 +Simplifying constant integer cast 7 +Successful SSA optimization PassNCastSimplification +Finalized unsigned number type (byte) 7 +Finalized unsigned number type (byte) 7 +Successful SSA optimization PassNFinalizeNumberTypeConversions +Alias (byte) main::i#1 = (byte) main::i#3 (byte) main::i#4 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$0 [5] if((byte) main::i#2!=(byte) 7) goto main::@2 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) SCREEN#0 = (byte*) 1024 +Constant (const byte) main::i#0 = 7 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte) 7 +Successful SSA optimization Pass2ConstantInlining +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 +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Not coalescing [12] main::i#5 ← main::i#1 +Coalesced down to 2 phi equivalence classes +Culled Empty Block (label) @2 +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::@1 +main::@1: scope:[main] from main main::@2 + [5] (byte) main::i#2 ← phi( main/(byte) 7 main::@2/(byte~) main::i#5 ) + [6] (byte) main::i#1 ← ++ (byte) main::i#2 + [7] if((byte) main::i#2!=(byte) 7) goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 + [8] *((const byte*) SCREEN#0 + (byte) main::i#1) ← (byte) 'x' + to:main::@return +main::@return: scope:[main] from main::@3 + [9] return + to:@return +main::@2: scope:[main] from main::@1 + [10] *((const byte*) SCREEN#0 + (byte) main::i#1) ← (byte) main::i#1 + [11] (byte~) main::i#5 ← (byte) main::i#1 + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +(byte*) SCREEN +(void()) main() +(byte) main::i +(byte) main::i#1 15.333333333333332 +(byte) main::i#2 16.5 +(byte~) main::i#5 22.0 + +Initial phi equivalence classes +[ main::i#2 main::i#5 ] +Added variable main::i#1 to zero page equivalence class [ main::i#1 ] +Complete equivalence classes +[ main::i#2 main::i#5 ] +[ main::i#1 ] +Allocated zp ZP_BYTE:2 [ main::i#2 main::i#5 ] +Allocated zp ZP_BYTE:3 [ main::i#1 ] + +INITIAL ASM +Target platform is c64basic + // File Comments +// Test a while()-loop where the condition has a side-effect + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @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 i = 3 + .label i_2 = 2 + .label i_5 = 2 + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#2 = (byte) 7 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #7 + sta i_2 + jmp b1 + // main::@1 + b1: + // [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz2 + ldy i_2 + iny + sty i + // [7] if((byte) main::i#2!=(byte) 7) goto main::@2 -- vbuz1_neq_vbuc1_then_la1 + lda #7 + cmp i_2 + bne b2 + jmp b3 + // main::@3 + b3: + // [8] *((const byte*) SCREEN#0 + (byte) main::i#1) ← (byte) 'x' -- pbuc1_derefidx_vbuz1=vbuc2 + // The condition-evaluation should increment i - even if when it is not met + lda #'x' + ldy i + sta SCREEN,y + jmp breturn + // main::@return + breturn: + // [9] return + rts + // main::@2 + b2: + // [10] *((const byte*) SCREEN#0 + (byte) main::i#1) ← (byte) main::i#1 -- pbuc1_derefidx_vbuz1=vbuz1 + ldy i + tya + sta SCREEN,y + // [11] (byte~) main::i#5 ← (byte) main::i#1 -- vbuz1=vbuz2 + lda i + sta i_5 + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [5] phi (byte) main::i#2 = (byte~) main::i#5 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} + // File Data + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [8] *((const byte*) SCREEN#0 + (byte) main::i#1) ← (byte) 'x' [ ] ( main:2 [ ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#5 ] : zp ZP_BYTE:2 , reg byte a , reg byte x , reg byte y , +Potential registers zp ZP_BYTE:3 [ main::i#1 ] : zp ZP_BYTE:3 , reg byte a , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 38.5: zp ZP_BYTE:2 [ main::i#2 main::i#5 ] 15.33: zp ZP_BYTE:3 [ main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 313 combination reg byte a [ main::i#2 main::i#5 ] reg byte x [ main::i#1 ] +Uplifting [] best 313 combination + +ASSEMBLER BEFORE OPTIMIZATION + // File Comments +// Test a while()-loop where the condition has a side-effect + // Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @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: { + // [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + // [5] phi (byte) main::i#2 = (byte) 7 [phi:main->main::@1#0] -- vbuaa=vbuc1 + lda #7 + jmp b1 + // main::@1 + b1: + // [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuaa + tax + inx + // [7] if((byte) main::i#2!=(byte) 7) goto main::@2 -- vbuaa_neq_vbuc1_then_la1 + cmp #7 + bne b2 + jmp b3 + // main::@3 + b3: + // [8] *((const byte*) SCREEN#0 + (byte) main::i#1) ← (byte) 'x' -- pbuc1_derefidx_vbuxx=vbuc2 + // The condition-evaluation should increment i - even if when it is not met + lda #'x' + sta SCREEN,x + jmp breturn + // main::@return + breturn: + // [9] return + rts + // main::@2 + b2: + // [10] *((const byte*) SCREEN#0 + (byte) main::i#1) ← (byte) main::i#1 -- pbuc1_derefidx_vbuxx=vbuxx + txa + sta SCREEN,x + // [11] (byte~) main::i#5 ← (byte) main::i#1 -- vbuaa=vbuxx + txa + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + b1_from_b2: + // [5] phi (byte) main::i#2 = (byte~) main::i#5 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} + // File Data + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp b1 +Removing instruction jmp b3 +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction b3: +Removing instruction breturn: +Removing instruction b1_from_b2: +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*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 15.333333333333332 +(byte) main::i#2 reg byte a 16.5 +(byte~) main::i#5 reg byte a 22.0 + +reg byte a [ main::i#2 main::i#5 ] +reg byte x [ main::i#1 ] + + +FINAL ASSEMBLER +Score: 238 + + // File Comments +// Test a while()-loop where the condition has a side-effect + // Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + // Global Constants & labels + .label SCREEN = $400 + // @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: { + // [5] phi from main to main::@1 [phi:main->main::@1] + // [5] phi (byte) main::i#2 = (byte) 7 [phi:main->main::@1#0] -- vbuaa=vbuc1 + lda #7 + // main::@1 + b1: + // while(i++!=7) + // [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuaa + tax + inx + // [7] if((byte) main::i#2!=(byte) 7) goto main::@2 -- vbuaa_neq_vbuc1_then_la1 + cmp #7 + bne b2 + // main::@3 + // (SCREEN)[i] = 'x' + // [8] *((const byte*) SCREEN#0 + (byte) main::i#1) ← (byte) 'x' -- pbuc1_derefidx_vbuxx=vbuc2 + // The condition-evaluation should increment i - even if when it is not met + lda #'x' + sta SCREEN,x + // main::@return + // } + // [9] return + rts + // main::@2 + b2: + // SCREEN[i] = i + // [10] *((const byte*) SCREEN#0 + (byte) main::i#1) ← (byte) main::i#1 -- pbuc1_derefidx_vbuxx=vbuxx + txa + sta SCREEN,x + // [11] (byte~) main::i#5 ← (byte) main::i#1 -- vbuaa=vbuxx + txa + // [5] phi from main::@2 to main::@1 [phi:main::@2->main::@1] + // [5] phi (byte) main::i#2 = (byte~) main::i#5 [phi:main::@2->main::@1#0] -- register_copy + jmp b1 +} + // File Data + diff --git a/src/test/ref/loop-while-sideeffect.sym b/src/test/ref/loop-while-sideeffect.sym new file mode 100644 index 000000000..a335e8d3f --- /dev/null +++ b/src/test/ref/loop-while-sideeffect.sym @@ -0,0 +1,17 @@ +(label) @1 +(label) @begin +(label) @end +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = (byte*) 1024 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 15.333333333333332 +(byte) main::i#2 reg byte a 16.5 +(byte~) main::i#5 reg byte a 22.0 + +reg byte a [ main::i#2 main::i#5 ] +reg byte x [ main::i#1 ]