diff --git a/src/main/java/dk/camelot64/kickc/model/values/ForwardVariableRef.java b/src/main/java/dk/camelot64/kickc/model/values/ForwardVariableRef.java index 4b16224b4..376c5b665 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/ForwardVariableRef.java +++ b/src/main/java/dk/camelot64/kickc/model/values/ForwardVariableRef.java @@ -4,10 +4,10 @@ import dk.camelot64.kickc.model.Program; /** An unresolved forward variable reference. * Created in {@link dk.camelot64.kickc.passes.Pass0GenerateStatementSequence}. - * Resolved in {@link }. + * Resolved in {@link dk.camelot64.kickc.passes.Pass1ResolveForwardReferences}. * After this point they are never present in the program. * */ -public class ForwardVariableRef implements RValue { +public class ForwardVariableRef implements LValue { /** The referenced unresolved variable name. */ private String name; diff --git a/src/main/java/dk/camelot64/kickc/model/values/LValue.java b/src/main/java/dk/camelot64/kickc/model/values/LValue.java index a52959936..afe7e9638 100644 --- a/src/main/java/dk/camelot64/kickc/model/values/LValue.java +++ b/src/main/java/dk/camelot64/kickc/model/values/LValue.java @@ -8,7 +8,7 @@ public interface LValue extends RValue { /** * Singleton signalling that an RValue is never assigned and can safely be discarded as rvalue in phi-functions. */ - public static RValue VOID = new RValue() { + RValue VOID = new RValue() { @Override public String toString() { diff --git a/src/main/java/dk/camelot64/kickc/passes/Pass1ResolveForwardReferences.java b/src/main/java/dk/camelot64/kickc/passes/Pass1ResolveForwardReferences.java index 0e004cfe2..2c1e00949 100644 --- a/src/main/java/dk/camelot64/kickc/passes/Pass1ResolveForwardReferences.java +++ b/src/main/java/dk/camelot64/kickc/passes/Pass1ResolveForwardReferences.java @@ -5,7 +5,6 @@ import dk.camelot64.kickc.model.Program; import dk.camelot64.kickc.model.iterator.ProgramValueIterator; import dk.camelot64.kickc.model.symbols.Scope; import dk.camelot64.kickc.model.symbols.Symbol; -import dk.camelot64.kickc.model.symbols.Variable; import dk.camelot64.kickc.model.values.ForwardVariableRef; import dk.camelot64.kickc.model.values.RValue; diff --git a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java index 4492c9a76..f0f28c9fb 100644 --- a/src/test/java/dk/camelot64/kickc/test/TestPrograms.java +++ b/src/test/java/dk/camelot64/kickc/test/TestPrograms.java @@ -114,6 +114,11 @@ public class TestPrograms { // compileAndCompare("complex/robozzle64/robozzle64"); // } + //@Test + //public void testTravisGame() throws IOException, URISyntaxException { + // compileAndCompare("complex/travis/game"); + //} + @Test public void testTetrisSprites() throws IOException, URISyntaxException { compileAndCompare("complex/tetris/test-sprites"); @@ -466,6 +471,11 @@ public class TestPrograms { compileAndCompare("var-forward-problem"); } + @Test + public void testVarForwardProblem2() throws IOException, URISyntaxException { + compileAndCompare("var-forward-problem2"); + } + @Test public void testInlineString3() throws IOException, URISyntaxException { compileAndCompare("inline-string-3"); @@ -1056,6 +1066,11 @@ public class TestPrograms { assertError("useundeclared", "Unknown variable"); } + @Test + public void testassignUndeclared() throws IOException, URISyntaxException { + assertError("assignundeclared", "Unknown variable"); + } + @Test public void testUseUninitialized() throws IOException, URISyntaxException { assertError("useuninitialized", "Variable used before being defined"); diff --git a/src/test/kc/assignundeclared.kc b/src/test/kc/assignundeclared.kc new file mode 100644 index 000000000..04cbc3013 --- /dev/null +++ b/src/test/kc/assignundeclared.kc @@ -0,0 +1,4 @@ +// Assign an undeclared variable - should fail gracefully +void main() { + s = 1; +} \ No newline at end of file diff --git a/src/test/kc/var-forward-problem2.kc b/src/test/kc/var-forward-problem2.kc new file mode 100644 index 000000000..2013c0f15 --- /dev/null +++ b/src/test/kc/var-forward-problem2.kc @@ -0,0 +1,10 @@ +// Illustrates the problem with variable forward references not working + +void main() { + screen = $400; + b = 'a'; + *screen = b; +} + +byte* screen; +byte b; \ No newline at end of file diff --git a/src/test/ref/var-forward-problem2.asm b/src/test/ref/var-forward-problem2.asm new file mode 100644 index 000000000..7ab0cfb2c --- /dev/null +++ b/src/test/ref/var-forward-problem2.asm @@ -0,0 +1,11 @@ +// Illustrates the problem with variable forward references not working +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" + .label screen = $400 + .const b = 'a' +main: { + lda #b + sta screen + rts +} diff --git a/src/test/ref/var-forward-problem2.cfg b/src/test/ref/var-forward-problem2.cfg new file mode 100644 index 000000000..948b46b3c --- /dev/null +++ b/src/test/ref/var-forward-problem2.cfg @@ -0,0 +1,15 @@ +@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*) screen#0) ← (const byte) b#0 + to:main::@return +main::@return: scope:[main] from main + [5] return + to:@return diff --git a/src/test/ref/var-forward-problem2.log b/src/test/ref/var-forward-problem2.log new file mode 100644 index 000000000..0e9dc829d --- /dev/null +++ b/src/test/ref/var-forward-problem2.log @@ -0,0 +1,255 @@ +Resolved forward reference screen to (byte*) screen +Resolved forward reference b to (byte) b +Resolved forward reference b to (byte) b +Resolved forward reference screen to (byte*) screen + +CONTROL FLOW GRAPH SSA +@begin: scope:[] from + (byte) b#6 ← phi( ) + (byte*) screen#6 ← phi( ) + to:@1 +main: scope:[main] from @1 + (byte*) screen#0 ← ((byte*)) (word/signed word/dword/signed dword) 1024 + (byte) b#0 ← (byte) 'a' + *((byte*) screen#0) ← (byte) b#0 + to:main::@return +main::@return: scope:[main] from main + (byte) b#3 ← phi( main/(byte) b#0 ) + (byte*) screen#3 ← phi( main/(byte*) screen#0 ) + (byte*) screen#1 ← (byte*) screen#3 + (byte) b#1 ← (byte) b#3 + return + to:@return +@1: scope:[] from @begin + (byte) b#5 ← phi( @begin/(byte) b#6 ) + (byte*) screen#5 ← phi( @begin/(byte*) screen#6 ) + call main + to:@2 +@2: scope:[] from @1 + (byte) b#4 ← phi( @1/(byte) b#1 ) + (byte*) screen#4 ← phi( @1/(byte*) screen#1 ) + (byte*) screen#2 ← (byte*) screen#4 + (byte) b#2 ← (byte) b#4 + 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 +(void()) main() +(label) main::@return +(byte*) screen +(byte*) screen#0 +(byte*) screen#1 +(byte*) screen#2 +(byte*) screen#3 +(byte*) screen#4 +(byte*) screen#5 +(byte*) screen#6 + +Alias (byte*) screen#0 = (byte*) screen#3 (byte*) screen#1 +Alias (byte) b#0 = (byte) b#3 (byte) b#1 +Alias (byte*) screen#5 = (byte*) screen#6 +Alias (byte) b#5 = (byte) b#6 +Alias (byte*) screen#2 = (byte*) screen#4 +Alias (byte) b#2 = (byte) b#4 +Successful SSA optimization Pass2AliasElimination +Redundant Phi (byte*) screen#5 VOID +Redundant Phi (byte) b#5 VOID +Redundant Phi (byte*) screen#2 (byte*) screen#0 +Redundant Phi (byte) b#2 (byte) b#0 +Successful SSA optimization Pass2RedundantPhiElimination +Constant (const byte*) screen#0 = ((byte*))1024 +Constant (const byte) b#0 = 'a' +Successful SSA optimization Pass2ConstantIdentification +Culled Empty Block (label) @2 +Successful SSA optimization Pass2CullEmptyBlocks +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*) screen#0) ← (const byte) b#0 + to:main::@return +main::@return: scope:[main] from main + [5] return + to:@return + + +VARIABLE REGISTER WEIGHTS +(byte) b +(void()) main() +(byte*) screen + +Initial phi equivalence classes +Complete equivalence classes + +INITIAL ASM +//SEG0 File Comments +// Illustrates the problem with variable forward references not working +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .label screen = $400 + .const b = 'a' +//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: { + //SEG10 [4] *((const byte*) screen#0) ← (const byte) b#0 -- _deref_pbuc1=vbuc2 + lda #b + sta screen + jmp breturn + //SEG11 main::@return + breturn: + //SEG12 [5] return + rts +} + +REGISTER UPLIFT POTENTIAL REGISTERS +Statement [4] *((const byte*) screen#0) ← (const byte) b#0 [ ] ( main:2 [ ] ) always clobbers reg byte a + +REGISTER UPLIFT SCOPES +Uplift Scope [main] +Uplift Scope [] + +Uplifting [main] best 27 combination +Uplifting [] best 27 combination + +ASSEMBLER BEFORE OPTIMIZATION +//SEG0 File Comments +// Illustrates the problem with variable forward references not working +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(bbegin) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .label screen = $400 + .const b = 'a' +//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: { + //SEG10 [4] *((const byte*) screen#0) ← (const byte) b#0 -- _deref_pbuc1=vbuc2 + lda #b + sta screen + jmp breturn + //SEG11 main::@return + breturn: + //SEG12 [5] 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 +(const byte) b#0 b = (byte) 'a' +(void()) main() +(label) main::@return +(byte*) screen +(const byte*) screen#0 screen = ((byte*))(word/signed word/dword/signed dword) 1024 + + + +FINAL ASSEMBLER +Score: 12 + +//SEG0 File Comments +// Illustrates the problem with variable forward references not working +//SEG1 Basic Upstart +.pc = $801 "Basic" +:BasicUpstart(main) +.pc = $80d "Program" +//SEG2 Global Constants & labels + .label screen = $400 + .const b = 'a' +//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: { + //SEG10 [4] *((const byte*) screen#0) ← (const byte) b#0 -- _deref_pbuc1=vbuc2 + lda #b + sta screen + //SEG11 main::@return + //SEG12 [5] return + rts +} + diff --git a/src/test/ref/var-forward-problem2.sym b/src/test/ref/var-forward-problem2.sym new file mode 100644 index 000000000..b92799dd2 --- /dev/null +++ b/src/test/ref/var-forward-problem2.sym @@ -0,0 +1,10 @@ +(label) @1 +(label) @begin +(label) @end +(byte) b +(const byte) b#0 b = (byte) 'a' +(void()) main() +(label) main::@return +(byte*) screen +(const byte*) screen#0 screen = ((byte*))(word/signed word/dword/signed dword) 1024 +