diff options
| author | Cliff Click <cliffc@acm.org> | 2026-11-01 10:33:55 -0800 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-11-01 10:33:55 -0800 |
| commit | f4bd723d929e53475f7990374dca7b7dfcbbdc42 (patch) | |
| tree | 7bb080119e7b88ad4ac9b30af019fe6a5e4ba9dd /chapter29/src/test/java/com/seaofnodes/simple/Chapter10Test.java | |
| parent | 0ac0c9e10287972ac3af5a4ee43d26f0d5b318fe (diff) | |
| download | Simple-main.tar.gz Simple-main.zip | |
Add Chapter 29 for tech debt cleanup with the parser splitmain
Keep Chapter 28b intact and carry the grammar-oriented parser package, Var move, IterPeeps move, and parser regression tests into Chapter 29. Defer privacy and exclude its experimental tests and fixture changes.
Register the chapter in the indexes, IntelliJ modules and run configurations, Maven reactor, roadmap, and linear-history publisher. Windows and Linux each pass 573 tests, release builds, 199 allocation replays, and 29 native checks.
Diffstat (limited to 'chapter29/src/test/java/com/seaofnodes/simple/Chapter10Test.java')
| -rw-r--r-- | chapter29/src/test/java/com/seaofnodes/simple/Chapter10Test.java | 511 |
1 files changed, 511 insertions, 0 deletions
diff --git a/chapter29/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter29/src/test/java/com/seaofnodes/simple/Chapter10Test.java new file mode 100644 index 00000000..2c9a69a7 --- /dev/null +++ b/chapter29/src/test/java/com/seaofnodes/simple/Chapter10Test.java @@ -0,0 +1,511 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.parser.Parser; +import com.seaofnodes.simple.codegen.CodeGen; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class Chapter10Test { + @Test public void testConditionalStoreSharing() { + // Sharing the writes on two of three paths must not write on the third. + // Return the object so Load forwarding cannot hide the physical Store. + String src = """ + struct S { int x; }; + !S !a = new S; + a.x = 10; + if (arg == 1) a.x = 9; + else if (arg == 2) a.x = 9; + return a; + """; + var code = new CodeGen(src).driver(CodeGen.Phase.LocalSched); + for( int arg : new int[]{0,1,2,3} ) { + long expected = (arg==1 || arg==2) ? 9 : 10; + assertEquals("Test.S{x="+expected+"}",Eval2.eval(code,arg)); + } + } + + @Test public void testDeadReferenceReturn() { + for( String body : new String[] { + "return new S; return 0;", + "if(1) return new S; else return 0;" + } ) { + String src = "struct S { int x; }; " + body; + var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + // This chapter also has constructor/class-init Returns in the CompUnit. + var entry = (com.seaofnodes.simple.node.FunNode)code._start.uctrl(); + org.junit.Assert.assertTrue(body, + entry.ret().expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr); + } + } + + + + private static final String NULLABLE_POINT_SOURCE = """ + struct Point { int x; new Point = { int v -> x = v; }; }; + !Point?[] !points = new !Point?[2]; + points[arg] = new Point(42); + Point? p = points[1]; + """; + + @Test + public void testNullGuards() { + // Issue #246: != null lowers to !!p. Follow every negation, on either arm. + for( String body : new String[] { + "if (p != null) return p.x; return -1;", + "if (null != p) return p.x; return -1;", + "if (!!!!p) return p.x; return -1;", + "if (!!!p) return -1; return p.x;", + "val b = !!p; if (b) return p.x + b - 1; return -1;", + "if (!(p == null || arg == 0)) return p.x; return -1;" + } ) + for( long seed : new long[] { 1, 42, 126 } ) { + CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body,seed,true) + .driver(CodeGen.Phase.TypeCheck); + assertEquals(body,"-1",Eval2.eval(code,0)); + assertEquals(body,"42",Eval2.eval(code,1)); + code.driver(CodeGen.Phase.LocalSched); + } + } + + @Test + public void testShortCircuitGuardScheduling() { + // The call result exists only on the RHS path, not above the merge. + CodeGen code = new CodeGen(""" + val f = { int n -> (n+1)&7; }; + int x = 0; + if (!!(arg && (x=f(arg)))) return x; + return -1; + """).driver(CodeGen.Phase.TypeCheck); + assertEquals("-1",Eval2.eval(code,0)); + assertEquals("2",Eval2.eval(code,1)); + assertEquals("-1",Eval2.eval(code,7)); + code.driver(CodeGen.Phase.LocalSched); + } + + @Test + public void testNullGuardErrors() { + for( String body : new String[] { + "return p.x;", + "if (p == null) return p.x; return -1;", + "if (!!points) return p.x; return -1;", + "if (!!p) { int x = p.x; } return p.x;" + } ) { + try { + new CodeGen(NULLABLE_POINT_SOURCE+body).driver(CodeGen.Phase.TypeCheck); + fail(body); + } catch( Parser.ParseException e ) { + assertEquals(body,"Might be null accessing 'x'",e.getMessage()); + } + } + } + + @Test + public void testFuzzer() { + CodeGen code = new CodeGen( +""" +int a = arg/3; +int b = arg*5; +int x = arg*7; +int y = arg/11; +int p; int g; int h; +if( (arg/13)==0 ) { + p = x + y; + g = x; + h = y; +} else { + p = a + b; + g = a; + h = b; +} +int r = g+h; +return p-r; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 0;", code.print()); + } + + @Test + public void testStruct() { + CodeGen code = new CodeGen(""" +struct _Bar { + int a; + int b; +}; +struct _Foo { + int x; +}; +_Foo? foo = null; +!_Bar !bar = new _Bar; +bar.a = 1; +bar.a = 2; +return bar.a; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 2;", code.print()); + } + + @Test + public void testExample() { + CodeGen code = new CodeGen(""" +struct Vector2D { int x; int y; }; +!Vector2D !v = new Vector2D; +v.x = 1; +if (arg) + v.y = 2; +else + v.y = 3; +return v; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("Stop[ return Test.Vector2D; return MEM[ 2:___ 3:.x=0; 4:.y=0;]; return Test.Vector2D; ]", code.print()); + } + + @Test + public void testBug() { + CodeGen code = new CodeGen(""" +struct s0 { + int v0; +}; +s0? v1=null; +int v3=v1.zAicm; +"""); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Might be null accessing 'zAicm'",e.getMessage()); } + } + + @Test + public void testBug2() { + CodeGen code = new CodeGen(""" +struct s0 { int v0; }; +arg=0+new s0.0; +"""); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Expected an identifier, found 'null'",e.getMessage()); } + } + + @Test + public void testLoop() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +!Bar !bar = new Bar; +while (arg) { + bar.a = bar.a + 2; + arg = arg + 1; +} +return bar.a; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("Stop[ return Phi(Loop,0,(Phi_a+2)); return MEM[ 2:___ 3:.a=0;]; return Test.Bar; ]", code.print()); + } + + @Test + public void testIf() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +!Bar !bar = new Bar; +if (arg) bar = null; +bar.a = 1; +return bar.a; +"""); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Type null is not of declared type *Test.Bar",e.getMessage()); } + } + + @Test + public void testIf2() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +!Bar? !bar = null; +if (arg) bar = new Bar; +bar.a = 1; +return bar.a; +"""); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); } + } + + @Test + public void testIf3() { + CodeGen code = new CodeGen(""" +struct Bar { int a; }; +!Bar !bar = null; +if (arg) bar = null; +bar.a = 1; +return bar.a; +"""); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Type null is not of declared type *Test.Bar", e.getMessage()); } + } + + @Test + public void testIfOrNull() { + CodeGen code = new CodeGen(""" +struct _Bar { int a; }; +!_Bar? !bar = new _Bar; +if (arg) bar = null; +if( bar ) bar.a = 1; +return bar.a; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Region,1,.a);", code.print()); + } + + @Test + public void testIfOrNull2() { + CodeGen code = new CodeGen( +""" +struct _Bar { int a; }; +!_Bar? !bar = new _Bar; +if (arg) bar = null; +int rez = 3; +if( !bar ) rez=4; +else bar.a = 1; +return rez; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return Phi(Region,4,3);", code.print()); + } + + @Test + public void testWhileWithNullInside() { + CodeGen code = new CodeGen(""" +struct s0 {int v0;}; +!s0? !v0 = new s0; +int ret = 0; +while(arg) { + ret = v0.v0; + v0 = null; + arg = arg - 1; +} +return ret; +"""); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { + assertEquals("Might be null accessing 'v0'", e.getMessage()); } + } + + @Test + public void testRedeclareStruct() { + CodeGen code = new CodeGen(""" +struct s0 { + int v0; +}; +s0? v1=new s0; +!s0? !v1; +v1=new s0; +"""); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Redefining name 'v1'", e.getMessage()); } + } + + @Test + public void testIter() { + // Build and use an iterator + CodeGen code = new CodeGen( +""" +struct Iter { + int x; + int len; +}; +!Iter !i = new Iter; +i.len = arg; +int sum=0; +while( i.x < i.len ) { + sum = sum + i.x; + i.x = i.x + 1; +} +return sum; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("Stop[ return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum)); return MEM[ 2:___ 3:.x=0; 4:.len=0;]; return Test.Iter; ]", code.print()); + } + + + @Test + public void test1() { + CodeGen code = new CodeGen(""" +struct s0 {int v0;}; +!s0 !ret = new s0; +while(arg) { + !s0 !v0 = new s0; + v0.v0 = arg; + arg = arg-1; + if (arg==5) ret=v0; + +} +return ret; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("Stop[ return Phi(Loop,Test.s0,Phi(Region,Test.s0,Phi_ret)); return MEM[ 2:___ 3:.v0=0;]; return Test.s0; ]", code.print()); + } + + @Test + public void test2() { + CodeGen code = new CodeGen(""" +struct _s0 {int v0;}; +!_s0 !ret = new _s0; +!_s0 !v0 = new _s0; +while(arg) { + v0.v0 = arg; + arg = arg-1; + if (arg==5) ret=v0; +} +return ret.v0; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return .v0;", code.print()); + } + + + @Test + public void test3() { + CodeGen code = new CodeGen(""" +struct _s0 {int v0;}; +!_s0 !ret = new _s0; +while(arg < 10) { + !_s0 !v1 = new _s0; + if (arg == 5) ret=v1; + arg = arg + 1; +} +return ret.v0; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return .v0;", code.print()); + } + + + + @Test + public void testModGlobalInConstructor() { + CodeGen code = new CodeGen( +""" +int cnt = 1; +// cnt is incremented exactly once, it is not part of s0 constructor +// hence s0.x is always 1, and cnt remains at 2 +struct s0 { int x = cnt++; }; +s0 a = new s0(); +s0 b = new s0(); +// a.x==1, b.x==1, cnt==2 +return a.x * 100 + b.x * 10 + cnt; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("Stop[ return 112; return MEM[ 2:___ 3:___ 4:.x=1;]; return Test.s0; ]", code.print()); + } + + + @Test + public void testBug3() { + CodeGen code = new CodeGen(""" +struct s0 { int f0; }; +return new s0; +int v0=null.f0; +"""); + try { code.parse(); fail(); } + catch( Exception e ) { assertEquals("Syntax error, expected `;` but found `.`",e.getMessage()); } + } + + @Test + public void testBug4() { + CodeGen code = new CodeGen(""" +if(0) { + while(0) if(arg) continue; + int v0=0; + while(1) { + int v2=-arg; + v0=v2; + } +} +return 0; + """); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 0;", code.print()); + } + + @Test + public void testBug5() { + CodeGen code = new CodeGen(""" +struct s0 { + int f0; +}; +if(0) return 0; +else return new s0; +if(new s0.f0) return 0; + """); + code.driver(CodeGen.Phase.TypeCheck); + assertEquals("Stop[ return Test.s0; return MEM[ 2:___ 3:.f0=0;]; return Test.s0; ]", code.print()); + } + + @Test + public void testBug6MissedWorklist() { + CodeGen code = new CodeGen(""" +while(0) {} +int v4=0; +while(0<arg) { + v4=v4+1; + while(1) v4=-v4; + while(0) arg=-1; +} +return 0; + """); + code.driver(CodeGen.Phase.FinalizeIR); + } + + @Test + public void testBug7() { + CodeGen code = new CodeGen(""" +struct s0 { int f0; }; +s0 v0 = new s0; +while(v0.f0) {} +s0 v1 = v0; +return v1; + """); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("Stop[ return (const)Test.s0; return MEM[ 2:___ 3:.f0=0;]; return Test.s0; ]", code.print()); + } + + + @Test + public void testBug8() { + CodeGen code = new CodeGen(""" +int v2=0; +while(0) +while(0) {} +{ + { + { + int v36=0; + { + while(0) { + { + while(-v2) { + { + while(v36) { + while(v2) return 0; + break; + } } + if(-v2) break; + } + } + } + } + } } +} +return 0; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 0;", code.print()); + } + + @Test + public void testBug9() { + CodeGen code = new CodeGen(""" +int v0=arg==0; +while(v0) continue; +return 0; +"""); + code.driver(CodeGen.Phase.FinalizeIR); + assertEquals("return 0;", code.print()); + } + +} |
