From 39e727711abdc3339b09e6addc3ca2cdaf8467a9 Mon Sep 17 00:00:00 2001 From: jespergravgaard Date: Fri, 8 Mar 2019 06:33:59 +0100 Subject: [PATCH] Added missing error location for illegal lvalues. Closes #143 --- .../Pass0GenerateStatementSequence.java | 3 + .../dk/camelot64/kickc/test/TestPrograms.java | 15 +- src/test/kc/illegallvalue2.kc | 6 + src/test/kc/useuninitialized.kc | 4 +- src/test/ref/bool-function.asm | 2 +- src/test/ref/bool-function.log | 6 +- src/test/ref/bool-vars.asm | 2 +- src/test/ref/bool-vars.log | 6 +- src/test/ref/travis1.log | 6 +- src/test/ref/useuninitialized.asm | 13 + src/test/ref/useuninitialized.cfg | 16 + src/test/ref/useuninitialized.log | 292 ++++++++++++++++++ src/test/ref/useuninitialized.sym | 12 + 13 files changed, 365 insertions(+), 18 deletions(-) create mode 100644 src/test/kc/illegallvalue2.kc create mode 100644 src/test/ref/useuninitialized.asm create mode 100644 src/test/ref/useuninitialized.cfg create mode 100644 src/test/ref/useuninitialized.log create mode 100644 src/test/ref/useuninitialized.sym diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java index 313660925..b46f5161e 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass0GenerateStatementSequence.java @@ -765,6 +765,9 @@ public class Pass0GenerateStatementSequence extends KickCBaseVisitor { @Override public Object visitExprAssignment(KickCParser.ExprAssignmentContext ctx) { Object val = visit(ctx.expr(0)); + if(!(val instanceof LValue)) { + throw new CompileError("Error! Illegal assignment Lvalue " + val.toString(), new StatementSource(ctx)); + } LValue lValue = (LValue) val; if(lValue instanceof VariableRef && ((VariableRef) lValue).isIntermediate()) { // Encountered an intermediate variable. This must be turned into a proper LValue later. Put it into a marker to signify that diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 8f6be545e..afd5978d9 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -44,6 +44,16 @@ public class TestPrograms { AsmFragmentTemplateUsages.logUsages(log, false, false, false, false, false, false); } + @Test + public void testUseUninitialized() throws IOException, URISyntaxException { + compileAndCompare("useuninitialized"); + } + + @Test + public void testIllegalLValue2() throws IOException, URISyntaxException { + assertError("illegallvalue2", "Illegal assignment Lvalue"); + } + @Test public void testWordFragment1() throws IOException, URISyntaxException { compileAndCompare("wfragment1"); @@ -1086,11 +1096,6 @@ public class TestPrograms { assertError("assignundeclared", "Unknown variable"); } - @Test - public void testUseUninitialized() throws IOException, URISyntaxException { - assertError("useuninitialized", "Variable used before being defined"); - } - @Test public void testUseUninitialized2() throws IOException, URISyntaxException { assertError("useuninitialized2", "Variable used before being defined"); diff --git a/src/test/kc/illegallvalue2.kc b/src/test/kc/illegallvalue2.kc new file mode 100644 index 000000000..ebb143ff1 --- /dev/null +++ b/src/test/kc/illegallvalue2.kc @@ -0,0 +1,6 @@ +// Syntax error without line number + +void main() { + $d020 = 0; +} + diff --git a/src/test/kc/useuninitialized.kc b/src/test/kc/useuninitialized.kc index 61c7c8696..2283a0b03 100644 --- a/src/test/kc/useuninitialized.kc +++ b/src/test/kc/useuninitialized.kc @@ -1,5 +1,5 @@ -// Use an uninitialized variable - should fail gracefully! -// Currently it gets into the optimization phase, where the optimization runs into problems understanding the assignment/usage-graph and ends up failing on an exception +// Use an uninitialized variable - should use the default value (0)! + byte b; byte s; byte w=2; diff --git a/src/test/ref/bool-function.asm b/src/test/ref/bool-function.asm index 248d23d9b..38b38df27 100644 --- a/src/test/ref/bool-function.asm +++ b/src/test/ref/bool-function.asm @@ -8,7 +8,7 @@ main: { b1: txa and #1 - cmp #0 + eor #0 beq !+ lda #1 !: diff --git a/src/test/ref/bool-function.log b/src/test/ref/bool-function.log index 97ee706aa..ca068ad55 100644 --- a/src/test/ref/bool-function.log +++ b/src/test/ref/bool-function.log @@ -302,7 +302,7 @@ main: { sta _0 //SEG17 [7] (bool) isSet::b#0 ← (byte~) main::$0 == (byte/signed byte/word/signed word/dword/signed dword) 0 -- vboz1=vbuz2_eq_vbuc1 lda _0 - cmp #0 + eor #0 beq !+ lda #1 !: @@ -468,7 +468,7 @@ main: { txa and #1 //SEG17 [7] (bool) isSet::b#0 ← (byte~) main::$0 == (byte/signed byte/word/signed word/dword/signed dword) 0 -- vboz1=vbuaa_eq_vbuc1 - cmp #0 + eor #0 beq !+ lda #1 !: @@ -638,7 +638,7 @@ main: { txa and #1 //SEG17 [7] (bool) isSet::b#0 ← (byte~) main::$0 == (byte/signed byte/word/signed word/dword/signed dword) 0 -- vboz1=vbuaa_eq_vbuc1 - cmp #0 + eor #0 beq !+ lda #1 !: diff --git a/src/test/ref/bool-vars.asm b/src/test/ref/bool-vars.asm index 4799bfc9e..04b58e34a 100644 --- a/src/test/ref/bool-vars.asm +++ b/src/test/ref/bool-vars.asm @@ -22,7 +22,7 @@ bool_complex: { sta o1 txa and #1 - cmp #0 + eor #0 beq !+ lda #1 !: diff --git a/src/test/ref/bool-vars.log b/src/test/ref/bool-vars.log index 6b20d5ea4..93f783fd0 100644 --- a/src/test/ref/bool-vars.log +++ b/src/test/ref/bool-vars.log @@ -712,7 +712,7 @@ bool_complex: { sta _1 //SEG35 [17] (bool) bool_complex::o2#0 ← (byte~) bool_complex::$1 == (byte/signed byte/word/signed word/dword/signed dword) 0 -- vboz1=vbuz2_eq_vbuc1 lda _1 - cmp #0 + eor #0 beq !+ lda #1 !: @@ -1116,7 +1116,7 @@ bool_complex: { txa and #1 //SEG35 [17] (bool) bool_complex::o2#0 ← (byte~) bool_complex::$1 == (byte/signed byte/word/signed word/dword/signed dword) 0 -- vboz1=vbuaa_eq_vbuc1 - cmp #0 + eor #0 beq !+ lda #1 !: @@ -1563,7 +1563,7 @@ bool_complex: { txa and #1 //SEG35 [17] (bool) bool_complex::o2#0 ← (byte~) bool_complex::$1 == (byte/signed byte/word/signed word/dword/signed dword) 0 -- vboz1=vbuaa_eq_vbuc1 - cmp #0 + eor #0 beq !+ lda #1 !: diff --git a/src/test/ref/travis1.log b/src/test/ref/travis1.log index 791e774a6..d64e550ba 100644 --- a/src/test/ref/travis1.log +++ b/src/test/ref/travis1.log @@ -952,11 +952,11 @@ game_ready: { dec action_count //SEG83 [38] (bool) game_ready::return#1 ← (byte) action_count#11 == (byte/signed byte/word/signed word/dword/signed dword) 0 -- vboz1=vbuz2_eq_vbuc1 lda action_count - lda #1 - cmp #0 + eor #0 beq !+ - lda #0 + lda #1 !: + eor #1 sta return_1 jmp breturn //SEG84 game_ready::@return diff --git a/src/test/ref/useuninitialized.asm b/src/test/ref/useuninitialized.asm new file mode 100644 index 000000000..24be884ea --- /dev/null +++ b/src/test/ref/useuninitialized.asm @@ -0,0 +1,13 @@ +// Use an uninitialized variable - should use the default value (0)! +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .const s = 1 +main: { + .label screen = $400 + lda #3 + sta screen + lda #s + sta screen+1 + rts +} diff --git a/src/test/ref/useuninitialized.cfg b/src/test/ref/useuninitialized.cfg new file mode 100644 index 000000000..989dd68fa --- /dev/null +++ b/src/test/ref/useuninitialized.cfg @@ -0,0 +1,16 @@ +@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] *((const byte*) main::screen#0) ← (byte/signed byte/word/signed word/dword/signed dword) 3 + [5] *((const byte*) main::screen#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) s#1 + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return diff --git a/src/test/ref/useuninitialized.log b/src/test/ref/useuninitialized.log new file mode 100644 index 000000000..f3da2f4e4 --- /dev/null +++ b/src/test/ref/useuninitialized.log @@ -0,0 +1,292 @@ + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte) b#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + (byte) s#0 ← (byte/signed byte/word/signed word/dword/signed dword) 0 + (byte) w#0 ← (byte/signed byte/word/signed word/dword/signed dword) 2 + to:@1 +main: scope:[main] from @1 + (byte) b#4 ← phi( @1/(byte) b#7 ) + (byte/signed word/word/dword/signed dword~) main::$0 ← (byte) b#4 + (byte/signed byte/word/signed word/dword/signed dword) 1 + (byte) s#1 ← (byte/signed word/word/dword/signed dword~) main::$0 + (byte) b#1 ← (byte/signed byte/word/signed word/dword/signed dword) 3 + (byte*) main::screen#0 ← ((byte*)) (word/signed word/dword/signed dword) 1024 + *((byte*) main::screen#0) ← (byte) b#1 + (byte*~) main::$1 ← (byte*) main::screen#0 + (byte/signed byte/word/signed word/dword/signed dword) 1 + *((byte*~) main::$1) ← (byte) s#1 + to:main::@return +main::@return: scope:[main] from main + (byte) b#5 ← phi( main/(byte) b#1 ) + (byte) s#4 ← phi( main/(byte) s#1 ) + (byte) s#2 ← (byte) s#4 + (byte) b#2 ← (byte) b#5 + return + to:@return +@1: scope:[] from @begin + (byte) s#6 ← phi( @begin/(byte) s#0 ) + (byte) b#7 ← phi( @begin/(byte) b#0 ) + call main + to:@2 +@2: scope:[] from @1 + (byte) b#6 ← phi( @1/(byte) b#2 ) + (byte) s#5 ← phi( @1/(byte) s#2 ) + (byte) s#3 ← (byte) s#5 + (byte) b#3 ← (byte) b#6 + to:@end +@end: scope:[] from @2 + +SYMBOL TABLE SSA +(label) @1 +(label) @2 +(label) @begin +(label) @end +(byte) b +(byte) b#0 +(byte) b#1 +(byte) b#2 +(byte) b#3 +(byte) b#4 +(byte) b#5 +(byte) b#6 +(byte) b#7 +(void()) main() +(byte/signed word/word/dword/signed dword~) main::$0 +(byte*~) main::$1 +(label) main::@return +(byte*) main::screen +(byte*) main::screen#0 +(byte) s +(byte) s#0 +(byte) s#1 +(byte) s#2 +(byte) s#3 +(byte) s#4 +(byte) s#5 +(byte) s#6 +(byte) w +(byte) w#0 + +Alias (byte) s#1 = (byte/signed word/word/dword/signed dword~) main::$0 (byte) s#4 (byte) s#2 +Alias (byte) b#1 = (byte) b#5 (byte) b#2 +Alias (byte) b#0 = (byte) b#7 +Alias (byte) s#0 = (byte) s#6 +Alias (byte) s#3 = (byte) s#5 +Alias (byte) b#3 = (byte) b#6 +Successful SSA optimization Pass2AliasElimination +Redundant Phi (byte) b#4 (byte) b#0 +Redundant Phi (byte) s#3 (byte) s#1 +Redundant Phi (byte) b#3 (byte) b#1 +Successful SSA optimization Pass2RedundantPhiElimination +Constant (const byte) b#0 = 0 +Constant (const byte) s#0 = 0 +Constant (const byte) w#0 = 2 +Constant (const byte) b#1 = 3 +Constant (const byte*) main::screen#0 = ((byte*))1024 +Successful SSA optimization Pass2ConstantIdentification +Constant (const byte) s#1 = b#0+1 +Constant (const byte*) main::$1 = main::screen#0+1 +Successful SSA optimization Pass2ConstantIdentification +Successful SSA optimization PassNEliminateUnusedVars +Culled Empty Block (label) @2 +Successful SSA optimization Pass2CullEmptyBlocks +Inlining constant with different constant siblings (const byte) b#0 +Inlining constant with different constant siblings (const byte) b#1 +Constant inlined main::$1 = (const byte*) main::screen#0+(byte/signed byte/word/signed word/dword/signed dword) 1 +Constant inlined b#1 = (byte/signed byte/word/signed word/dword/signed dword) 3 +Constant inlined b#0 = (byte/signed byte/word/signed word/dword/signed dword) 0 +Successful SSA optimization Pass2ConstantInlining +Simplifying constant plus zero 0+1 +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end +CALL GRAPH +Calls in [] to main:2 + +Created 0 initial phi equivalence classes +Coalesced down to 0 phi equivalence classes +Adding NOP phi() at start of @begin +Adding NOP phi() at start of @1 +Adding NOP phi() at start of @end + +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] *((const byte*) main::screen#0) ← (byte/signed byte/word/signed word/dword/signed dword) 3 + [5] *((const byte*) main::screen#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) s#1 + to:main::@return +main::@return: scope:[main] from main + [6] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte) b +(void()) main() +(byte*) main::screen +(byte) s +(byte) w + +Initial phi equivalence classes +Complete equivalence classes + +INITIAL ASM +//SEG0 File Comments +// Use an uninitialized variable - should use the default value (0)! +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .const s = 1 +//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 + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .label screen = $400 + //SEG10 [4] *((const byte*) main::screen#0) ← (byte/signed byte/word/signed word/dword/signed dword) 3 -- _deref_pbuc1=vbuc2 + lda #3 + sta screen + //SEG11 [5] *((const byte*) main::screen#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) s#1 -- _deref_pbuc1=vbuc2 + lda #s + sta screen+1 + jmp breturn + //SEG12 main::@return + breturn: + //SEG13 [6] return + rts +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((const byte*) main::screen#0) ← (byte/signed byte/word/signed word/dword/signed dword) 3 [ ] ( main:2 [ ] ) always clobbers reg byte a +Statement [5] *((const byte*) main::screen#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) s#1 [ ] ( main:2 [ ] ) always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [main] +Uplift Scope [] + +Uplifting [main] best 33 combination +Uplifting [] best 33 combination + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Use an uninitialized variable - should use the default value (0)! +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .const s = 1 +//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 + jsr main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +bend_from_b1: + jmp bend +//SEG8 @end +bend: +//SEG9 main +main: { + .label screen = $400 + //SEG10 [4] *((const byte*) main::screen#0) ← (byte/signed byte/word/signed word/dword/signed dword) 3 -- _deref_pbuc1=vbuc2 + lda #3 + sta screen + //SEG11 [5] *((const byte*) main::screen#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) s#1 -- _deref_pbuc1=vbuc2 + lda #s + sta screen+1 + jmp breturn + //SEG12 main::@return + breturn: + //SEG13 [6] return + rts +} + +ASSEMBLER OPTIMIZATIONS +Removing instruction jmp b1 +Removing instruction jmp bend +Removing instruction jmp breturn +Succesful ASM optimization Pass5NextJumpElimination +Removing instruction b1_from_bbegin: +Removing instruction b1: +Removing instruction bend_from_b1: +Succesful ASM optimization Pass5RedundantLabelElimination +Removing instruction bend: +Removing instruction breturn: +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) b +(void()) main() +(label) main::@return +(byte*) main::screen +(const byte*) main::screen#0 screen = ((byte*))(word/signed word/dword/signed dword) 1024 +(byte) s +(const byte) s#1 s = (byte/signed byte/word/signed word/dword/signed dword) 1 +(byte) w + + + +FINAL ASSEMBLER +Score: 18 + +//SEG0 File Comments +// Use an uninitialized variable - should use the default value (0)! +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .const s = 1 +//SEG3 @begin +//SEG4 [1] phi from @begin to @1 [phi:@begin->@1] +//SEG5 @1 +//SEG6 [2] call main +//SEG7 [3] phi from @1 to @end [phi:@1->@end] +//SEG8 @end +//SEG9 main +main: { + .label screen = $400 + //SEG10 [4] *((const byte*) main::screen#0) ← (byte/signed byte/word/signed word/dword/signed dword) 3 -- _deref_pbuc1=vbuc2 + lda #3 + sta screen + //SEG11 [5] *((const byte*) main::screen#0+(byte/signed byte/word/signed word/dword/signed dword) 1) ← (const byte) s#1 -- _deref_pbuc1=vbuc2 + lda #s + sta screen+1 + //SEG12 main::@return + //SEG13 [6] return + rts +} + diff --git a/src/test/ref/useuninitialized.sym b/src/test/ref/useuninitialized.sym new file mode 100644 index 000000000..2b2122ecf --- /dev/null +++ b/src/test/ref/useuninitialized.sym @@ -0,0 +1,12 @@ +(label) @1 +(label) @begin +(label) @end +(byte) b +(void()) main() +(label) main::@return +(byte*) main::screen +(const byte*) main::screen#0 screen = ((byte*))(word/signed word/dword/signed dword) 1024 +(byte) s +(const byte) s#1 s = (byte/signed byte/word/signed word/dword/signed dword) 1 +(byte) w +