1
0
mirror of https://gitlab.com/camelot/kickc.git synced 2024-12-17 16:31:34 +00:00

Implemented continue statement. Closes #91

This commit is contained in:
jespergravgaard 2019-03-31 20:15:01 +02:00
parent 4424ffe92c
commit 2d7825a0b9
14 changed files with 969 additions and 14 deletions

View File

@ -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;
}

View File

@ -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();

View File

@ -637,6 +637,9 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor<Object> {
/** 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<Object> {
}
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<Object> {
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<Object> {
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<Object> {
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<Object> {
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<Object> {
}
}
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<Object> {
@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

View File

@ -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");

View File

@ -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]++;
}
}

View File

@ -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]++;
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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 ]

View File

@ -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
}

View File

@ -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

View File

@ -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
}

View File

@ -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 ]