From 2d7825a0b9024f650e647b84722906ae148918f7 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Sun, 31 Mar 2019 20:15:01 +0200 Subject: [PATCH] Implemented continue statement. Closes #91 --- .../java/dk/camelot64/kickc/CompileLog.java | 16 + .../java/dk/camelot64/kickc/Compiler.java | 6 +- .../Pass0GenerateStatementSequence.java | 40 +- .../dk/camelot64/kickc/test/TestPrograms.java | 31 +- src/test/kc/loop-continue.kc | 11 + src/test/kc/loop-while-continue.kc | 12 + src/test/ref/loop-continue.asm | 20 + src/test/ref/loop-continue.cfg | 26 ++ src/test/ref/loop-continue.log | 372 ++++++++++++++++++ src/test/ref/loop-continue.sym | 15 + src/test/ref/loop-while-continue.asm | 21 + src/test/ref/loop-while-continue.cfg | 26 ++ src/test/ref/loop-while-continue.log | 372 ++++++++++++++++++ src/test/ref/loop-while-continue.sym | 15 + 14 files changed, 969 insertions(+), 14 deletions(-) create mode 100644 src/test/kc/loop-continue.kc create mode 100644 src/test/kc/loop-while-continue.kc create mode 100644 src/test/ref/loop-continue.asm create mode 100644 src/test/ref/loop-continue.cfg create mode 100644 src/test/ref/loop-continue.log create mode 100644 src/test/ref/loop-continue.sym create mode 100644 src/test/ref/loop-while-continue.asm create mode 100644 src/test/ref/loop-while-continue.cfg create mode 100644 src/test/ref/loop-while-continue.log create mode 100644 src/test/ref/loop-while-continue.sym diff --git a/src/main/java/dk/camelot64/kickc/CompileLog.java b/src/main/java/dk/camelot64/kickc/CompileLog.java index 63779d6af..5f6ec3768 100644 --- a/src/main/java/dk/camelot64/kickc/CompileLog.java +++ b/src/main/java/dk/camelot64/kickc/CompileLog.java @@ -65,6 +65,10 @@ public class CompileLog { /** Should comments be output as part of the intermediate SSA prints. */ private boolean verboseComments = false; + /** + * Should the statement sequence be verbose. + */ + private boolean verboseStatementSequence = false; /** * Should the log be output to System.out while being built @@ -87,6 +91,18 @@ public class CompileLog { return log; } + public boolean isVerboseCreateSsa() { + return verboseCreateSsa; + } + + public boolean isVerboseStatementSequence() { + return verboseStatementSequence; + } + + public void setVerboseStatementSequence(boolean verboseStatementSequence) { + this.verboseStatementSequence = verboseStatementSequence; + } + public void setVerboseComments(boolean verboseComments) { this.verboseComments = verboseComments; } diff --git a/src/main/java/dk/camelot64/kickc/Compiler.java b/src/main/java/dk/camelot64/kickc/Compiler.java index 9b0362173..881ec0117 100644 --- a/src/main/java/dk/camelot64/kickc/Compiler.java +++ b/src/main/java/dk/camelot64/kickc/Compiler.java @@ -145,8 +145,10 @@ public class Compiler { private Program pass1GenerateSSA() { - //getLog().append("\nSTATEMENTS"); - //getLog().append(program.getStatementSequence().toString(program)); + if(getLog().isVerboseStatementSequence()) { + getLog().append("\nSTATEMENTS"); + getLog().append(program.getStatementSequence().toString(program)); + } new Pass1GenerateControlFlowGraph(program).execute(); new Pass1ResolveForwardReferences(program).execute(); diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 8bac64383..5597dd7d5 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -637,6 +637,9 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { /** The label after the loop that a break will jump to. Null if no break has been encountered. */ Label breakLabel; + /** The label that a continue will jump to. Null if no continue has been encountered. */ + Label continueLabel; + public Loop(Scope loopScope) { this.loopScope = loopScope; } @@ -651,6 +654,22 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { } return breakLabel; } + + public Label getContinueLabel() { + return continueLabel; + } + + public void setContinueLabel(Label continueLabel) { + this.continueLabel = continueLabel; + } + + public Label getOrCreateContinueLabel() { + if(continueLabel==null) { + continueLabel = loopScope.addLabelIntermediate(); + } + return continueLabel; + } + } /** The loops being generated. */ @@ -677,6 +696,8 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { sequence.addStatement(endJmpStmt); StatementLabel doJumpTarget = new StatementLabel(doJumpLabel.getRef(), new StatementSource(ctx), Comment.NO_COMMENTS); sequence.addStatement(doJumpTarget); + // Reuse the begin jump target for continue. + loopStack.peek().setContinueLabel(beginJumpLabel); addLoopBody(ctx.stmt()); Statement beginJmpStmt = new StatementJump(beginJumpLabel.getRef(), new StatementSource(ctx), Comment.NO_COMMENTS); sequence.addStatement(beginJmpStmt); @@ -700,6 +721,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { StatementLabel beginJumpTarget = new StatementLabel(beginJumpLabel.getRef(), new StatementSource(ctx), comments); sequence.addStatement(beginJumpTarget); addLoopBody(ctx.stmt()); + addLoopContinueLabel(loopStack.peek(), ctx); PrePostModifierHandler.addPreModifiers(this, ctx.expr()); RValue rValue = (RValue) this.visit(ctx.expr()); PrePostModifierHandler.addPostModifiers(this, ctx.expr()); @@ -742,6 +764,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { sequence.addStatement(repeatTarget); // Add body addLoopBody(stmtForCtx.stmt()); + addLoopContinueLabel(loopStack.peek(), ctx); // Add increment if(ctx.expr(1) != null) { PrePostModifierHandler.addPreModifiers(this, ctx.expr(1)); @@ -784,6 +807,7 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { sequence.addStatement(repeatTarget); // Add body addLoopBody(stmtForCtx.stmt()); + addLoopContinueLabel(loopStack.peek(), ctx); // Add increment Statement stmtNxt = new StatementAssignment(lValue.getRef(), lValue.getRef(), Operators.PLUS, new RangeNext(rangeFirstValue, rangeLastValue), new StatementSource(ctx), Comment.NO_COMMENTS); sequence.addStatement(stmtNxt); @@ -809,6 +833,13 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { } } + private void addLoopContinueLabel(Loop loop, ParserRuleContext ctx) { + if(loop.getContinueLabel()!=null) { + StatementLabel continueTarget = new StatementLabel(loop.getContinueLabel().getRef(), new StatementSource(ctx), Comment.NO_COMMENTS); + sequence.addStatement(continueTarget); + } + } + private void addLoopBody(KickCParser.StmtContext stmt) { if(stmt != null) { if(stmt instanceof KickCParser.StmtBlockContext) { @@ -864,7 +895,14 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { @Override public Object visitStmtContinue(KickCParser.StmtContinueContext ctx) { - return super.visitStmtContinue(ctx); + if(loopStack.isEmpty()) { + throw new CompileError("Continue not inside a loop! ", new StatementSource(ctx)); + } + Loop currentLoop = loopStack.peek(); + Label continueLabel = currentLoop.getOrCreateContinueLabel(); + Statement continueJmpStmt = new StatementJump(continueLabel.getRef(), new StatementSource(ctx), Comment.NO_COMMENTS); + sequence.addStatement(continueJmpStmt); + return null; } @Override diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 969a0e92a..d69a4c141 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -32,6 +32,26 @@ public class TestPrograms { public TestPrograms() { } + @Test + public void testLoopWhileContinue() throws IOException, URISyntaxException { + compileAndCompare("loop-while-continue"); + } + + @Test + public void testLoopContinue() throws IOException, URISyntaxException { + compileAndCompare("loop-continue"); + } + + @Test + public void testLoopBreakNested() throws IOException, URISyntaxException { + compileAndCompare("loop-break-nested"); + } + + @Test + public void testLoopBreak() throws IOException, URISyntaxException { + compileAndCompare("loop-break"); + } + @Test public void testLocalScopeLoops() throws IOException, URISyntaxException { compileAndCompare("localscope-loops", getLogSysout()); @@ -67,17 +87,6 @@ public class TestPrograms { compileAndCompare("examples/music/music"); } - @Test - public void testLoopBreakNested() throws IOException, URISyntaxException { - compileAndCompare("loop-break-nested"); - } - - @Test - public void testLoopBreak() throws IOException, URISyntaxException { - compileAndCompare("loop-break"); - } - - @Test public void testConstEarlyIdentification() throws IOException, URISyntaxException { compileAndCompare("const-early-identification"); diff --git a/src/test/kc/loop-continue.kc b/src/test/kc/loop-continue.kc new file mode 100644 index 000000000..ecb85ad80 --- /dev/null +++ b/src/test/kc/loop-continue.kc @@ -0,0 +1,11 @@ +// Tests break statement in a simple loop + +const byte* SCREEN = $400; + +void main() { + for( byte i: 0..40*6) { + if(SCREEN[i]==' ') + continue; + SCREEN[i]++; + } +} \ No newline at end of file diff --git a/src/test/kc/loop-while-continue.kc b/src/test/kc/loop-while-continue.kc new file mode 100644 index 000000000..9316939be --- /dev/null +++ b/src/test/kc/loop-while-continue.kc @@ -0,0 +1,12 @@ +// Tests break statement in a simple loop + +const byte* SCREEN = $400; + +void main() { + byte i=0; + while(++i<40*6) { + if(SCREEN[i]==' ') + continue; + SCREEN[i]++; + } +} \ No newline at end of file diff --git a/src/test/ref/loop-continue.asm b/src/test/ref/loop-continue.asm new file mode 100644 index 000000000..c5abdab71 --- /dev/null +++ b/src/test/ref/loop-continue.asm @@ -0,0 +1,20 @@ +// Tests break statement in a simple loop +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + ldx #0 + b1: + lda SCREEN,x + cmp #' ' + bne b2 + b3: + inx + cpx #$28*6+1 + bne b1 + rts + b2: + inc SCREEN,x + jmp b3 +} diff --git a/src/test/ref/loop-continue.cfg b/src/test/ref/loop-continue.cfg new file mode 100644 index 000000000..445e1b833 --- /dev/null +++ b/src/test/ref/loop-continue.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::@1 +main::@1: scope:[main] from main main::@3 + [5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@3/(byte) main::i#1 ) + [6] if(*((const byte*) SCREEN#0 + (byte) main::i#2)!=(byte) ' ') goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 main::@2 + [7] (byte) main::i#1 ← ++ (byte) main::i#2 + [8] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6+(byte/signed byte/word/signed word/dword/signed dword) 1) goto main::@1 + 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#2) ← ++ *((const byte*) SCREEN#0 + (byte) main::i#2) + to:main::@3 diff --git a/src/test/ref/loop-continue.log b/src/test/ref/loop-continue.log new file mode 100644 index 000000000..8a0d78d03 --- /dev/null +++ b/src/test/ref/loop-continue.log @@ -0,0 +1,372 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte*) SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400 + to:@1 +main: scope:[main] from @1 + (byte/word/signed word/dword/signed dword~) main::$0 ← (byte/signed byte/word/signed word/dword/signed dword) $28 * (byte/signed byte/word/signed word/dword/signed dword) 6 + (byte) main::i#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + to:main::@1 +main::@1: scope:[main] from main main::@3 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@3/(byte) main::i#1 ) + (bool~) main::$1 ← *((byte*) SCREEN#0 + (byte) main::i#2) == (byte) ' ' + (bool~) main::$2 ← ! (bool~) main::$1 + if((bool~) main::$2) goto main::@2 + to:main::@3 +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*) SCREEN#0 + (byte) main::i#3) + to:main::@3 +main::@3: scope:[main] from main::@1 main::@2 + (byte) main::i#4 ← phi( main::@1/(byte) main::i#2 main::@2/(byte) main::i#3 ) + (byte) main::i#1 ← (byte) main::i#4 + rangenext(0,main::$0) + (bool~) main::$3 ← (byte) main::i#1 != rangelast(0,main::$0) + if((bool~) main::$3) goto main::@1 + 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() +(byte/word/signed word/dword/signed dword~) main::$0 +(bool~) main::$1 +(bool~) main::$2 +(bool~) main::$3 +(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 + +Culled Empty Block (label) @2 +Successful SSA optimization Pass2CullEmptyBlocks +Inversing boolean not [5] (bool~) main::$2 ← *((byte*) SCREEN#0 + (byte) main::i#2) != (byte) ' ' from [4] (bool~) main::$1 ← *((byte*) SCREEN#0 + (byte) main::i#2) == (byte) ' ' +Successful SSA optimization Pass2UnaryNotSimplification +Alias (byte) main::i#2 = (byte) main::i#3 +Successful SSA optimization Pass2AliasElimination +Alias (byte) main::i#2 = (byte) main::i#4 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$2 [6] if(*((byte*) SCREEN#0 + (byte) main::i#2)!=(byte) ' ') goto main::@2 +Simple Condition (bool~) main::$3 [12] if((byte) main::i#1!=rangelast(0,main::$0)) goto main::@1 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) SCREEN#0 = ((byte*))$400 +Constant (const byte/word/signed word/dword/signed dword) main::$0 = $28*6 +Constant (const byte) main::i#0 = 0 +Successful SSA optimization Pass2ConstantIdentification +Resolved ranged next value main::i#1 ← ++ main::i#2 to ++ +Resolved ranged comparison value if(main::i#1!=rangelast(0,main::$0)) goto main::@1 to (const byte/word/signed word/dword/signed dword) main::$0+(byte/signed byte/word/signed word/dword/signed dword) 1 +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte/signed byte/word/signed word/dword/signed dword) 0 +Constant inlined main::$0 = (byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@7(between main::@3 and main::@1) +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 +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Coalesced [10] main::i#5 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) main::@7 +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#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@3/(byte) main::i#1 ) + [6] if(*((const byte*) SCREEN#0 + (byte) main::i#2)!=(byte) ' ') goto main::@2 + to:main::@3 +main::@3: scope:[main] from main::@1 main::@2 + [7] (byte) main::i#1 ← ++ (byte) main::i#2 + [8] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6+(byte/signed byte/word/signed word/dword/signed dword) 1) goto main::@1 + 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#2) ← ++ *((const byte*) SCREEN#0 + (byte) main::i#2) + to:main::@3 + + +VARIABLE REGISTER WEIGHTS +(byte*) SCREEN +(void()) main() +(byte) main::i +(byte) main::i#1 16.5 +(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 +//SEG0 File Comments +// Tests break statement in a simple loop +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .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 i = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta i + jmp b1 + //SEG13 [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + //SEG14 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 + //SEG15 main::@1 + b1: + //SEG16 [6] if(*((const byte*) SCREEN#0 + (byte) main::i#2)!=(byte) ' ') goto main::@2 -- pbuc1_derefidx_vbuz1_neq_vbuc2_then_la1 + lda #' ' + ldy i + cmp SCREEN,y + bne b2 + jmp b3 + //SEG17 main::@3 + b3: + //SEG18 [7] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + //SEG19 [8] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6+(byte/signed byte/word/signed word/dword/signed dword) 1) goto main::@1 -- vbuz1_neq_vbuc1_then_la1 + lda #$28*6+1 + cmp i + bne b1_from_b3 + jmp breturn + //SEG20 main::@return + breturn: + //SEG21 [9] return + rts + //SEG22 main::@2 + b2: + //SEG23 [10] *((const byte*) SCREEN#0 + (byte) main::i#2) ← ++ *((const byte*) SCREEN#0 + (byte) main::i#2) -- pbuc1_derefidx_vbuz1=_inc_pbuc1_derefidx_vbuz1 + ldx i + inc SCREEN,x + jmp b3 +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [6] if(*((const byte*) SCREEN#0 + (byte) main::i#2)!=(byte) ' ') goto main::@2 [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Statement [6] if(*((const byte*) SCREEN#0 + (byte) main::i#2)!=(byte) ' ') goto main::@2 [ main::i#2 ] ( main:2 [ main::i#2 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 34.83: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 413 combination reg byte x [ main::i#2 main::i#1 ] +Uplifting [] best 413 combination + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Tests break statement in a simple loop +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .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: { + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + //SEG13 [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + b1_from_b3: + //SEG14 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + jmp b1 + //SEG15 main::@1 + b1: + //SEG16 [6] if(*((const byte*) SCREEN#0 + (byte) main::i#2)!=(byte) ' ') goto main::@2 -- pbuc1_derefidx_vbuxx_neq_vbuc2_then_la1 + lda SCREEN,x + cmp #' ' + bne b2 + jmp b3 + //SEG17 main::@3 + b3: + //SEG18 [7] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG19 [8] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6+(byte/signed byte/word/signed word/dword/signed dword) 1) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$28*6+1 + bne b1_from_b3 + jmp breturn + //SEG20 main::@return + breturn: + //SEG21 [9] return + rts + //SEG22 main::@2 + b2: + //SEG23 [10] *((const byte*) SCREEN#0 + (byte) main::i#2) ← ++ *((const byte*) SCREEN#0 + (byte) main::i#2) -- pbuc1_derefidx_vbuxx=_inc_pbuc1_derefidx_vbuxx + inc SCREEN,x + jmp b3 +} + +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 +Replacing label b1_from_b3 with b1 +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction main_from_b1: +Removing instruction bend_from_b1: +Removing instruction b1_from_b3: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Removing instruction jmp b1 +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction bbegin: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(byte) main::i#2 reg byte x 18.333333333333332 + +reg byte x [ main::i#2 main::i#1 ] + + +FINAL ASSEMBLER +Score: 281 + +//SEG0 File Comments +// Tests break statement in a simple loop +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .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 [5] phi from main to main::@1 [phi:main->main::@1] + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + //SEG13 [5] phi from main::@3 to main::@1 [phi:main::@3->main::@1] + //SEG14 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@3->main::@1#0] -- register_copy + //SEG15 main::@1 + b1: + //SEG16 [6] if(*((const byte*) SCREEN#0 + (byte) main::i#2)!=(byte) ' ') goto main::@2 -- pbuc1_derefidx_vbuxx_neq_vbuc2_then_la1 + lda SCREEN,x + cmp #' ' + bne b2 + //SEG17 main::@3 + b3: + //SEG18 [7] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG19 [8] if((byte) main::i#1!=(byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6+(byte/signed byte/word/signed word/dword/signed dword) 1) goto main::@1 -- vbuxx_neq_vbuc1_then_la1 + cpx #$28*6+1 + bne b1 + //SEG20 main::@return + //SEG21 [9] return + rts + //SEG22 main::@2 + b2: + //SEG23 [10] *((const byte*) SCREEN#0 + (byte) main::i#2) ← ++ *((const byte*) SCREEN#0 + (byte) main::i#2) -- pbuc1_derefidx_vbuxx=_inc_pbuc1_derefidx_vbuxx + inc SCREEN,x + jmp b3 +} + diff --git a/src/test/ref/loop-continue.sym b/src/test/ref/loop-continue.sym new file mode 100644 index 000000000..5dcec58f5 --- /dev/null +++ b/src/test/ref/loop-continue.sym @@ -0,0 +1,15 @@ +(label) @1 +(label) @begin +(label) @end +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 16.5 +(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-continue.asm b/src/test/ref/loop-while-continue.asm new file mode 100644 index 000000000..1424862f5 --- /dev/null +++ b/src/test/ref/loop-while-continue.asm @@ -0,0 +1,21 @@ +// Tests break statement in a simple loop +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label SCREEN = $400 +main: { + ldx #0 + b1: + inx + cpx #$28*6 + bcc b2 + rts + b2: + lda SCREEN,x + cmp #' ' + bne b3 + jmp b1 + b3: + inc SCREEN,x + jmp b1 +} diff --git a/src/test/ref/loop-while-continue.cfg b/src/test/ref/loop-while-continue.cfg new file mode 100644 index 000000000..f02c093cd --- /dev/null +++ b/src/test/ref/loop-while-continue.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::@1 +main::@1: scope:[main] from main main::@2 main::@3 + [5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@2/(byte) main::i#1 main::@3/(byte) main::i#1 ) + [6] (byte) main::i#1 ← ++ (byte) main::i#2 + [7] if((byte) main::i#1<(byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [8] return + to:@return +main::@2: scope:[main] from main::@1 + [9] if(*((const byte*) SCREEN#0 + (byte) main::i#1)!=(byte) ' ') goto main::@3 + to:main::@1 +main::@3: scope:[main] from main::@2 + [10] *((const byte*) SCREEN#0 + (byte) main::i#1) ← ++ *((const byte*) SCREEN#0 + (byte) main::i#1) + to:main::@1 diff --git a/src/test/ref/loop-while-continue.log b/src/test/ref/loop-while-continue.log new file mode 100644 index 000000000..6489b0a61 --- /dev/null +++ b/src/test/ref/loop-while-continue.log @@ -0,0 +1,372 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte*) SCREEN#0 ← ((byte*)) (word/signed word/dword/signed dword) $400 + to:@1 +main: scope:[main] from @1 + (byte) main::i#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + to:main::@1 +main::@1: scope:[main] from main main::@2 main::@4 + (byte) main::i#2 ← phi( main/(byte) main::i#0 main::@2/(byte) main::i#3 main::@4/(byte) main::i#4 ) + (byte) main::i#1 ← ++ (byte) main::i#2 + (byte/word/signed word/dword/signed dword~) main::$0 ← (byte/signed byte/word/signed word/dword/signed dword) $28 * (byte/signed byte/word/signed word/dword/signed dword) 6 + (bool~) main::$1 ← (byte) main::i#1 < (byte/word/signed word/dword/signed dword~) main::$0 + if((bool~) main::$1) goto main::@2 + to:main::@return +main::@2: scope:[main] from main::@1 + (byte) main::i#3 ← phi( main::@1/(byte) main::i#1 ) + (bool~) main::$2 ← *((byte*) SCREEN#0 + (byte) main::i#3) == (byte) ' ' + (bool~) main::$3 ← ! (bool~) main::$2 + if((bool~) main::$3) goto main::@4 + to:main::@1 +main::@4: scope:[main] from main::@2 + (byte) main::i#4 ← phi( main::@2/(byte) main::i#3 ) + *((byte*) SCREEN#0 + (byte) main::i#4) ← ++ *((byte*) SCREEN#0 + (byte) main::i#4) + 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() +(byte/word/signed word/dword/signed dword~) main::$0 +(bool~) main::$1 +(bool~) main::$2 +(bool~) main::$3 +(label) main::@1 +(label) main::@2 +(label) main::@4 +(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 + +Culled Empty Block (label) @2 +Successful SSA optimization Pass2CullEmptyBlocks +Inversing boolean not [9] (bool~) main::$3 ← *((byte*) SCREEN#0 + (byte) main::i#3) != (byte) ' ' from [8] (bool~) main::$2 ← *((byte*) SCREEN#0 + (byte) main::i#3) == (byte) ' ' +Successful SSA optimization Pass2UnaryNotSimplification +Alias (byte) main::i#1 = (byte) main::i#3 (byte) main::i#4 +Successful SSA optimization Pass2AliasElimination +Simple Condition (bool~) main::$1 [6] if((byte) main::i#1<(byte/word/signed word/dword/signed dword~) main::$0) goto main::@2 +Simple Condition (bool~) main::$3 [10] if(*((byte*) SCREEN#0 + (byte) main::i#1)!=(byte) ' ') goto main::@4 +Successful SSA optimization Pass2ConditionalJumpSimplification +Constant (const byte*) SCREEN#0 = ((byte*))$400 +Constant (const byte) main::i#0 = 0 +Constant (const byte/word/signed word/dword/signed dword) main::$0 = $28*6 +Successful SSA optimization Pass2ConstantIdentification +Inlining constant with var siblings (const byte) main::i#0 +Constant inlined main::i#0 = (byte/signed byte/word/signed word/dword/signed dword) 0 +Constant inlined main::$0 = (byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6 +Successful SSA optimization Pass2ConstantInlining +Added new block during phi lifting main::@10(between main::@2 and main::@1) +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 +CALL GRAPH +Calls in [] to main:2 + +Created 1 initial phi equivalence classes +Coalesced [10] main::i#5 ← main::i#1 +Coalesced (already) [12] main::i#6 ← main::i#1 +Coalesced down to 1 phi equivalence classes +Culled Empty Block (label) main::@10 +Renumbering block main::@4 to main::@3 +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 main::@3 + [5] (byte) main::i#2 ← phi( main/(byte/signed byte/word/signed word/dword/signed dword) 0 main::@2/(byte) main::i#1 main::@3/(byte) main::i#1 ) + [6] (byte) main::i#1 ← ++ (byte) main::i#2 + [7] if((byte) main::i#1<(byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6) goto main::@2 + to:main::@return +main::@return: scope:[main] from main::@1 + [8] return + to:@return +main::@2: scope:[main] from main::@1 + [9] if(*((const byte*) SCREEN#0 + (byte) main::i#1)!=(byte) ' ') goto main::@3 + to:main::@1 +main::@3: scope:[main] from main::@2 + [10] *((const byte*) SCREEN#0 + (byte) main::i#1) ← ++ *((const byte*) SCREEN#0 + (byte) main::i#1) + to:main::@1 + + +VARIABLE REGISTER WEIGHTS +(byte*) SCREEN +(void()) main() +(byte) main::i +(byte) main::i#1 109.25 +(byte) main::i#2 213.0 + +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 +//SEG0 File Comments +// Tests break statement in a simple loop +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .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 i = 2 + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuz1=vbuc1 + lda #0 + sta i + jmp b1 + //SEG13 main::@1 + b1: + //SEG14 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuz1=_inc_vbuz1 + inc i + //SEG15 [7] if((byte) main::i#1<(byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6) goto main::@2 -- vbuz1_lt_vbuc1_then_la1 + lda i + cmp #$28*6 + bcc b2 + jmp breturn + //SEG16 main::@return + breturn: + //SEG17 [8] return + rts + //SEG18 main::@2 + b2: + //SEG19 [9] if(*((const byte*) SCREEN#0 + (byte) main::i#1)!=(byte) ' ') goto main::@3 -- pbuc1_derefidx_vbuz1_neq_vbuc2_then_la1 + lda #' ' + ldy i + cmp SCREEN,y + bne b3 + //SEG20 [5] phi from main::@2 main::@3 to main::@1 [phi:main::@2/main::@3->main::@1] + b1_from_b2: + b1_from_b3: + //SEG21 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2/main::@3->main::@1#0] -- register_copy + jmp b1 + //SEG22 main::@3 + b3: + //SEG23 [10] *((const byte*) SCREEN#0 + (byte) main::i#1) ← ++ *((const byte*) SCREEN#0 + (byte) main::i#1) -- pbuc1_derefidx_vbuz1=_inc_pbuc1_derefidx_vbuz1 + ldx i + inc SCREEN,x + jmp b1_from_b3 +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [9] if(*((const byte*) SCREEN#0 + (byte) main::i#1)!=(byte) ' ') goto main::@3 [ main::i#1 ] ( main:2 [ main::i#1 ] ) always clobbers reg byte a +Removing always clobbered register reg byte a as potential for zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Statement [9] if(*((const byte*) SCREEN#0 + (byte) main::i#1)!=(byte) ' ') goto main::@3 [ main::i#1 ] ( main:2 [ main::i#1 ] ) always clobbers reg byte a +Potential registers zp ZP_BYTE:2 [ main::i#2 main::i#1 ] : zp ZP_BYTE:2 , reg byte x , reg byte y , + +REGISTER UPLIFT SCOPES +Uplift Scope [main] 322.25: zp ZP_BYTE:2 [ main::i#2 main::i#1 ] +Uplift Scope [] + +Uplifting [main] best 2768 combination reg byte x [ main::i#2 main::i#1 ] +Uplifting [] best 2768 combination + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Tests break statement in a simple loop +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .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: { + //SEG11 [5] phi from main to main::@1 [phi:main->main::@1] + b1_from_main: + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + jmp b1 + //SEG13 main::@1 + b1: + //SEG14 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG15 [7] if((byte) main::i#1<(byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$28*6 + bcc b2 + jmp breturn + //SEG16 main::@return + breturn: + //SEG17 [8] return + rts + //SEG18 main::@2 + b2: + //SEG19 [9] if(*((const byte*) SCREEN#0 + (byte) main::i#1)!=(byte) ' ') goto main::@3 -- pbuc1_derefidx_vbuxx_neq_vbuc2_then_la1 + lda SCREEN,x + cmp #' ' + bne b3 + //SEG20 [5] phi from main::@2 main::@3 to main::@1 [phi:main::@2/main::@3->main::@1] + b1_from_b2: + b1_from_b3: + //SEG21 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2/main::@3->main::@1#0] -- register_copy + jmp b1 + //SEG22 main::@3 + b3: + //SEG23 [10] *((const byte*) SCREEN#0 + (byte) main::i#1) ← ++ *((const byte*) SCREEN#0 + (byte) main::i#1) -- pbuc1_derefidx_vbuxx=_inc_pbuc1_derefidx_vbuxx + inc SCREEN,x + jmp b1_from_b3 +} + +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: +Removing instruction b1_from_b2: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction b1_from_main: +Removing instruction breturn: +Succesful ASM optimization Pass5UnusedLabelElimination +Updating BasicUpstart to call main directly +Removing instruction jsr main +Succesful ASM optimization Pass5SkipBegin +Skipping double jump to b1 in jmp b1_from_b3 +Succesful ASM optimization Pass5DoubleJumpElimination +Relabelling long label b1_from_b3 to b4 +Succesful ASM optimization Pass5RelabelLongLabels +Removing instruction bbegin: +Removing instruction b4: +Succesful ASM optimization Pass5UnusedLabelElimination + +FINAL SYMBOL TABLE +(label) @1 +(label) @begin +(label) @end +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 109.25 +(byte) main::i#2 reg byte x 213.0 + +reg byte x [ main::i#2 main::i#1 ] + + +FINAL ASSEMBLER +Score: 2156 + +//SEG0 File Comments +// Tests break statement in a simple loop +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .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 [5] phi from main to main::@1 [phi:main->main::@1] + //SEG12 [5] phi (byte) main::i#2 = (byte/signed byte/word/signed word/dword/signed dword) 0 [phi:main->main::@1#0] -- vbuxx=vbuc1 + ldx #0 + //SEG13 main::@1 + b1: + //SEG14 [6] (byte) main::i#1 ← ++ (byte) main::i#2 -- vbuxx=_inc_vbuxx + inx + //SEG15 [7] if((byte) main::i#1<(byte/signed byte/word/signed word/dword/signed dword) $28*(byte/signed byte/word/signed word/dword/signed dword) 6) goto main::@2 -- vbuxx_lt_vbuc1_then_la1 + cpx #$28*6 + bcc b2 + //SEG16 main::@return + //SEG17 [8] return + rts + //SEG18 main::@2 + b2: + //SEG19 [9] if(*((const byte*) SCREEN#0 + (byte) main::i#1)!=(byte) ' ') goto main::@3 -- pbuc1_derefidx_vbuxx_neq_vbuc2_then_la1 + lda SCREEN,x + cmp #' ' + bne b3 + //SEG20 [5] phi from main::@2 main::@3 to main::@1 [phi:main::@2/main::@3->main::@1] + //SEG21 [5] phi (byte) main::i#2 = (byte) main::i#1 [phi:main::@2/main::@3->main::@1#0] -- register_copy + jmp b1 + //SEG22 main::@3 + b3: + //SEG23 [10] *((const byte*) SCREEN#0 + (byte) main::i#1) ← ++ *((const byte*) SCREEN#0 + (byte) main::i#1) -- pbuc1_derefidx_vbuxx=_inc_pbuc1_derefidx_vbuxx + inc SCREEN,x + jmp b1 +} + diff --git a/src/test/ref/loop-while-continue.sym b/src/test/ref/loop-while-continue.sym new file mode 100644 index 000000000..6ae929430 --- /dev/null +++ b/src/test/ref/loop-while-continue.sym @@ -0,0 +1,15 @@ +(label) @1 +(label) @begin +(label) @end +(byte*) SCREEN +(const byte*) SCREEN#0 SCREEN = ((byte*))(word/signed word/dword/signed dword) $400 +(void()) main() +(label) main::@1 +(label) main::@2 +(label) main::@3 +(label) main::@return +(byte) main::i +(byte) main::i#1 reg byte x 109.25 +(byte) main::i#2 reg byte x 213.0 + +reg byte x [ main::i#2 main::i#1 ]