diff options
| author | Cliff Click <cliffc@acm.org> | 2025-04-21 14:18:23 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-10-19 14:56:14 -0700 |
| commit | 68a012df32c0ce9464fd1482bd1debc321abdf75 (patch) | |
| tree | e2ad770da52c5e3c1eaa8f3edbcd52aa3eefa83c /src/test/java/com/seaofnodes/simple | |
| parent | 46f02316703bcaf368d098527acf58e2c0dc2ae0 (diff) | |
| download | Simple-68a012df32c0ce9464fd1482bd1debc321abdf75.tar.gz Simple-68a012df32c0ce9464fd1482bd1debc321abdf75.zip | |
Chapter 22: A Hello, World!
Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com>
Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com>
Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com>
Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com>
Co-authored-by: Symious <14933944+symious@users.noreply.github.com>
Co-authored-by: Jun Kawasaki <jun784@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'src/test/java/com/seaofnodes/simple')
55 files changed, 1187 insertions, 499 deletions
diff --git a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java index 97d33b70..9f626805 100644 --- a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java +++ b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java @@ -20,7 +20,7 @@ public class BrainFuckTest { String brain_fuck = "Hello World!\n"; TestC.run("brain_fuck", brain_fuck, 40); - EvalRisc5 R5 = TestRisc5.build("brain_fuck", 0, 42, false); + EvalRisc5 R5 = TestRisc5.build("brain_fuck", 0, 28, false); int trap = R5.step(100000); assertEquals(0,trap); int ptr = (int)R5.regs[riscv.A0]; @@ -28,7 +28,7 @@ public class BrainFuckTest { for( int i=0; i<brain_fuck.length(); i++ ) assertEquals(brain_fuck.charAt(i), R5.ld1z(ptr+4+i)); - EvalArm64 A5 = TestArm64.build("brain_fuck", 0, 34, false); + EvalArm64 A5 = TestArm64.build("brain_fuck", 0, 28, false); int trap_arm = A5.step(100000); assertEquals(0,trap_arm); assertEquals(0,trap); diff --git a/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/src/test/java/com/seaofnodes/simple/Chapter01Test.java index cddbdfcf..ac9adacc 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter01Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter01Test.java @@ -25,9 +25,9 @@ public class Chapter01Test { public void testZero() { CodeGen code = new CodeGen("return 0;"); code.parse(); - for( Node use : code._start._outputs ) - if( use instanceof ConstantNode con && con._type instanceof TypeInteger ) - assertEquals(TypeInteger.constant(0),con._type); + Node use = code.expr(); + if( use instanceof ConstantNode con && con._type instanceof TypeInteger ) + assertEquals(TypeInteger.constant(0),con._type); } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/src/test/java/com/seaofnodes/simple/Chapter05Test.java index c809f72f..8168594a 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter05Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter05Test.java @@ -20,7 +20,7 @@ else { } return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,(arg+2),(arg-3));", code.print()); } @@ -36,7 +36,7 @@ if (arg == 1) { } return c; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,4,3);", code.print()); } @@ -64,7 +64,7 @@ if( arg==1 ) else b=a+1; return a+b;"""); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return ((arg<<1)+Phi(Region,2,3));", code.print()); } @@ -79,7 +79,7 @@ if( arg==1 ) else a=b+1; return a+b;"""); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return ((Phi(Region,(arg<<1),arg)+arg)+Phi(Region,4,5));", code.print()); } @@ -99,7 +99,7 @@ else a=5; return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,3,5,2,4);", code.print()); } @@ -115,7 +115,7 @@ if( arg==0 ) b=2; return arg+a+b; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));", code.print()); } @@ -130,7 +130,7 @@ if( arg==1 ) } return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return (arg==Phi(Region,3,2));", code.print()); } @@ -145,7 +145,7 @@ else a=3; return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3;", code.print()); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/src/test/java/com/seaofnodes/simple/Chapter06Test.java index 2c97b9c2..ad3597f2 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter06Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter06Test.java @@ -15,7 +15,7 @@ public class Chapter06Test { if( true ) return 2; return 1; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 2;", code.print()); assertTrue(code.ctrl() instanceof FunNode); } @@ -29,7 +29,7 @@ if (arg) a = 2; return (arg < a) < 3; // Because (arg < a) is a bool/uint1/[0-1], its always less than 3 """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 1;", code.print()); } @@ -44,7 +44,7 @@ else a=3; return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 2;", code.print()); assertTrue(code.ctrl() instanceof FunNode); } @@ -65,7 +65,7 @@ else b=5; return b; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,42,5);", code.print()); } @@ -84,7 +84,7 @@ if( arg==2 ) else b=5; return b;"""); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,2,5);", code.print()); } @@ -104,7 +104,7 @@ else if( arg==3 ) else a=5; return a; -""", TypeInteger.constant(2), 123L).parse().iter(); +""", TypeInteger.constant(2), 123L).parse().iter().finalizeIR(); assertEquals("return 5;", code.print()); } @@ -124,7 +124,7 @@ else if( arg==3 ) else a=5; return a; -""", TypeInteger.constant(1), 123L).parse().iter(); +""", TypeInteger.constant(1), 123L).parse().iter().finalizeIR(); assertEquals("return 3;", code.print()); } @@ -144,7 +144,7 @@ else a=5; return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,3,5,4);", code.print()); } @@ -165,7 +165,7 @@ else a=5; return a; """, TypeInteger.constant(1), 123L); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3;", code.print()); } @@ -186,7 +186,7 @@ else a=5; return a; """, TypeInteger.constant(3), 123L); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 4;", code.print()); } @@ -202,7 +202,7 @@ if( arg ) { } return a+b; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,4,1);", code.print()); } @@ -221,7 +221,7 @@ if( arg ) { } return a+b; """, TypeInteger.constant(1), 123L); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 4;", code.print()); } @@ -239,7 +239,7 @@ if( arg ) { } return a+b; """, TypeInteger.constant(0), 123L); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 1;", code.print()); } @@ -257,7 +257,7 @@ if( arg ) { } return a+b+c; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,6,1,5);", code.print()); } @@ -278,7 +278,7 @@ if( arg ) { } return a+b+c; """, TypeInteger.constant(1), 123L); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 6;", code.print()); } @@ -298,7 +298,7 @@ if( arg ) { } return a+b+c; """, TypeInteger.constant(2), 123L); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 5;", code.print()); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/src/test/java/com/seaofnodes/simple/Chapter07Test.java index b0526eb1..0ebc427c 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter07Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter07Test.java @@ -34,7 +34,7 @@ if(arg){}else{ } return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,1,Phi(Loop,1,(Phi_a+1)));", code.print()); } @@ -54,7 +54,7 @@ while(i < arg) { } return sum; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,0,Phi(Loop,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", code.print()); } @@ -89,7 +89,7 @@ while(a < 10) { } return b; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } @@ -122,7 +122,7 @@ while(a < 10) { } return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } @@ -148,7 +148,7 @@ int a = 1; while(arg) a = 2; return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,1,2);", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } @@ -180,7 +180,7 @@ while(a < 10) { } return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } @@ -214,7 +214,7 @@ while(a < 10) { } return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/src/test/java/com/seaofnodes/simple/Chapter08Test.java index 487a2c7b..543b91d1 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter08Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter08Test.java @@ -22,7 +22,7 @@ while(arg < 10) { } return arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,(Phi(Loop,arg,Add)+1),Add,Phi_arg);", code.print()); assertTrue(code.ctrl() instanceof RegionNode); Assert.assertEquals( "5", Eval2.eval(code, 1)); @@ -45,7 +45,7 @@ while(arg < 10) { } return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1),Phi_a));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } @@ -64,7 +64,7 @@ while(arg < 10) { } return arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print()); assertTrue(code.ctrl() instanceof RegionNode); } @@ -80,7 +80,7 @@ while(arg < 10) { } return arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print()); assertTrue(code.ctrl() instanceof RegionNode); } @@ -99,7 +99,7 @@ while(arg < 10) { } return arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } @@ -116,7 +116,7 @@ while(arg < 10) { } return arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } @@ -132,14 +132,14 @@ while( arg < 10 ) { } return arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return arg;", code.print()); } @Test public void testRegress2() { CodeGen code = new CodeGen("if(1) return 0; else while(arg>- -arg) arg=arg+1; return 0;"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -167,7 +167,7 @@ while(arg < 10) { } return arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return arg;", code.print()); } @@ -184,7 +184,7 @@ while(arg < 10) { } return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,Phi(Loop,1,(Phi_a+1)),Add);", code.print()); assertTrue(code.ctrl() instanceof RegionNode); } @@ -200,7 +200,7 @@ while(1) { } return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return (Phi(Loop,1,Add)+1);", code.print()); assertTrue(code.ctrl() instanceof CProjNode prj && prj._idx==1 ); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java index 0a3049e6..df590448 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java @@ -24,7 +24,7 @@ while(v0) { } return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); } @Test @@ -40,7 +40,7 @@ else { } return x; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,(arg<<1),(Shl+1));", code.print()); Assert.assertEquals( "2", Eval2.eval(code, 1)); Assert.assertEquals("23", Eval2.eval(code, 11)); @@ -49,7 +49,7 @@ return x; @Test public void testGVN2() { CodeGen code = new CodeGen("return arg*arg-arg*arg;"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); Assert.assertEquals("0", Eval2.eval(code, 1, 1)); } @@ -63,7 +63,7 @@ while (arg < 10) { } return arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,arg,(Phi_arg+2));", code.print()); Assert.assertEquals("11", Eval2.eval(code, 1)); } @@ -80,7 +80,7 @@ while (arg < 10) { } return arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,arg,(Phi_arg+4));", code.print()); Assert.assertEquals("13", Eval2.eval(code, 1)); } @@ -109,7 +109,7 @@ while (arg) { } return arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -127,15 +127,15 @@ while(v1+arg) { v1=v2; } """); - code.parse().iter(); - assertEquals("return 0;", code.print()); + code.parse().iter().finalizeIR(); + assertEquals("Stop[ ]", code.print()); // No explicit return: omit default main. } @Test public void testWhile0() { - CodeGen code = new CodeGen("while(0) continue; if(0) arg=0;"); - code.parse().iter(); - assertEquals("return 0;", code.print()); + CodeGen code = new CodeGen("while(0) continue; if(0) arg=0; return arg;"); + code.parse().iter().finalizeIR(); + assertEquals("return arg;", code.print()); } @Test @@ -145,8 +145,9 @@ if(0) while(0) { int arg=arg; while(0) {} } +return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -154,14 +155,14 @@ if(0) while(0) { @Test public void testPrecedence() { CodeGen code = new CodeGen("return 3-1+2;"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 4;", code.print()); } @Test public void testSwap2() { CodeGen code = new CodeGen("return 1+(1+1);"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3;", code.print()); } @@ -179,7 +180,7 @@ while(arg) { } return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,0,(-(Phi_a+3)));", code.print()); } @@ -193,7 +194,7 @@ arg=0; int v0=0!=0<-0; return -0+0+0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Top;", code.print()); } @@ -201,15 +202,15 @@ return -0+0+0; @Test public void testFuzz2() { CodeGen code = new CodeGen("return 0+-0;"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @Test public void testFuzz3() { CodeGen code = new CodeGen("int v0=0; while(0==69) while(v0) return 0;"); - code.parse().iter(); - assertEquals("return 0;", code.print()); + code.parse().iter().finalizeIR(); + assertEquals("Stop[ ]", code.print()); // The only return is unreachable. } @Test @@ -220,7 +221,7 @@ while(1) { if(1<0) while(arg==-0) arg=arg-arg; } """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Top;", code.print()); } @@ -242,7 +243,7 @@ while(1) { } return 0!=0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Top;", code.print()); } @@ -254,8 +255,8 @@ int v0=0; while(0==1) while(v0) v0=1+v0; """); - code.parse().iter(); - assertEquals("return 0;", code.print()); + code.parse().iter().finalizeIR(); + assertEquals("Stop[ ]", code.print()); } @Test @@ -270,14 +271,14 @@ while(1) v1=1; return v1+v0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Top;", code.print()); } @Test public void testFuzz8() { CodeGen code = new CodeGen("while(arg) arg = arg - 1; return arg;"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/src/test/java/com/seaofnodes/simple/Chapter10Test.java index 3e01458b..5fbae210 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter10Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter10Test.java @@ -32,7 +32,7 @@ public class Chapter10Test { "if(1) return new S; else return 0;" } ) { String src = "struct S { int x; }; " + body; - var code = new CodeGen(src).parse().iter().typeCheck(); + var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); org.junit.Assert.assertTrue(body, code.expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr); } @@ -57,7 +57,7 @@ public class Chapter10Test { "if (!!!p) return -1; return p.x;", "int b = !!p; if (b) return p.x + b - 1; return -1;" } ) { - CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().iter().typeCheck(); + CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).driver(CodeGen.Phase.TypeCheck); assertEquals(body,"-1",Eval2.eval(code,0)); assertEquals(body,"42",Eval2.eval(code,1)); } @@ -72,7 +72,7 @@ public class Chapter10Test { "if (!!p) { int x = p.x; } return p.x;" } ) { try { - new CodeGen(NULLABLE_POINT_SOURCE+body).parse().iter().typeCheck(); + new CodeGen(NULLABLE_POINT_SOURCE+body).driver(CodeGen.Phase.TypeCheck); fail(body); } catch( RuntimeException e ) { // Known null is rejected during parsing in these chapters. @@ -104,7 +104,7 @@ if( (arg/13)==0 ) { int r = g+h; return p-r; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -124,7 +124,7 @@ bar.a = 1; bar.a = 2; return bar.a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 2;", code.print()); } @@ -140,7 +140,7 @@ else v.y = 3; return v; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Vector2D;", code.print()); } @@ -178,7 +178,7 @@ while (arg) { } return bar.a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,0,(Phi_a+2));", code.print()); } @@ -191,7 +191,7 @@ if (arg) bar = null; bar.a = 1; return bar.a; """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Type null is not of declared type *Bar",e.getMessage()); } } @@ -204,7 +204,7 @@ if (arg) bar = new Bar; bar.a = 1; return bar.a; """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); } } @@ -230,7 +230,7 @@ if (arg) bar = null; if( bar ) bar.a = 1; return bar; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,null,Bar);", code.print()); } @@ -246,7 +246,7 @@ if( !bar ) rez=4; else bar.a = 1; return rez; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,4,3);", code.print()); } @@ -263,7 +263,7 @@ while(arg) { } return ret; """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Might be null accessing 'v0'", e.getMessage()); } } @@ -300,7 +300,7 @@ while( i.x < i.len ) { } return sum; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", code.print()); } @@ -319,7 +319,7 @@ while(arg) { } return ret; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print()); } @@ -337,7 +337,7 @@ while(arg) { } return ret; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print()); } @@ -354,7 +354,7 @@ while(arg < 10) { } return ret; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print()); } @@ -380,8 +380,9 @@ if(0) { v0=v2; } } +return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -395,7 +396,7 @@ if(0) return 0; else return new s0; if(new s0.f0) return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return s0;", code.print()); } @@ -411,7 +412,7 @@ while(0<arg) { } return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); } @Test @@ -423,7 +424,7 @@ while(v0.f0) {} s0 v1 = v0; return v1; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return (const)s0;", code.print()); } @@ -454,8 +455,9 @@ while(0) {} } } } } +return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -466,7 +468,7 @@ int v0=arg==0; while(v0) continue; return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -480,7 +482,7 @@ return 0; a.y = 55; b.y = 66; a.x = 33; b.x = 44; return before*100 + p.x; - """).parse().iter(); + """).parse().iter().finalizeIR(); StopNode stop = code._stop; assertEquals("1133",Eval2.eval(code,0)); assertEquals("2244",Eval2.eval(code,1)); @@ -506,7 +508,7 @@ return 0; """; @Test public void testMemoryAcrossNestedLoops() { - var code = new CodeGen(NESTED_MEMORY).parse().iter(); + var code = new CodeGen(NESTED_MEMORY).parse().iter().finalizeIR(); StopNode stop = code._stop; for (int arg=0; arg<7; arg++) { assertEquals("S{x="+(5L+arg)+",y="+(7L+arg*(arg+1)-(arg>=2 ? 2 : 0))+ @@ -522,7 +524,7 @@ return 0; if (arg) { s.x = 11; s.z = 13; return s; } s.y = 17; return s; - """).parse().iter(); + """).parse().iter().finalizeIR(); StopNode stop = code._stop; for (int arg=0; arg<2; arg++) { assertEquals(arg==0 ? "S{x=3,y=17,z=7}" : "S{x=11,y=5,z=13}",Eval2.eval(code,arg)); @@ -539,7 +541,7 @@ return 0; int v; if (arg>1) v=p.x; else v=q.x; return v; - """).parse().iter(); + """).parse().iter().finalizeIR(); StopNode stop = code._stop; // The one-step safety check stops at the memory aggregate. assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet())); @@ -554,7 +556,7 @@ return 0; !S !s = new S; if (arg) s.x=arg+1; else s.x=arg+2; return s; - """).parse().iter(); + """).parse().iter().finalizeIR(); StopNode stop = code._stop; // Keep each Store on its own branch. assertEquals(2,countMemoryNodes(stop,StoreNode.class,new BitSet())); @@ -573,7 +575,7 @@ return 0; if (arg>1) { v=p.x; p.x=41; p.y=5; } else { v=q.x; q.x=42; } return v; - """).parse().iter(); + """).parse().iter().finalizeIR(); StopNode stop = code._stop; assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet())); assertEquals("-1",Eval2.eval(code,-1)); diff --git a/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/src/test/java/com/seaofnodes/simple/Chapter11Test.java index a74f8540..a2a6aab4 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter11Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter11Test.java @@ -13,7 +13,7 @@ public class Chapter11Test { s.x = 42; while (arg > 0) { s.y = s.y + arg; arg = arg - 1; } return s.x; - """).parse().iter(); + """).parse().iter().finalizeIR(); var stop = code._stop; assertEquals("return 42;",stop.toString()); assertEquals("42",Eval2.eval(code,5)); @@ -32,7 +32,7 @@ public class Chapter11Test { arg = arg - 1; } return a; - """).parse().iter(); + """).parse().iter().finalizeIR(); var stop = code._stop; int[] phis = {0}; stop.walk(n -> { @@ -54,7 +54,7 @@ public class Chapter11Test { // it. Keep both the worklist assertion and the final heap check. for (int seed : new int[]{0,97,123,456}) { var code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse(); - code.iter(); + code.iter().finalizeIR(); assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4)); } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/src/test/java/com/seaofnodes/simple/Chapter12Test.java index 324b2a12..e680af8c 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter12Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter12Test.java @@ -14,7 +14,7 @@ public class Chapter12Test { """ return 3.14; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3.14;", code.print()); assertEquals("3.14", Eval2.eval(code, 0)); } @@ -34,7 +34,7 @@ while( arg ) { } return head.next.i; """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); } } @@ -56,7 +56,7 @@ LLI? next = head.next; if( next==null ) return 1; return next.i; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,0,1,.i);", code.print()); assertEquals("2", Eval2.eval(code, 3)); } @@ -75,7 +75,7 @@ i0.f = f0; f0.i = i0; return f0.i.f.i.i; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 17;", code.print()); } @@ -87,7 +87,7 @@ struct N { !N? !next; int i; }; N n = new N; return n.next; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return null;", code.print()); } @@ -100,7 +100,7 @@ struct N { !M !next; int i; }; N n = new N { next = new M; }; return n.next; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return (const)M;", code.print()); } @@ -113,7 +113,7 @@ struct N { !M !next; int i; }; N n = new N { next = null; } return n.next; """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Type null is not of declared type *M",e.getMessage()); } } @@ -126,21 +126,21 @@ struct N { !N? !next; int i; }; n.i = 3.14; return n.i; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("3",Eval2.eval(code,0)); } @Test public void testNullRef4() { - CodeGen code = new CodeGen("-null-5/null-5;"); - try { code.parse().iter().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); } + CodeGen code = new CodeGen("return -null-5/null-5;"); + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Cannot '+' null",e.getMessage()); } } @Test public void testNullRef5() { CodeGen code = new CodeGen("return null+42;"); - try { code.parse().iter().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } + catch( Exception e ) { assertEquals("Cannot '+' null",e.getMessage()); } } @Test @@ -148,8 +148,9 @@ return n.i; CodeGen code = new CodeGen( """ struct S{}; +return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); assertEquals("0", Eval2.eval(code, 0)); } @@ -161,7 +162,7 @@ struct S{}; struct S1 { !S2? !s; }; return new S2; """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); } } @@ -173,7 +174,7 @@ struct S1 { !S2? !s; }; struct S2 { int x; }; return new S1.s=new S2; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return S2;", code.print()); } @@ -190,7 +191,7 @@ p2.pi.i = 2; if (arg) p1 = new P; return p1.pi.i + 1; """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/src/test/java/com/seaofnodes/simple/Chapter13Test.java index 4e56b9de..4f699a3f 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter13Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter13Test.java @@ -325,7 +325,7 @@ while( prime <= arg ) { } return primeCount; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi_primeCount,(Phi_primeCount+1))));", code.print()); assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below assertEquals("1", Eval2.eval(code, 2)); // 2 @@ -347,7 +347,7 @@ i=v.f; if (arg) v.f=1; return i; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 2;", code.print()); assertEquals("2", Eval2.eval(code, 0)); assertEquals("2", Eval2.eval(code, 1)); @@ -370,7 +370,7 @@ if (arg) { } return i; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,.f,0);", code.print()); } @@ -389,7 +389,7 @@ if (v1) { } return v0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return S;", code.print()); } @@ -407,7 +407,7 @@ if (arg+1) arg= 0; while (arg) v.f = 2; return i; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return arg;", code.print()); } @@ -424,7 +424,7 @@ while(1) { } return v; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return S;", code.print()); } @@ -439,7 +439,7 @@ while( -arg ) while(1) arg = arg+ptr.v; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Top;", code.print()); } @@ -457,7 +457,7 @@ while (arg) { } return i; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -477,7 +477,7 @@ while(arg) { } return arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -495,7 +495,7 @@ if (arg) { } return v; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return S;", code.print()); } @@ -514,7 +514,7 @@ while (arg > 0) { } return v; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); //assertEquals("return new S;", code.print()); } @@ -531,7 +531,7 @@ while(0>=0) { } return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Top;", code.print()); } @@ -549,7 +549,7 @@ u32 v0=0; return v1; } """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java index cf7d3481..51397e77 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java +++ b/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java @@ -20,7 +20,7 @@ public class Chapter14FloatTest { {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"} }; for( String[] test : tests ) { - var code = new CodeGen(test[0]).parse().iter().typeCheck(); + var code = new CodeGen(test[0]).driver(CodeGen.Phase.TypeCheck); assertEquals(test[0],test[1],Eval2.eval(code,17)); assertEquals(test[0],test[2],Eval2.eval(code,-17)); } @@ -28,7 +28,7 @@ public class Chapter14FloatTest { @Test public void testSubZeroFloat() { - var code = new CodeGen("flt x = arg; return 0-x;").parse().iter().typeCheck(); + var code = new CodeGen("flt x = arg; return 0-x;").driver(CodeGen.Phase.TypeCheck); // Compare strings so +0.0 and -0.0 remain distinct. assertEquals("0.0", Eval2.eval(code,0)); assertEquals("-1.0", Eval2.eval(code,1)); @@ -42,7 +42,7 @@ public class Chapter14FloatTest { {"return 2.5; return 7;", "2.5"} } ) { String src = test[0]; - var code = new CodeGen(src).parse().iter().typeCheck(); + var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); assertEquals(src,test[1],Eval2.eval(code,0)); } } @@ -55,7 +55,7 @@ public class Chapter14FloatTest { """ return 3.14; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3.14;", code.print()); assertEquals("3.14", Eval2.eval(code, 0)); } @@ -66,7 +66,7 @@ return 3.14; """ return 3.14; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3.14;", code.print()); assertEquals("3.14", Eval2.eval(code, 0)); } @@ -83,7 +83,7 @@ while( 1 ) { } return guess; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)*0.5f));", code.print()); assertEquals("3.0", Eval2.eval(code, 9)); assertEquals("1.414213562373095", Eval2.eval(code, 2)); @@ -96,7 +96,7 @@ return guess; flt x = arg; return x+1==x; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print()); assertEquals("0", Eval2.eval(code, 1)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/src/test/java/com/seaofnodes/simple/Chapter14Test.java index ebc94d21..95a5ee81 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter14Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter14Test.java @@ -16,7 +16,7 @@ public class Chapter14Test { """ return 3.14; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3.14;", code.print()); assertEquals("3.14", Eval2.eval(code, 0)); } @@ -32,7 +32,7 @@ if( b < 0 ) c = -1; if( b > 2 ) c = 1; return c; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 99;", code.print()); assertEquals("99", Eval2.eval(code, 0)); } @@ -45,7 +45,7 @@ u8 b = 123; b = b + 456;// Truncate return b; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 67;", code.print()); assertEquals("67", Eval2.eval(code, 0)); } @@ -59,7 +59,7 @@ u8 b = 123; while( b ) b = b + 456;// Truncate return b; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -72,7 +72,7 @@ b = b + 456;// Truncate u1 c = b; // No more truncate needed return c; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 1;", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @@ -85,7 +85,7 @@ int b = 123; b = b+456 & 31; // Precedence return b; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3;", code.print()); assertEquals("3", Eval2.eval(code, 0)); } @@ -99,7 +99,7 @@ struct Foo { u1 b; }; f.b = 123; return f.b; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 1;", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @@ -111,7 +111,7 @@ return f.b; i8 b = 255; // Chopped return b; // Sign extend """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return -1;", code.print()); assertEquals("-1", Eval2.eval(code, 0)); } @@ -124,7 +124,7 @@ i8 b = arg; b = b + 1;// Truncate return b; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return (((((arg<<56)>>56)+1)<<56)>>56);", code.print()); assertEquals("1", Eval2.eval(code, 0)); assertEquals("-128", Eval2.eval(code, 127)); @@ -138,7 +138,7 @@ u16 mask = (1<<16)-1; // AND mask int c = 123456789 & mask; return c; // """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 52501;", code.print()); assertEquals("52501", Eval2.eval(code, 0)); } @@ -149,7 +149,7 @@ return c; // """ return (arg | 123 ^ 456) >>> 1; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return (((arg|123)^456)>>>1);", code.print()); assertEquals("217", Eval2.eval(code, 0)); } @@ -162,7 +162,7 @@ flt f = arg; arg = f & 1; return arg; """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Cannot '&' flt",e.getMessage()); } } @@ -175,8 +175,8 @@ while(arg) v1=-v0; while(v1) break; return v1; """); - code.parse().iter(); - assertEquals("return (Phi(Loop,((1<<arg)&1),0)&Phi(Loop,Shl,4294967295));", code.print()); + code.parse().iter().finalizeIR(); + assertEquals("return Phi(Loop,(((1<<arg)&1)&Shl),0);", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @@ -194,7 +194,7 @@ if(0&0>>>0) { } return v0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); assertEquals("0", Eval2.eval(code, 0)); } @@ -220,7 +220,7 @@ f32 ff32 = 3.141592653589793; if( ff32 != 3.1415927410125732) return 5; return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); assertEquals("0", Eval2.eval(code, 0)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/src/test/java/com/seaofnodes/simple/Chapter15Test.java index 4265aecc..c67d1eac 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter15Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter15Test.java @@ -75,7 +75,7 @@ public class Chapter15Test { """ return 3.14; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3.14;", code.print()); assertEquals("3.14", Eval2.eval(code, 0)); } @@ -89,7 +89,7 @@ struct C { !C? !l; }; c.l = c; return c; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return C;", code.print()); assertEquals("C{l=$cyclic}", Eval2.eval(code, 0)); } @@ -108,7 +108,7 @@ while (i < old#) { output[i] = 1; return output; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return [u8];", code.print()); assertEquals("\u0001", Eval2.eval(code, 0)); } @@ -120,7 +120,7 @@ return output; int[] is = new int[2]; return is[1]; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); assertEquals("0", Eval2.eval(code, 0)); } @@ -133,7 +133,7 @@ int[] is = new int[2]; int[] is2 = new int[2]; return is[1]; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); assertEquals("0", Eval2.eval(code, 0)); } @@ -147,7 +147,7 @@ a[0] = 1; a[1] = 2; return a[0]; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 1;", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @@ -160,7 +160,7 @@ struct A { int i; }; !A?[] !a = new !A?[2]; return a; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return [*A?];", code.print()); assertEquals("*A {int !i; }?[ null,null]", Eval2.eval(code, 0)); } @@ -189,7 +189,7 @@ else { } return rez; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,1.2,.y,2.3);", code.print()); assertEquals("3.14", Eval2.eval(code, 0)); assertEquals("1.2", Eval2.eval(code, 1)); @@ -214,7 +214,7 @@ if( iss[arg] ) if( iss[arg][2] ) rez = iss[arg][2].y; return rez;"""); - code.parse().iter().typeCheck(); + code.driver(CodeGen.Phase.TypeCheck); assertEquals("return Phi(Region,1.2,1.2,.y);", code.print()); assertEquals("3.14", Eval2.eval(code, 0)); try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks @@ -232,7 +232,7 @@ root._kids = new !Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES???? root._kids[0] = new Tree; return root; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Tree;", code.print()); assertEquals("Tree{_kids=*Tree?[ Tree{_kids=null},null]}", Eval2.eval(code, 0)); } @@ -244,7 +244,7 @@ return root; struct S { int a; int[] !b; }; return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -267,7 +267,7 @@ while( i < ary# - 1 ) { } return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return (.[]+(.[]*1000));", code.print()); assertEquals("1006", Eval2.eval(code, 4)); } @@ -276,7 +276,7 @@ return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 public void sieveOEratosthenes() throws IOException { String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp")); CodeGen code = new CodeGen(src+"return sieve(arg);"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return [u32];", code.print()); assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); } @@ -293,7 +293,7 @@ s2.f = 2.0; if (arg) s1 = new S; return s1.i + s1.f; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return ((flt).i+.f);", code.print()); assertEquals("0.0", Eval2.eval(code, 0)); } @@ -305,7 +305,7 @@ return s1.i + s1.f; """ return new flt; """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); } } @@ -313,10 +313,10 @@ return new flt; public void testBad1() { CodeGen code = new CodeGen( """ -int is = new int[2]; +flt is = new int[2]; """); - try { code.parse().iter(); fail(); } - catch( Exception e ) { assertEquals("Type *[int] is not of declared type int",e.getMessage()); } + try { code.parse().iter().finalizeIR(); fail(); } + catch( Exception e ) { assertEquals("Type *[int] is not of declared type flt",e.getMessage()); } } @Test @@ -326,7 +326,7 @@ int is = new int[2]; int[] is = new int[3.14]; return is[1]; """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Cannot allocate an array with length 3.14",e.getMessage()); } } @@ -336,7 +336,7 @@ return is[1]; int[] is = new int[arg]; return is[1]; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); assertEquals("0", Eval2.eval(code, 4)); try { Eval2.eval(code,-1); fail(); } @@ -369,7 +369,7 @@ while(v5+(0&0)) { } return v1; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); assertEquals("0", Eval2.eval(code, 0)); } @@ -383,8 +383,9 @@ while(0) {} u32 v7=0; int v8=0; while(0<--1>>>---(v7*0==v8)) {} +return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } @@ -407,8 +408,9 @@ else { } } } +return 0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/src/test/java/com/seaofnodes/simple/Chapter16Test.java index 78e468cd..27766f20 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter16Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter16Test.java @@ -17,7 +17,7 @@ public class Chapter16Test { """ return 3.14; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3.14;", code.print()); assertEquals("3.14", Eval2.eval(code, 0)); } @@ -30,7 +30,7 @@ return 3.14; int x, y; return x+y; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); assertEquals("0", Eval2.eval(code, 0)); } @@ -41,7 +41,7 @@ return x+y; int x=2, y=x+1; return x+y; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 5;", code.print()); assertEquals("5", Eval2.eval(code, 0)); } @@ -54,7 +54,7 @@ struct X { int x=3; }; X z = new X; return z.x; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3;", code.print()); assertEquals("3", Eval2.eval(code, 0)); } @@ -66,7 +66,7 @@ struct X { int x=3; }; X z = new X { x = 4; }; return z.x; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 4;", code.print()); assertEquals("4", Eval2.eval(code, 0)); } @@ -87,7 +87,7 @@ LLI? next = head.next; if( !next ) return 1; return next.i; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,0,1,.i);", code.print()); assertEquals("0", Eval2.eval(code, 0)); assertEquals("1", Eval2.eval(code, 1)); @@ -118,7 +118,7 @@ LLI? next = head.next; if( !next ) return 1; return next.i; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,0,1,.i);", code.print()); assertEquals("0", Eval2.eval(code, 0)); assertEquals("1", Eval2.eval(code, 1)); @@ -143,7 +143,7 @@ struct Square { }; return new Square; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Square;", code.print()); assertEquals("Square{side=3.0,diag=1.7320508075688772}", Eval2.eval(code, 3)); assertEquals("Square{side=4.0,diag=2.0}", Eval2.eval(code, 4)); @@ -167,7 +167,7 @@ return new Square; """; @Test public void testConstructorMemory() { - var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().iter(); + var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().iter().finalizeIR(); assertEquals("111249",Eval2.eval(code,0)); assertEquals("222352",Eval2.eval(code,1)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/src/test/java/com/seaofnodes/simple/Chapter17aTest.java index 4a69c33d..b8a25016 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter17aTest.java +++ b/src/test/java/com/seaofnodes/simple/Chapter17aTest.java @@ -17,7 +17,7 @@ int x=2, y=3; if( arg ) { int x = y; x = x*x; y=x; } // Shadow final x return y; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,9,3);", code.print()); assertEquals("3", Eval2.eval(code, 0)); assertEquals("9", Eval2.eval(code, 1)); @@ -30,7 +30,7 @@ struct X { int ~x; }; X z = new X { x=3; }; return z.x; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 3;", code.print()); assertEquals("3", Eval2.eval(code, 0)); } @@ -42,7 +42,7 @@ struct Point { int ~x, ~y; }; Point p = new Point { x=3; y=4; }; return p; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return (const)Point;", code.print()); assertEquals("Point{x=3,y=4}", Eval2.eval(code, 0)); } @@ -55,7 +55,7 @@ Point p = new Point { x=5; y=6; }; p.x++; return p; """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } } @@ -67,7 +67,7 @@ Point p = new Point; p.x++; return p; """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } } @@ -75,7 +75,7 @@ return p; public void testStructFinal3() { for( String fields : new String[] { "var x; var y;", "int[] !x;", "int[] !x;" } ) { String src = "struct Point { "+fields+" }; Point p = new Point; p.x++; return p;"; - try { new CodeGen(src).parse().iter(); fail(fields); } + try { new CodeGen(src).parse().iter().finalizeIR(); fail(fields); } catch( Exception e ) { assertEquals("'Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); } } } @@ -88,7 +88,7 @@ Point p = new Point; p.x++; return p; """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } } @@ -100,7 +100,7 @@ struct Point { var x=3; var y=4; }; p.x++; return p; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Point;", code.print()); assertEquals("Point{x=4,y=4}", Eval2.eval(code, 0)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java index a761a31b..9185aec8 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java +++ b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java @@ -40,7 +40,7 @@ public class Chapter17bTest { """ return 1; """); - code.parse().iter().typeCheck(); + code.driver(CodeGen.Phase.TypeCheck); assertEquals("return 1;", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @@ -52,7 +52,7 @@ return 1; CodeGen code = new CodeGen(""" return arg++; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return arg;", code.print()); assertEquals("0", Eval2.eval(code, 0)); } @@ -62,7 +62,7 @@ return arg++; CodeGen code = new CodeGen(""" return arg+++arg++; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return ((arg<<1)+1);", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @@ -73,7 +73,7 @@ return arg+++arg++; // -(arg--)-(arg--) return -arg---arg--; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return (-((arg<<1)+-1));", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @@ -86,7 +86,7 @@ xs[0]++; xs[1]++; return xs[0]; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 1;", code.print()); assertEquals("1", Eval2.eval(code, 2)); } @@ -98,7 +98,7 @@ u8[] !xs = new u8[1]; xs[0]--; return xs[0]; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 255;", code.print()); assertEquals("255", Eval2.eval(code, 2)); } @@ -111,7 +111,7 @@ struct S { u16 x; }; s.x--; return s.x; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 65535;", code.print()); assertEquals("65535", Eval2.eval(code, 2)); } @@ -119,7 +119,7 @@ return s.x; @Test public void testInc6() { CodeGen code = new CodeGen("return --arg;"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return (arg+-1);", code.print()); assertEquals("-1", Eval2.eval(code, 0)); } @@ -127,7 +127,7 @@ return s.x; @Test public void testInc7() { CodeGen code = new CodeGen("u8 x=0; return --x;"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 255;", code.print()); assertEquals("255", Eval2.eval(code, 0)); } @@ -135,7 +135,7 @@ return s.x; @Test public void testInc8() { CodeGen code = new CodeGen("int x; x+=2; return x+=3;"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 5;", code.print()); assertEquals("5", Eval2.eval(code, 0)); } @@ -143,14 +143,14 @@ return s.x; @Test public void testInc9() { CodeGen code = new CodeGen("flt x = 1.5; x++; return x;"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 2.5f;", code.print()); assertEquals("2.5", Eval2.eval(code, 0)); } @Test public void testInt10() { CodeGen code = new CodeGen("flt x = 1.5; x++; int i = x; return i;"); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("2",Eval2.eval(code,0)); } @@ -158,73 +158,73 @@ return s.x; // --------------------------------------------------------------- @Test public void testVar0() { CodeGen code = new CodeGen("var d; return d;"); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); } } @Test public void testVar1() { CodeGen code = new CodeGen("val d; return d;"); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); } } @Test public void testVar2() { CodeGen code = new CodeGen("int x; x=3; x++; return x; // Ok, no initializer so x is mutable "); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 4;", code.print()); assertEquals("4", Eval2.eval(code, 0)); } @Test public void testVar3() { CodeGen code = new CodeGen("int x=3; x++; return x; // Ok, primitive so x is mutable despite initializer "); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 4;", code.print()); assertEquals("4", Eval2.eval(code, 0)); } @Test public void testVar4() { CodeGen code = new CodeGen("struct S{int x;}; !S? !s; s=new S; s.x++; return s.x; // Ok, no initializer so x is mutable "); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 1;", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @Test public void testVar5() { CodeGen code = new CodeGen("struct S{int x;}; !S? !s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable "); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 4;", code.print()); assertEquals("4", Eval2.eval(code, 0)); } @Test public void testVar6() { CodeGen code = new CodeGen("struct S{int x;}; S s=new S; s.x++; return s.x; // Error initializer so x is immutable "); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } } @Test public void testVar7() { CodeGen code = new CodeGen("struct S{int x;}; S s=new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable "); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } } @Test public void testVar8() { CodeGen code = new CodeGen("struct S{int x;}; !S !s=new S; s.x++; return s.x; // Ok, has '!' so s.x is mutable "); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 1;", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @Test public void testVar9() { CodeGen code = new CodeGen("struct S{int x;}; var s=new S; s.x++; return s.x; // Ok, has var so s.x is mutable "); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 1;", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @Test public void testVar10() { CodeGen code = new CodeGen("struct S{int x;}; ~S s=new S; s.x++; return s.x; // Error, has val so x is immutable "); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } } @@ -242,7 +242,7 @@ foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable ~Foo xfoo = foo; // Throw away mutability xfoo.bar.x++; // Error, cannot mutate through xfoo """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } } @@ -263,7 +263,7 @@ foo.bar.x++; // Bumps to 5 int x5 = xfoo.bar.x; // Ok to read through xfoo, gets 5 return x4*10+x5; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 45;", code.print()); assertEquals("45", Eval2.eval(code, 0)); } @@ -273,7 +273,7 @@ return x4*10+x5; CodeGen code = new CodeGen(""" int i,i++; """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); } } @@ -288,7 +288,7 @@ A x = new A { b = new B; }; """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Cannot read uninitialized field 'b'",e.getMessage()); } } @@ -301,7 +301,7 @@ return new A { if (arg) b = new B; // Constructor ends with partial init of b }.b; """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("'A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); } } @@ -315,7 +315,7 @@ val s = new S{x = 2;}; return x; """ ); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } } @@ -334,7 +334,7 @@ focus(me); return me; """ ); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); } } @@ -344,7 +344,7 @@ return me; CodeGen code = new CodeGen(""" return arg ? 1 : 2; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,1,2);", code.print()); assertEquals("2", Eval2.eval(code, 0)); assertEquals("1", Eval2.eval(code, 1)); @@ -355,7 +355,7 @@ return arg ? 1 : 2; CodeGen code = new CodeGen(""" return arg ? 0 : arg; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 0;", code.print()); assertEquals("0", Eval2.eval(code, 0)); } @@ -367,7 +367,7 @@ struct Bar { int x; }; var b = arg ? new Bar : null; return b ? b.x++ + b.x++ : -1; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); assertEquals("-1", Eval2.eval(code, 0)); assertEquals("1", Eval2.eval(code, 1)); @@ -380,7 +380,7 @@ struct Bar { int x; }; var b = arg ? new Bar; return b ? b.x++ + b.x++ : -1; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); assertEquals("-1", Eval2.eval(code, 0)); assertEquals("1", Eval2.eval(code, 1)); @@ -394,7 +394,7 @@ struct Bar { !Bar? !next; int x; }; var b = arg ? new Bar { next = (arg==2) ? new Bar{x=2;}; x=1; }; return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0" """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,.x,0,.x);", code.print()); assertEquals("0", Eval2.eval(code, 0)); assertEquals("1", Eval2.eval(code, 1)); @@ -407,7 +407,7 @@ return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0 flt f=arg?1:1.2; return f; // missing widening """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Region,1.0f,1.2);", code.print()); assertEquals("1.2", Eval2.eval(code, 0)); } @@ -419,7 +419,7 @@ return f; // missing widening struct S{}; return arg ? 7 : new S; """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("No common type amongst int and reference",e.getMessage()); } } @@ -432,7 +432,7 @@ for( int i=0; i<arg; i++ ) sum += i; return sum; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); assertEquals("3", Eval2.eval(code, 3)); assertEquals("45", Eval2.eval(code, 10)); @@ -445,7 +445,7 @@ for( ; i<arg; i++ ) sum += i; return sum; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); assertEquals("3", Eval2.eval(code, 3)); assertEquals("45", Eval2.eval(code, 10)); @@ -460,7 +460,7 @@ for( int i=0; ; i++ ) { } return sum; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); assertEquals("3", Eval2.eval(code, 3)); assertEquals("45", Eval2.eval(code, 10)); @@ -473,7 +473,7 @@ for( int i=0; i<arg; ) sum += i++; return sum; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print()); assertEquals("3", Eval2.eval(code, 3)); assertEquals("45", Eval2.eval(code, 10)); @@ -486,7 +486,7 @@ for( int i=0; i<arg; i++ ) sum += i; return i; """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); } } @Test @@ -494,7 +494,7 @@ return i; CodeGen code = new CodeGen(""" for(;;arg++;) {} """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Syntax error, expected Unexpected code after expression: ;",e.getMessage()); } } @@ -510,7 +510,7 @@ struct A{ }; return new A; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return A;", code.print()); assertEquals("A{f1=null,f2=null}", Eval2.eval(code,0)); } @@ -524,7 +524,7 @@ struct A{ }; return new A.not_array_of_b; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return [*B?];", code.print()); assertEquals("*B?[]", Eval2.eval(code,0)); } @@ -543,7 +543,7 @@ for( ; ptr; ptr = ptr.next ) sum += ptr.i; return sum; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print()); assertEquals("45", Eval2.eval(code, 10)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java index 6282f26c..e310366d 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -91,7 +91,7 @@ public class Chapter18Test { "No common type amongst int and reference"} } ) { try { - new CodeGen(test[0]).parse().iter().typeCheck(); + new CodeGen(test[0]).driver(CodeGen.Phase.TypeCheck); fail("Expected incompatible return types: " + test[0]); } catch( Parser.ParseException e ) { assertEquals(test[1],e.getMessage()); @@ -140,7 +140,7 @@ while(arg--) { } return a; """); - code.driver(CodeGen.Phase.Schedule); + code.driver(CodeGen.Phase.TypeCheck); assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString()); assertEquals("1", Eval2.eval(code, 0)); assertEquals("2", Eval2.eval(code, 1)); @@ -157,7 +157,7 @@ return a; {int -> int}? x2 = null; // null function ptr return x2; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return null;", code._stop.toString()); assertEquals("null", Eval2.eval(code, 0 ) ); } @@ -170,7 +170,7 @@ return x2; x*x; }; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return (Parm_x(sq,int)*x);", code._stop.toString()); //assertEquals("{ int -> int #1}", Eval2.eval(code, 3)); } @@ -185,7 +185,7 @@ var sq = { int x -> return sq(arg)+sq(3); """); code.driver(Phase.LocalSched); - assertEquals("Stop[ return (#2+#2); return (Parm_x(sq,int)*x); ]", code._stop.toString()); + assertEquals("Stop[ return (Parm_x(sq,int)*x); return (#2+#2); ]", code._stop.toString()); assertEquals("13", Eval2.eval(code, 2)); } @@ -197,7 +197,7 @@ return sq(arg)+sq(3); int cnt=1; return { -> cnt; }; """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); } } @@ -209,7 +209,7 @@ return { -> cnt; }; val cnt=2; return { -> cnt; }(); """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("return 2;", code._stop.toString()); assertEquals("2", Eval2.eval(code, 0)); } @@ -222,8 +222,8 @@ return { -> cnt; }(); var fcn = arg ? { int x -> x*x; } : { int x -> x+x; }; return fcn(3); """); - code.parse().iter(); - assertEquals("Stop[ return #2; return (Parm_x($fun1,int,3)*x); return (Parm_x($fun2,int,3)<<1); ]", code._stop.toString()); + code.parse().iter().finalizeIR(); + assertEquals("Stop[ return (Parm_x($fun2,int,3)<<1); return (Parm_x($fun1,int,3)*x); return #2; ]", code._stop.toString()); assertEquals("6", Eval2.eval(code, 0)); assertEquals("9", Eval2.eval(code, 1)); } @@ -232,8 +232,8 @@ return fcn(3); @Test public void testFcn5() { CodeGen code = new CodeGen("val fact = { int x -> x <= 1 ? 1 : x*fact(x-1); }; return fact(arg);"); - code.parse().iter().typeCheck(); - assertEquals("Stop[ return #2; return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*#2)); ]", code._stop.toString()); + code.driver(CodeGen.Phase.TypeCheck); + assertEquals("Stop[ return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*#2)); return #2; ]", code._stop.toString()); assertEquals( "1", Eval2.eval(code, 0)); assertEquals( "1", Eval2.eval(code, 1)); assertEquals( "2", Eval2.eval(code, 2)); @@ -265,7 +265,7 @@ val g = {->2;}; return 2; """); code.driver(CodeGen.Phase.Schedule); - assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString()); + assertEquals("Stop[ return 2; return 1; return 1; ]", code._stop.toString()); assertEquals("1", Eval2.eval(code, 0)); } @@ -283,7 +283,7 @@ for(;;) { } """); code.driver(Phase.LocalSched); - assertEquals("Stop[ return #2; return Parm_i(x,int); ]", code._stop.toString()); + assertEquals("Stop[ return Parm_i(x,int); return #2; ]", code._stop.toString()); assertEquals("3", Eval2.eval(code, 0)); } @@ -321,7 +321,7 @@ val fcn = { !Person?[] !ps, int x -> var ps = new !Person?[2]; ps[0] = new Person; ps[1] = new Person; -fcn(ps,1); +return fcn(ps,1); """); code.driver(Phase.LocalSched); assertEquals("return 0;", code._stop.toString()); @@ -342,7 +342,7 @@ for(;;) { } return 1; """); - try { code.parse().iter(); fail(); } + try { code.parse().iter().finalizeIR(); fail(); } catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); } } @@ -356,8 +356,9 @@ for(;;) { if (i2i(2) == arg) break; i2i = null; } +return 0; """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Might be null calling { int -> int #1}?",e.getMessage()); } } @@ -369,7 +370,7 @@ for(;;) { val f = { int i, int j -> return i+j; }; return f(); """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Expecting 2 arguments, but found 0",e.getMessage()); } } @@ -387,7 +388,7 @@ for(;;) { if (s) return s.x; } """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*S'",e.getMessage()); } } @@ -400,7 +401,7 @@ val g = { -> }; return 0; """); - try { code.parse().iter().typeCheck(); fail(); } + try { code.driver(CodeGen.Phase.TypeCheck); fail(); } catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); } } @@ -414,7 +415,7 @@ val is_even = { int x -> x ? is_odd (x-1) : true ; }; val is_odd = { int x -> x ? is_even(x-1) : false; }; return is_even(arg); """); - code.parse().iter(); + code.parse().iter().finalizeIR(); assertEquals("Stop[ return is_even( arg); return Phi(Region,Phi(Region,is_even( ((Parm_x(is_even,int,arg,Sub)-1)-1)),0),1); ]", code._stop.toString()); assertEquals("1", Eval2.eval(code, 0)); assertEquals("0", Eval2.eval(code, 1)); @@ -454,7 +455,7 @@ if (i2i) return i2i(arg); return f2f(o)(1); """); code.driver(Phase.LocalSched); - assertEquals("Stop[ return Phi(Region,#2,1); return Parm_i(o,int); ]", code._stop.toString()); + assertEquals("Stop[ return Parm_i(i2i.o,int); return Phi(Region,#2,1); ]", code._stop.toString()); assertEquals("1", Eval2.eval(code, 2)); } @@ -488,7 +489,7 @@ return p.coffee_count; """; @Test public void testCallMemory() { - var code = new CodeGen(CALL_MEMORY).parse().iter(); + var code = new CodeGen(CALL_MEMORY).parse().iter().finalizeIR(); int[] calls={0}; code._stop.walk(n -> { if(n instanceof CallNode) calls[0]++; return null; }); assertEquals(2,calls[0]); // Keep real calls so this checks scheduling across them. @@ -517,7 +518,7 @@ return p.coffee_count; """; @Test public void testRecursiveMemory() { - var code = new CodeGen(RECURSIVE_MEMORY).parse().iter(); + var code = new CodeGen(RECURSIVE_MEMORY).parse().iter().finalizeIR(); for(int n=0; n<7; n++) assertEquals(Long.toString((10L*(n+1)+n*(n+1)/2)*100+10+n),Eval2.eval(code,n)); } @@ -535,7 +536,7 @@ return p.coffee_count; """; @Test public void testInlineAllocationMemory() { - var code = new CodeGen(INLINE_MEMORY).parse().iter(); + var code = new CodeGen(INLINE_MEMORY).parse().iter().finalizeIR(); assertNull(code._stop.walk(n -> n instanceof CallNode ? n : null)); assertEquals("400944",Eval2.eval(code,0)); assertEquals("420946",Eval2.eval(code,2)); diff --git a/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/src/test/java/com/seaofnodes/simple/Chapter19Test.java index 2027dbd6..ac141247 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter19Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java @@ -22,7 +22,7 @@ public class Chapter19Test { con.toString(); org.junit.Assert.assertNull(call.name()); - var fun = new FunNode(null,tfp); + var fun = new FunNode(null,tfp,"printerTarget"); fun._name = "printerTarget"; fun.addDef(code._start); code.link(fun); @@ -39,7 +39,7 @@ public class Chapter19Test { CodeGen code = new CodeGen(""" return 0; """); - code.parse().iter().typeCheck(); + code.driver(CodeGen.Phase.TypeCheck); assertEquals("return 0;", code._stop.toString()); assertEquals("0", Eval2.eval(code, 2)); } @@ -295,6 +295,7 @@ for( int i=0; i<A#; i++ ) A[i] = i; for( int i=0; i<A#; i++ ) B[i] += A[i]; +return 0; """); code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return 0;", code.print()); @@ -339,7 +340,7 @@ val fcn = arg ? { int x -> x*x; } : { int x -> x+x; }; return fcn(2)*10 + fcn(3); """); code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); - assertEquals("Stop[ return (add,#2,(muli,#2)); return (mul,Parm_x($fun1,int),x); return (shli,Parm_x($fun2,int)); ]", code.print()); + assertEquals("Stop[ return (shli,Parm_x($fun2,int)); return (mul,Parm_x($fun1,int),x); return (add,#2,(muli,#2)); ]", code.print()); } @Test @@ -350,16 +351,16 @@ val sq = { int x -> x*x; }; return sq(arg) + sq(3); """); code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); - assertEquals("Stop[ return (add,#2,#2); return (mul,Parm_x(sq,int),x); ]", code.print()); + assertEquals("Stop[ return (mul,Parm_x(sq,int),x); return (add,#2,#2); ]", code.print()); } @Test public void testSelectedMemory() { for( String cpu : new String[]{"x86_64_v2","riscv","arm"} ) for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY, Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY} ) { - var code = new CodeGen(src).parse().iter().typeCheck(); + var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); var aliases = memoryPhis(code._stop); - code.loopTree().driver(CodeGen.Phase.InstSelect,cpu,"SystemV"); + code.loopTree().driver(CodeGen.Phase.Select,cpu,"SystemV"); assertEquals(aliases,memoryPhis(code._stop)); code.driver(CodeGen.Phase.Schedule).localSched(); code._stop.walk(n -> { diff --git a/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/src/test/java/com/seaofnodes/simple/Chapter20Test.java index 93d8caae..3b1ca4ff 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter20Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter20Test.java @@ -1,5 +1,7 @@ package com.seaofnodes.simple; +import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen; + import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.print.ASMPrinter; @@ -73,13 +75,14 @@ return f(s); // The entry of g can disappear before its Return is folded into f. // Exercise both worklist orders; deleting the entry does not kill 123. for( int seed=0; seed<64; seed++ ) { - CodeGen code = new CodeGen(src,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse().iter().typeCheck(); + CodeGen code = new CodeGen(src,com.seaofnodes.simple.type.TypeInteger.BOT,seed).driver(CodeGen.Phase.TypeCheck); assertEquals("seed "+seed, "123", Eval2.eval(code,0)); } } @org.junit.Rule public final org.junit.rules.ErrorCollector _errors = new org.junit.rules.ErrorCollector(); + @Test public void testAllocatorMasks() { com.seaofnodes.simple.codegen.RegAllocTestSupport.masks(); } @Test public void testAllocatorUnion() { com.seaofnodes.simple.codegen.RegAllocTestSupport.union(); } @Test public void testAllocatorCopyClobber() throws Exception { com.seaofnodes.simple.codegen.RegAllocTestSupport.copyClobber(); } @@ -97,7 +100,7 @@ return f(s); @Test public void testJig() { CodeGen code = new CodeGen("return 0;"); - code.parse().iter().typeCheck(); + code.driver(CodeGen.Phase.TypeCheck); assertEquals("return 0;", code._stop.toString()); assertEquals("0", Eval2.eval(code, 2)); } @@ -108,9 +111,8 @@ return f(s); } static void testCPU(String src, String cpu, String os, int spills, String stop) { - CodeGen code = new CodeGen(src); + CodeGen code = new CheckedCodeGen(src); code.driver(CodeGen.Phase.RegAlloc,cpu,os); - com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code); SpillStats.record(code,"Chapter20",cpu,os); SpillStats.checkSpills(spills,code._regAlloc._spillScaled); if( stop!=null ) assertEquals(stop,code._stop.toString()); @@ -150,7 +152,7 @@ val sqrt = { int x -> }; return sqrt(arg) + sqrt(arg+2); """; - testTarget(src,"x86_64_v2", "SystemV",23,null); + testTarget(src,"x86_64_v2", "SystemV",40,null); testTarget(src,"riscv" , "SystemV",17,null); testTarget(src,"arm" , "SystemV",18,null); } @@ -179,9 +181,9 @@ return sqrt(farg) + sqrt(farg+2.0); @Test public void testAlloc2() { String src = "int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];"; - testTarget(src,"x86_64_v2","SystemV",4,"return .[];"); - testTarget(src,"riscv","SystemV",8,"return .[];"); - testTarget(src,"arm","SystemV",9,"return .[];"); + testTarget(src,"x86_64_v2","SystemV",3,"return .[];"); + testTarget(src,"riscv","SystemV",6,"return .[];"); + testTarget(src,"arm","SystemV",6,"return .[];"); } @Test @@ -197,7 +199,7 @@ for( int i=0; i<ary#-1; i++ ) ary[i+1] += ary[i]; return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 """; - testTarget(src,"x86_64_v2", "SystemV",7,"return .[];"); + testTarget(src,"x86_64_v2", "SystemV",5,"return .[];"); testTarget(src,"riscv" , "SystemV",7,"return (add,.[],(mul,.[],1000));"); testTarget(src,"arm" , "SystemV",5,"return (add,.[],(mul,.[],1000));"); } @@ -240,9 +242,14 @@ s.cs[0] = 67; // C s.cs[1] = 108; // l hashCode(s); """; - testTarget(src,"x86_64_v2", "SystemV",15,null); - testTarget(src,"riscv" , "SystemV", 10,null); - testTarget(src,"arm" , "SystemV", 10,null); + // Without an explicit return, default main must be removed completely. + // Seed 0 previously left a loop with a missing branch successor. + for( String cpu : new String[]{"x86_64_v2","riscv","arm"} ) + new CodeGen(src,com.seaofnodes.simple.type.TypeInteger.BOT,0) + .driver(CodeGen.Phase.Encoding,cpu,"SystemV"); + testTarget(src,"x86_64_v2", "SystemV",0,null); + testTarget(src,"riscv" , "SystemV",1,null); + testTarget(src,"arm" , "SystemV",0,null); } @Test @@ -343,8 +350,8 @@ for( int pc = 0; pc < program#; pc++ ) { return output; """; testTarget(src,"x86_64_v2", "SystemV",40,null); - testTarget(src,"riscv" , "SystemV",42,null); - testTarget(src,"arm" , "SystemV",34,null); + testTarget(src,"riscv" , "SystemV",28,null); + testTarget(src,"arm" , "SystemV",28,null); //assertEquals("Hello World!\n", Eval2.eval(code, 0, 10000)); } // Original Chapter 20 allocation workload, kept fixed for cohort comparisons. diff --git a/src/test/java/com/seaofnodes/simple/Chapter21AllocTest.java b/src/test/java/com/seaofnodes/simple/Chapter21AllocTest.java index bc300217..b81483d8 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter21AllocTest.java +++ b/src/test/java/com/seaofnodes/simple/Chapter21AllocTest.java @@ -21,7 +21,7 @@ val sqrt = { int x -> }; return sqrt(arg) + sqrt(arg+2); """; - Chapter21Test.testCPU(src,"x86_64_v2", "Win64" ,23,null); + Chapter21Test.testCPU(src,"x86_64_v2", "Win64" ,40,null); Chapter21Test.testCPU(src,"riscv" , "SystemV",19,null); Chapter21Test.testCPU(src,"arm" , "SystemV",19,null); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/src/test/java/com/seaofnodes/simple/Chapter21Test.java index b19d1bb5..4bce96ab 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -4,6 +4,7 @@ import com.seaofnodes.isa.eval.EvalRisc5; import com.seaofnodes.isa.eval.EvalArm64; +import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.cpus.arm.arm; import com.seaofnodes.simple.node.cpus.riscv.riscv; @@ -27,7 +28,7 @@ public class Chapter21Test { else if(i==4) b.x=40; } return sum; - """).parse().iter(); + """).parse().iter().finalizeIR(); // The i==2 read still sees 10: three reads of 10, then three of 30. assertEquals("120",Eval2.eval(code,0)); assertEquals("60",Eval2.eval(code,1)); @@ -103,39 +104,6 @@ public class Chapter21Test { } - @Test public void testNarrowStores() throws IOException { - for( String type : new String[]{"i8","u8","i16","u16"} ) { - String src = "struct S { "+type+" x; }; !S !s = new S; s.x = arg; return 0;"; - CodeGen code = new CodeGen(src).driver("riscv","SystemV",null); - int stores=0; - for( var bb : code._cfg ) - for( var node : bb.outs() ) - if( node instanceof com.seaofnodes.simple.node.cpus.riscv.StoreRISC st ) { - stores++; - assertEquals("Byte/short stores have no FP encoding",-1,st.regmap(4).nextReg((short)31)); - } - assertEquals(1,stores); - byte[] image = new byte[1<<20]; - byte[] bits = code._encoding.bits(); - System.arraycopy(bits,0,image,0,bits.length); - EvalRisc5 cpu = new EvalRisc5(image,1<<16); - cpu.regs[riscv.A0] = 0x8765; - assertEquals(0,cpu.step(100)); - assertEquals(type.endsWith("8") ? 0x65 : 0x8765, - type.endsWith("8") ? cpu.ld1z(1<<16) : cpu.ld2z(1<<16)); - code = new CodeGen(src).driver(CodeGen.Phase.Encoding,"x86_64_v2",TestC.CALL_CONVENTION); - stores=0; - for( var bb : code._cfg ) - for( var node : bb.outs() ) - if( node instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.StoreX86 st ) { - stores++; - assertEquals("Byte/short stores have no XMM encoding",-1,st.regmap(4).nextReg((short)15)); - } - assertEquals(1,stores); - } - } - - @Test public void testArmCallInstructions() { EvalArm64 cpu = new EvalArm64(new byte[512],256); for( int delta : new int[]{4,32,-4} ) { @@ -238,6 +206,52 @@ public class Chapter21Test { } + @Test public void testNarrowStores() throws IOException { + for( String type : new String[]{"i8","u8","i16","u16"} ) { + String src = "struct S { "+type+" x; }; !S !s = new S; s.x = arg; return 0;"; + CodeGen code = new CodeGen(src).driver("riscv","SystemV",null); + int stores=0; + for( var bb : code._cfg ) + for( var node : bb.outs() ) + if( node instanceof com.seaofnodes.simple.node.cpus.riscv.StoreRISC st ) { + stores++; + assertEquals("Byte/short stores have no FP encoding",-1,st.regmap(4).nextReg((short)31)); + } + assertEquals(1,stores); + byte[] image = new byte[1<<20]; + byte[] bits = code._encoding.bits(); + System.arraycopy(bits,0,image,0,bits.length); + EvalRisc5 cpu = new EvalRisc5(image,1<<16); + cpu.regs[riscv.A0] = 0x8765; + assertEquals(0,cpu.step(100)); + assertEquals(type.endsWith("8") ? 0x65 : 0x8765, + type.endsWith("8") ? cpu.ld1z(1<<16) : cpu.ld2z(1<<16)); + code = new CodeGen(src).driver(CodeGen.Phase.Encoding,"x86_64_v2",TestC.CALL_CONVENTION); + stores=0; + for( var bb : code._cfg ) + for( var node : bb.outs() ) + if( node instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.StoreX86 st ) { + stores++; + assertEquals("Byte/short stores have no XMM encoding",-1,st.regmap(4).nextReg((short)15)); + } + assertEquals(1,stores); + } + } + + @Test public void testNativeExitStatus() throws IOException { + Path source = Path.of("build/objs/nativeExit.c"); + Files.createDirectories(source.getParent()); + Files.writeString(source,"int main() { return 7; }\n"); + try { + TestC.gcc(source.toString(),null,null,false, + "build/objs/nativeExit"+(TestC.OS.startsWith("Windows") ? ".exe" : "")); + } catch( AssertionError error ) { + assertTrue(error.getMessage(),error.getMessage().contains("Program exit status")); + return; + } + fail("A failing native program must fail the test, even with empty stdout"); + } + @Test public void testCoalescing() { com.seaofnodes.simple.codegen.RegAllocTestSupport.coalescing(); } @Test public void testCopyForward() throws Exception { com.seaofnodes.simple.codegen.RegAllocTestSupport.copyForward(); } @Test public void testRisc64BitStore() { @@ -278,8 +292,7 @@ public class Chapter21Test { } static void testCPU( String src, String cpu, String os, int spills, String stop ) { - CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os); - com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code); + CodeGen code = new CheckedCodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os); SpillStats.record(code,"Chapter21",cpu,os); SpillStats.checkSpills(spills,code._regAlloc._spillScaled); if( stop != null ) @@ -422,7 +435,7 @@ public class Chapter21Test { @Test public void testPerson() throws IOException { String person = "6\n"; - TestC.run("person", person, 0); + TestC.run("person21", person, 0); // Memory layout starting at PS: int ps = 1<<16; // Person array pointer starts at heap start @@ -433,7 +446,7 @@ public class Chapter21Test { int p1 = ps+4*8+1*8; // P2 = { age } // sizeof=8 int p2 = ps+4*8+2*8; - EvalRisc5 R5 = TestRisc5.build("person", ps, 0, false); + EvalRisc5 R5 = TestRisc5.build("person21", ps, 0, false); R5.regs[riscv.A1] = 1; // Index 1 R5.st8(ps,3); // Length R5.st8(ps+1*8,p0); @@ -449,7 +462,7 @@ public class Chapter21Test { assertEquals(17+1,R5.ld8(p1)); assertEquals(60+0,R5.ld8(p2)); - EvalArm64 A5 = TestArm64.build("person", ps, 0, false); + EvalArm64 A5 = TestArm64.build("person21", ps, 0, false); A5.regs[arm.X1] = 1; // Index 1 A5.st8(ps, 3); A5.st8(ps+1*8,p0); diff --git a/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/src/test/java/com/seaofnodes/simple/Chapter22Test.java new file mode 100644 index 00000000..a506004e --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/Chapter22Test.java @@ -0,0 +1,411 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.isa.eval.EvalRisc5; + +import com.seaofnodes.isa.eval.EvalArm64; + +import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen; +import com.seaofnodes.simple.codegen.CodeGen; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.seaofnodes.simple.node.cpus.arm.arm; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter22Test { + + @Test public void testNarrowCReturnsEmulated() { + String[] types = {"i8","u8","i16","u16","i32","u32","int"}; + long[] values = {0xa5a5a5a500000000L,0xa5a5a5a50000007fL,0xa5a5a5a500000080L, + 0xa5a5a5a500007fffL,0xa5a5a5a500008000L,0xa5a5a5a57fffffffL, + 0xa5a5a5a580000000L,0xa5a5a5a5ffffffffL}; + for( String target : new String[]{"arm","riscv"} ) + for( int i=0; i<types.length; i++ ) { + String src = "{"+types[i]+"} c_narrow=\"C\"; return c_narrow();"; + CodeGen code = new CheckedCodeGen(src).driver(CodeGen.Phase.Encoding,target,"SystemV"); + var enc = code._encoding; + // Link the external call to a tiny independent native stub. + var call = code._stop.walk(n -> n instanceof com.seaofnodes.simple.node.CallNode ? n : null); + int start = enc._opStart[call._nid]; + ((com.seaofnodes.simple.codegen.RIPRelSize)call).patch(enc,start,enc._opLen[call._nid],0x1000-start); + for( long value : values ) { + long expected = switch(i) { + case 0 -> (byte)value; case 1 -> value&255; + case 2 -> (short)value; case 3 -> value&65535; + case 4 -> (int)value; case 5 -> value&0xffffffffL; + default -> value; + }; + byte[] image = new byte[1<<16]; + byte[] bits = enc.bits(); + System.arraycopy(bits,0,image,0,bits.length); + if( target.equals("arm") ) { + EvalArm64 cpu = new EvalArm64(image,1<<15); + // MOVZ X0,0x1100; LDR X0,[X0]; RET X30. + cpu._pc = enc._opStart[code.link(code._main)._nid]; + cpu.st4(0x1000,0xd2822000); cpu.st4(0x1004,0xf9400000); + cpu.st4(0x1008,0xd65f03c0); + cpu.st8(0x1100,value); + assertEquals(0,cpu.step(1000)); + assertEquals(0,cpu._pc); + assertEquals(types[i],expected,cpu.regs[0]); + } else { + EvalRisc5 cpu = new EvalRisc5(image,1<<15); + // AUIPC a0,0; LD a0,12(a0); RET. + cpu._pc = enc._opStart[code.link(code._main)._nid]; + cpu.st4(0x1000,0x00000517); cpu.st4(0x1004,0x00c53503); + cpu.st4(0x1008,0x00008067); + // RV64 widens even u32 returns by sign-extending bit 31. + cpu.st8(0x100c,i==6 ? value : (int)expected); + assertEquals(0,cpu.step(1000)); + assertEquals(0,cpu._pc); + assertEquals(types[i],expected,cpu.regs[riscv.A0]); + } + } + } + } + + @Test public void testNarrowCReturns() throws IOException { + String src = """ + {i8} c_i8="C"; {u8} c_u8="C"; + {i16} c_i16="C"; {u16} c_u16="C"; + {i32} c_i32="C"; {u32} c_u32="C"; + {int} c_i64="C"; + val test_i8={->return c_i8();}; + val test_u8={->return c_u8();}; + val test_i16={->return c_i16();}; + val test_u16={->return c_u16();}; + val test_i32={->return c_i32();}; + val test_u32={->return c_u32();}; + val test_i64={->return c_i64();}; + val test_cmp={->return c_i32()==-1;}; + """; + for( String conv : new String[]{"SystemV","Win64"} ) + TestC.run(src,conv,conv.equals("SystemV") ? "sysv_abi" : "ms_abi", + "src/test/java/com/seaofnodes/simple/progs/c_returns.c", + "build/objs/c_returns"+conv,"S","",-1); + } + + + + @Test public void testReturnedFunctions() throws IOException { + for( String src : new String[]{"return {->42;};", "val f={->42;}; return f;", "return sys.io.p;"} ) + for( String target : new String[]{"riscv","arm"} ) { + CodeGen code = new CheckedCodeGen(src).driver(CodeGen.Phase.Encoding,target,"SystemV").exportELF(null); + byte[] image = new byte[1<<16]; + byte[] bits = code._encoding.bits(); + System.arraycopy(bits,0,image,0,bits.length); + boolean library = src.contains("sys.io.p"); + // u8[]: four-byte length followed by its bytes. + image[0x2000]=2; image[0x2004]='o'; image[0x2005]='k'; + int entry = code._encoding._opStart[code.link(code._main)._nid]; + if( target.equals("riscv") ) { + EvalRisc5 cpu = new EvalRisc5(image,1<<15); + cpu._pc = entry; + assertEquals(0,cpu.step(1000)); + assertEquals(0,cpu._pc); + cpu._pc = (int)cpu.regs[riscv.A0]; // Call the returned address. + cpu.regs[riscv.A0] = 0x2000; + assertEquals(0,cpu.step(1000)); + assertEquals(0,cpu._pc); + if( library ) assertEquals("ok",cpu._stdout.toString()); + else assertEquals(42,cpu.regs[riscv.A0]); + } else { + EvalArm64 cpu = new EvalArm64(image,1<<15); + cpu._pc = entry; + assertEquals(0,cpu.step(1000)); + assertEquals(0,cpu._pc); + cpu._pc = (int)cpu.regs[0]; + cpu.regs[0] = 0x2000; + assertEquals(0,cpu.step(1000)); + assertEquals(0,cpu._pc); + if( library ) assertEquals("ok",cpu._stdout.toString()); + else assertEquals(42,cpu.regs[0]); + } + } + CodeGen unused = new CodeGen("({->42;}); return 0;").parse().iter().finalizeIR(); + assertEquals(1,unused._stop.nIns()); + } + + @Test public void testReturnedFunctionsNative() throws IOException { + String src = "val factory={int x -> if(x) return {->42;}; return {->43;};}; val printer={->return sys.io.p;};"; + String obj = "build/objs/funptr.o"; + new CheckedCodeGen(src).driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,obj); + assertEquals("ok",TestC.gcc(obj,"","src/test/java/com/seaofnodes/simple/progs/funptr.c",false, + "build/objs/funptr"+(TestC.OS.startsWith("Windows") ? ".exe" : ""))); + } + + @Test public void testRiscvRightShifts() { + // SRAI, SRLI, SRA, SRL: a0 = -32 shifted by 4. + int[] ops = {0x40455513,0x00455513,0x40b55533,0x00b55533}; + for( int i=0; i<ops.length; i++ ) { + EvalRisc5 r5 = new EvalRisc5(new byte[1<<16],1<<15); + r5.st4(0,ops[i]); + r5.regs[riscv.A0] = -32; + r5.regs[riscv.A0+1] = 4; + assertEquals(0,r5.step(1)); + assertEquals((i&1)==0 ? -2L : 0x0fff_ffff_ffff_fffeL,r5.regs[riscv.A0]); + } + } + + @Test public void testRiscvPointerRelocation() { + CodeGen code = new CheckedCodeGen("return \"x\";").driver(CodeGen.Phase.Encoding,"riscv","SystemV"); + var enc = code._encoding; + assertEquals(1,enc._bigCons.size()); + var ptr = (com.seaofnodes.simple.node.cpus.riscv.TMPRISC)enc._bigCons.keySet().iterator().next(); + int start = enc._opStart[ptr._nid]; + for( int delta : new int[]{0x7ff,0x800,0xfff,0x1000,0x1800,-1,-0x800,-0x801} ) { + ptr.patch(enc,start,enc._opLen[ptr._nid],delta); + // Place the program above zero so backward targets are valid addresses. + byte[] image = new byte[1<<16]; + System.arraycopy(enc.bits(),0,image,0x4000,enc._bits.size()); + EvalRisc5 r5 = new EvalRisc5(image,1<<15); + r5._pc = 0x4000; + assertEquals(0,r5.step(100)); + assertEquals(0x4000+start+delta,r5.regs[riscv.A0]); + } + } + + @Test public void testSubZeroTypeError() { + try { + new CodeGen("return null-0;").driver(CodeGen.Phase.TypeCheck); + fail("Subtraction must reject null even when the other operand is zero"); + } catch( Parser.ParseException e ) { + assertEquals("Cannot '-' null",e.getMessage()); + } + } + + @Test public void testInfiniteReturn() { + String src = "struct S { int i; }; !S !s = new S; while(1) s.i++; return s.i;"; + testCPU(src,"x86_64_v2","SystemV",0,"return Top;"); + testCPU(src,"riscv","SystemV",0,"return Top;"); + testCPU(src,"arm","SystemV",0,"return Top;"); + } + + + @Test @Ignore + public void testJig() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp")); + //String src = Files.readString(Path.of("docs/examples/BubbleSort.smp")); + testCPU(src,"x86_64_v2", "Win64" ,-1,null); + testCPU(src,"riscv" , "SystemV",-1,null); + testCPU(src,"arm" , "SystemV",-1,null); + } + + static CodeGen testCPU( String src, String cpu, String os, int spills, String stop ) { + CodeGen code = new CheckedCodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os); + SpillStats.record(code,"Chapter22",cpu,os); + SpillStats.checkSpills(spills,code._regAlloc._spillScaled); + if( stop != null ) + assertEquals(stop, code._stop.toString()); + return code; + } + + + static int testCPUSize( String src, String cpu, String os, int spills, String stop ) { + return testCPU(src,cpu,os,spills,stop)._encoding._bits.size(); + } + + // Should not fold away + @Test public void testSextFail() throws IOException { + String src = """ +struct Person { i32 age;}; +!Person !p = new Person; +p.age = (arg<<17)>>17; +return 0; +"""; + assertEquals(46, testCPUSize(src, "x86_64_v2","Win64",2,"return 0;")); + assertEquals(60, testCPUSize(src, "riscv","SystemV",4,"return 0;")); + assertEquals(56, testCPUSize(src, "arm","SystemV",4,"return 0;")); + + // do assertEquals here + EvalRisc5 R5 = TestRisc5.build("sext_str_not_fold_away", 0, 4, false); + int trap = R5.step(100); + assertEquals(0,trap); + + EvalArm64 A5 = TestArm64.build("sext_str_not_fold_away", 0, 4, false); + trap = A5.step(100); + assertEquals(0,trap); + + } + + // Should not fold away + @Test public void testSextFail2() throws IOException { + String src = """ + + struct Person { i32 age;}; + !Person !p = new Person; + p.age = (arg<<48)>>48; + return 0; + """; + + EvalRisc5 R5 = TestRisc5.build("sext_str_not_fold_away_2", 0, 4, false); + int trap = R5.step(100); + assertEquals(0,trap); + + EvalArm64 A5 = TestArm64.build("sext_str_not_fold_away_2", 0, 4, false); + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + + assertEquals(46, testCPUSize(src, "x86_64_v2","Win64",2,"return 0;")); + assertEquals(60, testCPUSize(src, "riscv","SystemV",4,"return 0;")); + assertEquals(56, testCPUSize(src, "arm","SystemV",4,"return 0;")); + + } + + // Should fold away + @Test public void testSextSuccess() throws IOException { + String src = """ +// Should fold away sign extend +struct Person { i8 age;}; +!Person !p = new Person; +p.age = (arg<<48)>>48; +return 0; + """; + + EvalRisc5 R5 = TestRisc5.build("sext_str_fold_away", 0, 5, false); + int trap = R5.step(100); + assertEquals(0,trap); + + EvalArm64 A5 = TestArm64.build("sext_str_fold_away", 0, 5, false); + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + + assertEquals(41, testCPUSize(src, "x86_64_v2","Win64",2,"return 0;")); + assertEquals(56, testCPUSize(src, "riscv","SystemV",5,"return 0;")); + assertEquals(52, testCPUSize(src, "arm","SystemV",5,"return 0;")); + // do assertEquals here + } + + + // Int now is changed to 4 bytes. + @Test public void testPerson() throws IOException { + String person = "6\n"; + TestC.run("person", person, 0); + + // Memory layout starting at PS: + int ps = 1<<16; // Person array pointer starts at heap start + // Person[3] = { len,pad,P0,P1,P2 }; // sizeof = 4*8 + // P0 = { age } // sizeof=8 + int p0 = ps+4*8+0*8; + // P1 = { age } // sizeof=8 + int p1 = ps+4*8+1*8; + // P2 = { age } // sizeof=8 + int p2 = ps+4*8+2*8; + EvalRisc5 R5 = TestRisc5.build("person", ps, 0, false); + R5.regs[riscv.A1] = 1; // Index 1 + R5.st8(ps,3); // Length + R5.st8(ps+1*8,p0); + R5.st8(ps+2*8,p1); + R5.st8(ps+3*8,p2); + R5.st8(p0, 5); // age= 5 + R5.st8(p1,17); // age=17 + R5.st8(p2,60); // age=60 + + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals( 5+0,R5.ld8(p0)); + assertEquals(17+1,R5.ld8(p1)); + assertEquals(60+0,R5.ld8(p2)); + + EvalArm64 A5 = TestArm64.build("person", ps, 0, false); + A5.regs[arm.X1] = 1; // Index 1 + A5.st8(ps, 3); + A5.st8(ps+1*8,p0); + A5.st8(ps+2*8,p1); + A5.st8(ps+3*8,p2); + A5.st8(p0, 5); // age= 5 + A5.st8(p1,17); // age=17 + A5.st8(p2,60); // age=60 + + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + assertEquals( 5+0, A5.ld8(p0)); + assertEquals(17+1, A5.ld8(p1)); + assertEquals(60+0, A5.ld8(p2)); + } + + @Test + public void testCoRecur() { + String src = """ +struct A { !B? !b; !C? !c; i64 ax; val az = x*2; }; +struct B { !A? !a; !C? !c; f32 bx; val bz = x*3; }; +struct C { !A? !a; !B? !b; f64 cx; val cz = x*x; }; +!A !aa = new A{ ax=17; }; +!B !bb = new B{ bx=3.14; a = aa; }; +!C !cc = new C{ cx=2.73; a = aa; b = bb; }; +aa.b = bb; +aa.c = cc; +bb.c = cc; +val x = 5; // aa.az; // Error to self-define forward ref +return cc.cz; +"""; + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + assertEquals("return 25;", code._stop.toString()); + assertEquals("25", Eval2.eval(code, 0)); + } + + @Test + public void testHelloWorld() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/helloWorld.smp")); + TestC.run(src,TestC.CALL_CONVENTION,null,null,"build/objs/helloWorld","","Hello, World!",0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build("helloWorld", 0, 2, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + assertEquals("Hello, World!",R5._stdout.toString()); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("helloWorld", 0, 2, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + assertEquals("Hello, World!",arm._stdout.toString()); + } + + @Test + public void testFinalArray() { + String src = """ +int N=4; +i32[] !is = new i32[N]; +for( int i=0; i<N; i++ ) + is[i] = i*i; +val sum = { i32[~] is -> // final array + int sum=0; + for( int i=0; i<is#; i++ ) + sum += is[i]; + return sum; +}; +return sum(is); +"""; + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); + assertEquals("return Phi(Loop,0,(Phi_sum+.[]));", code._stop.toString()); + assertEquals("14", Eval2.eval(code, 0)); + } + + + @Test @Ignore + public void testEcho() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/echo.smp")); + TestC.run(src,TestC.CALL_CONVENTION,null,null,"build/objs/echo","","",0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build("echo", 0, 2, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("echo", 0, 2, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + } + + +} diff --git a/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java index 8131c95f..e2d822d0 100644 --- a/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java +++ b/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java @@ -88,7 +88,7 @@ public class CompoundAssignmentTest { } private static void check(String src, long arg, long expected) { - var code = new CodeGen(src).parse().iter().typeCheck(); + var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); assertEquals(src, Long.toString(expected), Eval2.eval(code, arg)); } } diff --git a/src/test/java/com/seaofnodes/simple/Eval2.java b/src/test/java/com/seaofnodes/simple/Eval2.java index f39750c2..bbf88f1a 100644 --- a/src/test/java/com/seaofnodes/simple/Eval2.java +++ b/src/test/java/com/seaofnodes/simple/Eval2.java @@ -114,6 +114,7 @@ public abstract class Eval2 { public static String eval( CodeGen code, long arg ) { return eval(code,arg,1000); } public static String eval( CodeGen code, long arg, int timeout ) { + if( code._start.uctrl()==null ) return ""; // The empty program SB trace = null; // new SB(); // TRACE, set to null for off, new SB() for on // Force local scheduling phase code.driver(CodeGen.Phase.LocalSched); @@ -297,9 +298,9 @@ public abstract class Eval2 { // Fetch without unboxing, searching up-Frame static Object val( Node n ) { return F.get(n); } // Fetch and unbox as primitive long - static long x( Node n ) { Object d = F.get(n); return d==null ? 0 : (Long) F.get(n); } + static long x( Node n ) { Object d = F.get(n); return d==null ? 0 : (Long)d; } // Fetch and unbox as primitive double - static double d( Node n ) { Object d = F.get(n); return d==null ? 0 : (Double)F.get(n); } + static double d( Node n ) { Object d = F.get(n); return d==null ? 0 : (Double)d; } // Fetch and unbox a function constant static TypeFunPtr tfp(Node n) { return (TypeFunPtr)F.get(n); } // Fetch and unbox a closure @@ -355,10 +356,19 @@ public abstract class Eval2 { } private static Object load( LoadNode ld ) { - TypeMemPtr tmp = (TypeMemPtr)ld.ptr()._type; - Object[] fs = (Object[])val(ld.ptr()); + Object f = val(ld.ptr()); + // Check for dense constant array + if( f instanceof TypeMemPtr tmp ) { + assert tmp._obj._con != TypeConAry.BOT; + if( ld._name.equals("#") ) + return (long)tmp._obj._con.len(); + int idx = offToIdx(x(ld.off()),tmp._obj); + return tmp._obj._con.at(idx); + } + Object[] fs = (Object[])f; if( ld._name.equals("#") ) return (long)fs.length; + TypeMemPtr tmp = (TypeMemPtr)ld.ptr()._type; int idx = tmp._obj.isAry() ? offToIdx(x(ld.off()),tmp._obj) : tmp._obj.find(ld._name); diff --git a/src/test/java/com/seaofnodes/simple/LiteralTest.java b/src/test/java/com/seaofnodes/simple/LiteralTest.java new file mode 100644 index 00000000..0f6e43a4 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/LiteralTest.java @@ -0,0 +1,65 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.type.TypeConAryB; +import com.seaofnodes.simple.type.TypeInteger; +import com.seaofnodes.simple.type.TypeMemPtr; +import org.junit.Test; +import static org.junit.Assert.*; + +public class LiteralTest { + private static final String[] ESCAPES = {"\\0", "\\n", "\\t", "\\r", "\\\\", "\\\"", "\\'"}; + private static final int[] VALUES = {0, 10, 9, 13, 92, 34, 39}; + + @Test public void testStringEscapes() { + for (int i = 0; i < ESCAPES.length; i++) + assertString(ESCAPES[i], new byte[]{(byte)VALUES[i]}); + assertString("a\\\"b\\\\c\\0d", new byte[]{97, 34, 98, 92, 99, 0, 100}); + assertString("", new byte[]{}); + assertString("plain", new byte[]{112, 108, 97, 105, 110}); + } + + private static void assertString(String text, byte[] expected) { + var code = new CodeGen("return \"" + text + "\";").driver(CodeGen.Phase.TypeCheck); + TypeMemPtr ptr = (TypeMemPtr)code.expr()._type; + TypeConAryB bytes = (TypeConAryB)ptr._obj._con; + assertArrayEquals(text, expected, bytes._ary); + } + + @Test public void testCharacterEscapes() { + for (int i = 0; i < ESCAPES.length; i++) + assertCharacter(ESCAPES[i], VALUES[i]); + assertCharacter("A", 65); + } + + private static void assertCharacter(String text, int expected) { + var code = new CodeGen("return '" + text + "';").driver(CodeGen.Phase.TypeCheck); + assertEquals(text, expected, ((TypeInteger)code.expr()._type).value()); + } + + @Test public void testMalformedStrings() { + assertBadLiteral("return \"\\q\";", "Unknown string escape"); + assertBadLiteral("return \"abc", "Unclosed string"); + assertBadLiteral("return \"abc\\", "Unclosed string"); + // The quote closes the string at EOF; the missing token is the semicolon. + assertBadLiteral("return \"plain\"", ";"); + } + + @Test public void testMalformedCharacters() { + assertBadLiteral("return '\\q';", "Unknown character escape"); + assertBadLiteral("return '", "Unclosed character"); + assertBadLiteral("return '\\", "Unclosed character"); + assertBadLiteral("return '';", "Syntax error"); + assertBadLiteral("return 'ab';", "Syntax error"); + assertBadLiteral("return 'a", "Syntax error"); + } + + private static void assertBadLiteral(String src, String message) { + try { + new CodeGen(src).parse(); + fail("Accepted malformed literal: " + src); + } catch (Parser.ParseException e) { + assertTrue(e.getMessage(), e.getMessage().contains(message)); + } + } +} diff --git a/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java b/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java index 7cdf217b..700e5344 100644 --- a/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java +++ b/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java @@ -9,6 +9,7 @@ import com.seaofnodes.simple.codegen.RegAllocTestSupport; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.node.cpus.arm.arm; import com.seaofnodes.simple.node.cpus.riscv.riscv; +import com.seaofnodes.simple.type.TypeMemPtr; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -65,7 +66,7 @@ public class MemoryEncodingTest { """ }; for( int p=0; p<sources.length; p++ ) { - CodeGen code=new CodeGen(sources[p]).parse().iter(); + CodeGen code=new CodeGen(sources[p]).parse().iter().finalizeIR(); if( p==0 ) assertNull(code._stop.walk(n -> n instanceof LoadNode ld && ld.ptr() instanceof ProjNode ptr && ptr.in(0) instanceof NewNode nn && @@ -152,8 +153,9 @@ public class MemoryEncodingTest { assertNotNull(code._stop.walk(n -> { if( n instanceof MemMergeNode mem && mem.in(0) instanceof CProjNode exit && exit.in(0) instanceof NeverNode ) { - assertTrue(mem.alias(2) instanceof MemPhiNode); - assertTrue(mem.alias(3) instanceof MemPhiNode); + var fields = ((TypeMemPtr)Parser.TYPES.get("S"))._obj._fields; + assertTrue(mem.alias(fields[0]._alias) instanceof MemPhiNode); + assertTrue(mem.alias(fields[1]._alias) instanceof MemPhiNode); return mem; } return null; @@ -173,7 +175,7 @@ public class MemoryEncodingTest { c.append("if(memory(").append(arg).append(")!=").append(expected(p,arg)).append("LL) return ").append(arg+1).append(";\n"); c.append("return 0;\n}\n"); Files.writeString(Path.of(file+".c"),c); - TestC._run("val memory = { int arg -> "+SOURCES[p]+" };", + TestC.run("val memory = { int arg -> "+SOURCES[p]+" };", TestC.CALL_CONVENTION,"",file+".c",file,"S","",-1); } } diff --git a/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/src/test/java/com/seaofnodes/simple/MutabilityTest.java index baf4e252..51e31a17 100644 --- a/src/test/java/com/seaofnodes/simple/MutabilityTest.java +++ b/src/test/java/com/seaofnodes/simple/MutabilityTest.java @@ -6,12 +6,12 @@ import com.seaofnodes.simple.codegen.CodeGen; public class MutabilityTest { private static void ok(String src, String result) { - var code = new CodeGen(src).parse().iter().typeCheck(); + var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck); assertEquals(result,Eval2.eval(code,0)); } private static void bad(String src) { try { - new CodeGen(src).parse().iter().typeCheck(); + new CodeGen(src).driver(CodeGen.Phase.TypeCheck); } catch( RuntimeException expected ) { String msg = expected.getMessage(); assertTrue("Expected a permission error, got: "+expected, diff --git a/src/test/java/com/seaofnodes/simple/PhaseTest.java b/src/test/java/com/seaofnodes/simple/PhaseTest.java index d890a537..885889a8 100644 --- a/src/test/java/com/seaofnodes/simple/PhaseTest.java +++ b/src/test/java/com/seaofnodes/simple/PhaseTest.java @@ -27,13 +27,13 @@ public class PhaseTest { code.driver(Phase.LoopTree); assertEquals(Phase.LoopTree,code._phase); code.driver(Phase.LocalSched); - assertEquals("Parse Iter TypeCheck LoopTree Schedule LocalSched",String.join(" ",phases)); + assertEquals("Parse Iter FinalizeIR TypeCheck LoopTree Schedule LocalSched",String.join(" ",phases)); assertEquals("45",Eval2.eval(code,10)); } @Test public void testResumeMachineCompilation() { CodeGen staged = new CodeGen(SRC).driver(Phase.TypeCheck); - staged.driver(Phase.InstSelect,"x86_64_v2","SystemV"); + staged.driver(Phase.Select,"x86_64_v2","SystemV"); staged.driver(Phase.Encoding,"x86_64_v2","SystemV"); byte[] bits = staged._encoding._bits.toByteArray(); CodeGen direct = new CodeGen(SRC).driver(Phase.Encoding,"x86_64_v2","SystemV"); diff --git a/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/src/test/java/com/seaofnodes/simple/SchedulerTest.java index a388d8c3..ca211c60 100644 --- a/src/test/java/com/seaofnodes/simple/SchedulerTest.java +++ b/src/test/java/com/seaofnodes/simple/SchedulerTest.java @@ -51,7 +51,7 @@ int i=v.f; if (arg) v.f=1; return i; """); - code.parse();//.iter(); + code.parse();//.iter().finalizeIR(); var eval = new Evaluator(code._stop); assertEquals(2L, eval.evaluate(1, 10)); } @@ -66,7 +66,7 @@ struct S { if(arg) v0.f=1; return v0; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); var eval = new Evaluator(code._stop); assertObj(eval.evaluate(0, 10), "S", 0L); assertObj(eval.evaluate(1, 10), "S", 1L); @@ -85,7 +85,7 @@ i=v.f; if (arg) v.f=1; return i; """); - code.parse().iter(); + code.parse().iter().finalizeIR(); var eval = new Evaluator(code._stop); assertEquals(2L, eval.evaluate(0, 10)); assertEquals(2L, eval.evaluate(1, 10)); diff --git a/src/test/java/com/seaofnodes/simple/Simple.java b/src/test/java/com/seaofnodes/simple/Simple.java index c496ea90..69fbabc9 100644 --- a/src/test/java/com/seaofnodes/simple/Simple.java +++ b/src/test/java/com/seaofnodes/simple/Simple.java @@ -16,9 +16,10 @@ public class Simple { static final int DUMP_AFTER_PARSE = 1<<Phase.Parse.ordinal(); static final int DUMP_AFTER_ITER = 1<<Phase.Iter.ordinal(); + static final int DUMP_AFTER_FINALIZE_IR = 1<<Phase.FinalizeIR.ordinal(); static final int DUMP_AFTER_TYPE_CHECK = 1<<Phase.TypeCheck.ordinal(); static final int DUMP_AFTER_LOOP_TREE = 1<<Phase.LoopTree.ordinal(); - static final int DUMP_AFTER_INSTR_SELECT = 1<<Phase.InstSelect.ordinal(); + static final int DUMP_AFTER_SELECT = 1<<Phase.Select.ordinal(); static final int DUMP_AFTER_GCM = 1<<Phase.Schedule.ordinal(); static final int DUMP_AFTER_LOCAL_SCHED = 1<<Phase.LocalSched.ordinal(); static final int DUMP_AFTER_REG_ALLOC = 1<<Phase.RegAlloc.ordinal(); @@ -31,33 +32,31 @@ public class Simple { static final int DUMP_DISASSEMBLE = 1<<31; - static String system_cpu = null; - static String system_abi = null; - static void usage() { System.out.println( """ -simple <input-file> [options] [--eval|--run ...] +simple [options] <input-file> ...args... Options: - --dump - dump final intermediate representation + --dump - dump final intermediate representation --dump-after-parse - dump intermediate representation after parsing --dump-after-iter - dump intermediate representation after Iter + --dump-after-finalize-ir - dump intermediate representation after FinalizeIR --dump-after-type-check - dump intermediate representation after type checking --dump-after-loop-tree - dump intermediate representation after loop-tree construction - --dump-after-instr-select - dump intermediate representation after instruction selection + --dump-after-select - dump intermediate representation after instruction selection --dump-after-gcm - dump intermediate representation after GCM --dump-after-local-sched - dump intermediate representation after local scheduling --dump-after-reg-alloc - dump intermediate representation after register allocation --dump-after-encode - dump intermediate representation after encoding --dump-after-all - dump intermediate representation after all passes - -S - dump generated assembler code - --eval ... - evaluate the compiled code in emulator - --run ... - run the compiled code natively - --dump-size - print the size of generated code - --dump-time - print compilation and execution times - --cpu <cpu-name> - use specific CPU (x86_64_v2, riscv, arm) - --abi <abi-name> - use speific ABI variant (SystemV) - --target - print native CPU and ABI + -S - dump generated assembler code + --eval - evaluate the compiled code in emulator + --run - run the compiled code natively; this is the default + --dump-size - print the size of generated code + --dump-time - print compilation and execution times + --cpu <cpu-name> - use specific CPU (x86_64_v2, riscv, arm) + --abi <abi-name> - use speific ABI variant (SystemV) + --target - print native CPU and ABI --version --help """ @@ -65,20 +64,17 @@ Options: System.exit(0); } - static void bad_usage() { - System.err.println("ERROR: Invalid usage (use --help)"); + static void bad_usage() { bad("Invalid usage (use --help)"); } + static RuntimeException bad(String err) { + System.err.println("ERROR: "+err); System.exit(1); + return new RuntimeException(err); } static void target() { - if (system_cpu == null || system_abi == null) { - System.err.println("ERROR: Unknown target"); - System.exit(1); - } - System.out.print(system_cpu); + System.out.print(TestC.CPU_PORT); System.out.print("-"); - System.out.println(system_abi); - System.exit(0); + System.out.println(TestC.CALL_CONVENTION); } static void dump(CodeGen code, int dump, int pass) { @@ -105,9 +101,8 @@ Options: public static void main(String[] args) throws Exception { String input_filename = null; - boolean use_stdin = false; boolean do_eval = false; - boolean do_run = false; + boolean do_run = true; boolean do_codegen = false; boolean do_print_size = false; boolean do_print_time = false; @@ -117,83 +112,69 @@ Options: String cpu = null; String abi = null; - // TODO: autodetect - system_cpu = "x86_64_v2"; - system_abi = "SystemV"; - // Parse command line -loop: for (int i = 0; i < args.length; i++) { + int i; for( i = 0; i < args.length; i++ ) { String arg = args[i]; - if (arg.charAt(0) == '-') { - switch (arg) { - case "-": use_stdin = true; break; - case "--dump": dump |= DUMP_FINAL; break; - case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break; - case "--dump-after-iter": dump |= DUMP_AFTER_ITER; break; - case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break; - case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break; - case "--dump-after-instr-select": dump |= DUMP_AFTER_INSTR_SELECT; break; - case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break; - case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break; - case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break; - case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break; - case "--dump-after-all": dump |= DUMP_AFTER_ALL; break; - case "-S": dump |= DUMP_DISASSEMBLE; break; - case "--eval": do_eval = true; first_arg = i + 1; break loop; - case "--run": do_run = true; first_arg = i + 1; break loop; - case "--dump-size": do_print_size = true; break; - case "--dump-time": do_print_time = true; break; - case "--cpu": if (cpu != null - || i + 1 >= args.length - || args[i + 1].charAt(0) == '-') bad_usage(); - cpu = args[i + 1]; - i++; - break; - case "--abi": if (abi != null - || i + 1 >= args.length - || args[i + 1].charAt(0) == '-') bad_usage(); - abi = args[i + 1]; - i++; - break; - case "--target": target(); - case "--version": throw Utils.TODO(); - case "--help": usage(); - default: bad_usage(); - } - } else { - if (input_filename != null || use_stdin) bad_usage(); + if( arg.charAt(0) != '-' ) { input_filename = arg; + break; } - } - if (input_filename == null && !use_stdin) { - System.err.println("ERROR: no input file' (use --help)"); - System.exit(1); + switch (arg) { + case "-": input_filename=""; break; + case "--dump": dump |= DUMP_FINAL; break; + case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break; + case "--dump-after-finalize-ir": dump |= DUMP_AFTER_FINALIZE_IR; break; + case "--dump-after-iter": dump |= DUMP_AFTER_ITER; break; + case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break; + case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break; + case "--dump-after-select": dump |= DUMP_AFTER_SELECT; break; + case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break; + case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break; + case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break; + case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break; + case "--dump-after-all": dump |= DUMP_AFTER_ALL; break; + case "-S": dump |= DUMP_DISASSEMBLE; break; + case "--eval": do_eval = true ; do_run = false; break; + case "--run": do_run = true ; break; + case "--norun": do_run = false; break; + case "--dump-size": do_print_size = true; break; + case "--dump-time": do_print_time = true; break; + case "--cpu": if (cpu != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage(); + cpu = args[++i]; + break; + case "--abi": if (abi != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage(); + abi = args[++i]; + break; + case "--target": target(); break; + case "--version": throw Utils.TODO(); + case "--help": usage(); + default: bad_usage(); + } } + if (input_filename == null) throw bad("no input file' (use --help)"); + if( !input_filename.isEmpty() && !input_filename.endsWith(".smp") ) + throw bad("File extension must be .smp"); + String base = input_filename.substring(0,input_filename.length()-4); + if (do_run || (dump & DUMP_DISASSEMBLE) != 0 || do_print_size) { - if (cpu == null) cpu = system_cpu; - if (abi == null) abi = system_abi; - if (cpu == null || abi == null) { - System.err.println("ERROR: Cannot compile for unknown target"); - System.exit(1); - } + if (cpu == null) cpu = TestC.CPU_PORT; + if (abi == null) abi = TestC.CALL_CONVENTION; do_codegen = true; } // Read input file try { - src = use_stdin ? getInput() : Files.readString(Path.of(input_filename)); - } catch(IOException e) { - System.err.println("ERROR: Cannot read input file"); - System.exit(1); - } + src = input_filename.isEmpty() ? getInput() : Files.readString(Path.of(input_filename)); + } catch( IOException e ) { throw bad("Cannot read input file: "+input_filename); } // Compilation pipeline CodeGen code = new CodeGen(src); code.driver(do_codegen ? Phase.Encoding : Phase.LocalSched, do_codegen ? cpu : null, abi, dump); + if( do_codegen ) code.exportELF(base+".o"); dump(code, dump, DUMP_FINAL); @@ -202,7 +183,7 @@ loop: for (int i = 0; i < args.length; i++) { } if (do_print_size) { - System.out.println(String.format("Code Size: %d", code._encoding._bits.size())); + System.out.printf( "Code Size: %d%n", code._encoding._bits.size()); } if (do_print_time) { @@ -212,19 +193,19 @@ loop: for (int i = 0; i < args.length; i++) { if (do_eval) { // TODO: Support for evaluation of functions with different argument numbers and types long t = System.currentTimeMillis(); - long arg = (first_arg < args.length) ? Integer.valueOf(args[first_arg]) : 0; + long arg = (i+1 < args.length) ? Integer.parseInt(args[i+1]) : 0; System.out.println(Eval2.eval(code, arg, 100000)); if (do_print_time) { - System.out.println(String.format("EXECUTION TIME: %.3f sec", - (System.currentTimeMillis() - t) / 1e3)); + System.out.printf( "EXECUTION TIME: %.3f sec%n", + (System.currentTimeMillis() - t) / 1e3); } - } else if (do_run) { - if (cpu != system_cpu || abi != system_abi) { - System.err.println("ERROR: cannot run code on not native target"); - System.exit(1); - } - - throw Utils.TODO(); + } + if (do_run) { + if ( !TestC.CPU_PORT.equals( cpu ) || !TestC.CALL_CONVENTION.equals( abi ) ) + throw bad("cannot run code on not native target"); + String exe = TestC.OS.startsWith("Windows") ? base+".exe" : base; + String result = com.seaofnodes.simple.TestC.gcc(base+".o", null, null, true, exe); + System.out.print(result); } } } diff --git a/src/test/java/com/seaofnodes/simple/SpillStats.java b/src/test/java/com/seaofnodes/simple/SpillStats.java index 318a6e33..82cffef8 100644 --- a/src/test/java/com/seaofnodes/simple/SpillStats.java +++ b/src/test/java/com/seaofnodes/simple/SpillStats.java @@ -26,6 +26,13 @@ public class SpillStats extends RunListener { code._regAlloc._spills+","+code._regAlloc._spillScaled); } + // The same native helpers serve old and new program cohorts. + public static void recordNative(CodeGen code, String cpu, String abi) { + String cohort = ACTIVE!=null && ACTIVE._test.getTestClass()==Chapter22Test.class + ? "Chapter22" : "Chapter21"; + record(code,cohort,cpu,abi); + } + // Keep running every CPU and native result check when measuring quality. // A changed golden still makes spill-stats fail at the end. public static void checkSpills(int expected, int actual) { @@ -39,7 +46,7 @@ public class SpillStats extends RunListener { } public static void main(String[] args) throws ClassNotFoundException { - if( args.length==0 ) args = new String[]{"Chapter20Test","Chapter21Test","Chapter21AllocTest","BrainFuckTest","MergeSortTest"}; + if( args.length==0 ) args = new String[]{"Chapter20Test","Chapter21Test","Chapter21AllocTest","Chapter22Test","BrainFuckTest","MergeSortTest"}; Class<?>[] tests = new Class<?>[args.length]; for( int i=0; i<args.length; i++ ) tests[i] = Class.forName("com.seaofnodes.simple."+args[i]); @@ -48,7 +55,8 @@ public class SpillStats extends RunListener { junit.addListener(ACTIVE); Result result = junit.run(tests); boolean passed = result.wasSuccessful() && ACTIVE._qualityFailures==0; - if( !passed ) System.out.println("FAILED test run: totals are not a complete comparison."); + if( !result.wasSuccessful() ) System.out.println("FAILED test run: totals are not a complete comparison."); + else if( !passed ) System.out.println("Spill expectations differ; allocation and runtime checks passed."); System.out.println("cohort,cpu,abi,allocations,splits,scaled"); for( var row : ACTIVE._totals.entrySet() ) { long[] sum = row.getValue(); diff --git a/src/test/java/com/seaofnodes/simple/TestArm64.java b/src/test/java/com/seaofnodes/simple/TestArm64.java index e4bc38a0..a22063eb 100644 --- a/src/test/java/com/seaofnodes/simple/TestArm64.java +++ b/src/test/java/com/seaofnodes/simple/TestArm64.java @@ -2,6 +2,8 @@ package com.seaofnodes.simple; import com.seaofnodes.isa.eval.EvalArm64; +import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen; + import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.cpus.arm.arm; @@ -23,9 +25,8 @@ public class TestArm64 { public static EvalArm64 build( String dir, String file, int arg, int spills, boolean print ) throws IOException { // Compile and export Simple String src = Files.readString(Path.of(dir+"/"+file+".smp")); - CodeGen code = new CodeGen(src).driver("arm", "SystemV",null); - com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code); - SpillStats.record(code,"Chapter21","arm","SystemV"); + CodeGen code = new CheckedCodeGen(src).driver("arm", "SystemV",null); + SpillStats.recordNative(code,"arm","SystemV"); if( print ) { code.print_as_hex(); System.out.print(code.asm()); } // Allocation quality not degraded diff --git a/src/test/java/com/seaofnodes/simple/TestC.java b/src/test/java/com/seaofnodes/simple/TestC.java index b3e21ea7..44c06128 100644 --- a/src/test/java/com/seaofnodes/simple/TestC.java +++ b/src/test/java/com/seaofnodes/simple/TestC.java @@ -1,10 +1,11 @@ package com.seaofnodes.simple; +import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen; import com.seaofnodes.simple.codegen.CodeGen; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.io.InputStream; -import java.io.IOException; +import java.util.concurrent.TimeUnit; import static org.junit.Assert.*; // Call a C driver program, which calls a Simple-generated .o, and self-checks. @@ -16,8 +17,8 @@ public abstract class TestC { public static final String CALL_CONVENTION = OS.startsWith("Windows") ? "Win64" : "SystemV"; public static final String CPU_PORT = switch( CPU ) { - case "amd64" -> "x86_64_v2"; - default -> throw Utils.TODO("Map Yer CPU Port Here"); + case "amd64" -> "x86_64_v2"; + default -> throw Utils.TODO("Map Yer CPU Port Here"); }; public static void run( String file, int spills ) throws IOException { run(file,"",spills); } @@ -35,54 +36,69 @@ public abstract class TestC { // Compile and export Simple String src = Files.readString(Path.of(sfile)); - _run(src,CALL_CONVENTION,"",cfile,efile,"S",expected,spills); + run(src,CALL_CONVENTION,"",cfile,efile,"S",expected,spills); } - static void _run( String src, String simple_conv, String c_conv, String cfile, String efile, String xtn, String expected, int spills ) throws IOException { + public static void run( String src, String simple_conv, String c_conv, String cfile, String efile, String xtn, String expected, int spills ) throws IOException { String bin = efile+xtn; String obj = bin+".o"; + String exe = OS.startsWith("Windows") ? bin+".exe" : bin; // Compile simple, emit ELF - CodeGen code = new CodeGen(src).driver( CPU_PORT, simple_conv, obj); - com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code); - SpillStats.record(code,"Chapter21",CPU_PORT,simple_conv); + CodeGen code = new CheckedCodeGen(src).driver( CPU_PORT, simple_conv, obj); + SpillStats.recordNative(code,CPU_PORT,simple_conv); + String result = gcc(obj, c_conv, cfile, false, exe ); + assertEquals(expected,result); + + // Allocation quality not degraded + SpillStats.checkSpills(spills,code._regAlloc._spillScaled); + } + + public static String gcc( String obj, String c_conv, String cfile, boolean stdin, String... args ) throws IOException { + + // Compile the C program. Compiling code and constants in the low + // 2Gig. Pointers are 64b BUT since always in the low 2G all the + // high bits are zero - and Simple code can be emitted treating + // pointers as 4bytes. + var params = new Ary<>(String.class); + params.add("gcc"); + if( cfile!=null ) params.add(cfile); // Associated C driver, usually has a `main` + params.addAll(new String[] { + obj, + "-lm", // Picks up 'sqrt' for newtonFloat tests to compare + "-g", + "-o", + args[0], + }); + // Calling convention for C calls, if any + if( cfile!=null ) { + params.add("-D"); + params.add("CALL_CONV="+c_conv); + } - // Compile the C program - var params = new String[] { - //if (USE_WSL) "wsl.exe"; - "gcc", - cfile, - obj, - "-lm", // Picks up 'sqrt' for newtonFloat tests to compare - "-g", - "-o", - bin, - "-D", - "CALL_CONV="+c_conv, - }; - Process gcc = new ProcessBuilder(params).redirectErrorStream(true).start(); - int error; - try { error = gcc.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); } + // Run GCC to link (optionally compile C driver code) + Process gcc = new ProcessBuilder(params.asAry()).redirectErrorStream(true).start(); + int exit; + try { + boolean normal = gcc.waitFor(2, TimeUnit.SECONDS); + exit = normal ? gcc.exitValue() : -1; // no exit??? + } catch( InterruptedException e ) { + throw new IOException("interrupted"); + } String result = new String(gcc.getInputStream().readAllBytes()); - if( error!=0 ) { - System.err.println("gcc error code: "+error); + if( exit!=0 ) { + System.err.println("gcc error code: "+exit); System.err.println(result); } - assertEquals( 0, error ); + assertEquals( 0, exit ); //assertTrue(result.isEmpty()); // No data in error stream // Execute results - Process smp = new ProcessBuilder(bin).redirectErrorStream(true).start(); - try { error = smp.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); } - result = new String(smp.getInputStream().readAllBytes()); - if( error!=0 ) { - System.err.println("exec error code: "+error); - System.err.println(result); - } - assertEquals( 0, error ); - assertEquals(expected,result); - - // Allocation quality not degraded - SpillStats.checkSpills(spills,code._regAlloc._spillScaled); - + ProcessBuilder smp = new ProcessBuilder(args); + if( stdin ) smp.redirectInput(ProcessBuilder.Redirect.INHERIT); + Process p = smp.start(); + try { exit = p.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); } + result = new String(p.getInputStream().readAllBytes()); + assertEquals("Program exit status",0,exit); + return result; } } diff --git a/src/test/java/com/seaofnodes/simple/TestRisc5.java b/src/test/java/com/seaofnodes/simple/TestRisc5.java index 1289575d..ace2a7ea 100644 --- a/src/test/java/com/seaofnodes/simple/TestRisc5.java +++ b/src/test/java/com/seaofnodes/simple/TestRisc5.java @@ -2,6 +2,7 @@ package com.seaofnodes.simple; import com.seaofnodes.isa.eval.EvalRisc5; +import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.Node; @@ -22,9 +23,8 @@ public abstract class TestRisc5 { public static EvalRisc5 build( String dir, String file, String main, int arg, int spills, boolean print ) throws IOException { // Compile and export Simple String src = Files.readString(Path.of(dir+"/"+file+".smp")); - CodeGen code = new CodeGen(src).driver("riscv", "SystemV",null); - com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code); - SpillStats.record(code,"Chapter21","riscv","SystemV"); + CodeGen code = new CheckedCodeGen(src).driver("riscv", "SystemV",null); + SpillStats.recordNative(code,"riscv","SystemV"); if( print ) { code.print_as_hex(); System.out.print(code.asm()); } // Allocation quality not degraded diff --git a/src/test/java/com/seaofnodes/simple/TypeTest.java b/src/test/java/com/seaofnodes/simple/TypeTest.java index dee033bc..551bddad 100644 --- a/src/test/java/com/seaofnodes/simple/TypeTest.java +++ b/src/test/java/com/seaofnodes/simple/TypeTest.java @@ -20,9 +20,9 @@ public class TypeTest { TypeStruct s2 = TypeStruct.make("s2", new Field[]{ Field.make("a", TypeInteger.BOT,-3, false), Field.make("b", TypeInteger.BOT,-4, false) }); - Assert.assertEquals(s1.makeRO(), s1.glb()); + Assert.assertEquals(s1.makeRO(), s1.glb(false)); Assert.assertNotEquals(s1, s1.dual()); - Assert.assertEquals(s1.makeRO(), s1.dual().glb()); + Assert.assertEquals(s1.makeRO(), s1.dual().glb(false)); TypeMem m1 = TypeMem.make(1,TypeNil.NIL); TypeMem m2 = TypeMem.make(2,TypeInteger.U16); @@ -38,9 +38,9 @@ public class TypeTest { Assert.assertEquals(TypeMem.make(0,Type.BOTTOM), m1.meet(m3)); Assert.assertEquals(TypeMem .BOT, m3.meet(m4)); - Assert.assertEquals(TypeMem.make(1,Type.BOTTOM), m1.glb()); + Assert.assertEquals(TypeMem.make(1,Type.BOTTOM), m1.glb(false)); Assert.assertEquals(TypeMem.make(1,Type.XNIL), m1.dual()); - Assert.assertEquals(m4.dual(), m4.glb().dual()); + Assert.assertEquals(m4.dual(), m4.glb(false).dual()); TypeMemPtr ptr1 = TypeMemPtr.make(s1); Assert.assertEquals(s1, ptr1._obj); @@ -55,11 +55,11 @@ public class TypeTest { Assert.assertEquals(s2, ptr2nil._obj); Assert.assertNotEquals(ptr1, ptr2); - Assert.assertNotEquals(ptr1, ptr1.glb()); - Assert.assertEquals(ptr1nil.makeFrom(s1.glb()).makeRO(), ptr1.glb()); + Assert.assertNotEquals(ptr1, ptr1.glb(false)); + Assert.assertEquals(ptr1nil.makeFrom(s1.glb(false)).makeRO(), ptr1.glb(false)); Assert.assertEquals(ptr1, ptr1.dual().dual()); - Assert.assertEquals(ptr1.glb().makeFrom(s1.dual().glb()), ptr1.dual().glb()); + Assert.assertEquals(ptr1.glb(false).makeFrom(s1.dual().glb(false)), ptr1.dual().glb(false)); Assert.assertEquals(TypeMemPtr.makeNullable(TypeStruct.BOT).withAccess(false), ptr1.meet(ptr2nil)); Assert.assertEquals(ptr1nil, ptr1.meet(TypeNil.NIL)); diff --git a/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java b/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java index 7054ade4..00d86c63 100644 --- a/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java +++ b/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java @@ -60,7 +60,7 @@ public class FlagSpillTestSupport { assertEquals(stack,slots!=0); code._regAlloc=alloc; - FunNode frame = new FunNode(null,null,(Node)null); + FunNode frame = new FunNode(null,null,"flags",(Node)null); Encoding save = new Encoding(code), restore = new Encoding(code); save._fun=restore._fun=frame; Encoding enc = save; diff --git a/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java b/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java index 06ed40d9..0895a4fc 100644 --- a/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java +++ b/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java @@ -123,6 +123,16 @@ public class RegAllocTestSupport { assertTrue(BuildLRG.run(0,new RegAlloc(code))); } + // Check the scheduled graph before encoding adds untyped branches or rewrites tail calls. + public static class CheckedCodeGen extends CodeGen { + public CheckedCodeGen(String src) { super(src); } + @Override public CodeGen regAlloc() { + super.regAlloc(); + checkRegisters(this); + return this; + } + } + public static void topPhi() { // Either arm can be the synthetic exit. Both predecessors share a // block here so making TOP live would conflict with the real value. diff --git a/src/test/java/com/seaofnodes/simple/codegen/X86EncodingTest.java b/src/test/java/com/seaofnodes/simple/codegen/X86EncodingTest.java index 368c243b..0914d916 100644 --- a/src/test/java/com/seaofnodes/simple/codegen/X86EncodingTest.java +++ b/src/test/java/com/seaofnodes/simple/codegen/X86EncodingTest.java @@ -3,6 +3,7 @@ package com.seaofnodes.simple.codegen; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.cpus.x86_64_v2.CmpMemX86; import com.seaofnodes.simple.node.cpus.x86_64_v2.NotX86; +import com.seaofnodes.simple.node.cpus.x86_64_v2.MulIX86; import org.junit.Test; import static org.junit.Assert.*; @@ -11,7 +12,7 @@ public class X86EncodingTest { @Test public void testLogicalNotRegisters() { CodeGen code = new CodeGen("return !arg;") - .driver(CodeGen.Phase.InstSelect, "x86_64_v2", "SystemV"); + .driver(CodeGen.Phase.Select, "x86_64_v2", "SystemV"); var not = code._stop.walk(n -> n instanceof NotX86 ? (NotX86)n : null); assertNotNull("Must exercise logical not", not); int[][] registers = {{0,0}, {1,1}, {7,7}, {9,9}, {1,9}, {9,1}, {6,0}, {15,8}}; @@ -50,6 +51,31 @@ public class X86EncodingTest { """); com.seaofnodes.simple.TestC.run("build/objs", "x86LogicalNot", "", -1); } + + @Test + public void testMultiplyImmediateRegisters() { + // Destination, source, expected REX and ModRM. Cross the register-8 + // boundary in both directions, with low/low and high/high controls. + int[][] cases = {{1, 9, 0x49, 0xC9}, {9, 1, 0x4C, 0xC9}, + {1, 2, 0x48, 0xCA}, {9, 10, 0x4D, 0xCA}}; + for (int immediate : new int[]{11, -11, 123456789, -123456789}) { + CodeGen code = new CodeGen("return arg * " + immediate + ";") + .driver(CodeGen.Phase.Select, "x86_64_v2", "SystemV"); + var mul = code._stop.walk(n -> n instanceof MulIX86 ? n : null); + assertNotNull("Must exercise immediate multiply", mul); + for (int[] regs : cases) { + var enc = new FixedRegisterEncoding( + code, mul, regs[0], mul.in(1), regs[1]); + ((MulIX86)mul).encoding(enc); + boolean small = immediate == 11 || immediate == -11; + assertEquals("REX for dst=" + regs[0] + ", src=" + regs[1], regs[2], enc.read1(0)); + assertEquals(small ? 0x6B : 0x69, enc.read1(1)); + assertEquals(regs[3], enc.read1(2)); + assertEquals(small ? 4 : 7, enc._bits.size()); + assertEquals(immediate, small ? (byte)enc.read1(3) : enc.read4(3)); + } + } + } @Test public void testMemoryCompare64Immediate32() { for (int immediate : new int[]{123456789, -123456789}) { diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 7e8bdbf1..08e19fd4 100644 --- a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -133,7 +133,7 @@ public class Fuzzer { double parse_nop_ratio = (double)parse_nops/parse_peeps; double parse_peeps_per_node = (double)parse_peeps/nids; - code.iter(); + code.iter().finalizeIR(); int iter_peeps= code._iter_cnt; int iter_nops = code._iter_nop_cnt; double iter_nop_ratio = (double)iter_nops/iter_peeps; diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java index 167a5b24..79547c0d 100644 --- a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java +++ b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java @@ -97,7 +97,7 @@ public class ScriptGenerator { private static final List<TypeInt> INTTYPES = new ArrayList<>(); static { - for( var e : Parser.defaultTypes().entrySet() ) { + for( var e : Parser.TYPES.entrySet() ) { if( e.getValue() instanceof TypeInteger ) { INTTYPES.add(new TypeInt(e.getKey())); } diff --git a/src/test/java/com/seaofnodes/simple/progs/c_returns.c b/src/test/java/com/seaofnodes/simple/progs/c_returns.c new file mode 100644 index 00000000..6d0ff2fe --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/c_returns.c @@ -0,0 +1,28 @@ +#include <stdint.h> +#include <stdio.h> + +uint64_t c_bits; +// Legal narrow C returns: unused RAX bits deliberately contain garbage. +#define RET(name) __asm__(".text\n.globl " #name "\n" #name ":\nmovq c_bits(%rip), %rax\nret\n"); +RET(c_i8) RET(c_u8) RET(c_i16) RET(c_u16) RET(c_i32) RET(c_u32) RET(c_i64) +#define DECL(name) extern int64_t __attribute__((CALL_CONV)) name(void); +DECL(test_i8) DECL(test_u8) DECL(test_i16) DECL(test_u16) +DECL(test_i32) DECL(test_u32) DECL(test_i64) DECL(test_cmp) + +int main(void) { + uint64_t values[] = {0xa5a5a5a500000000ULL, 0xa5a5a5a50000007fULL, + 0xa5a5a5a500000080ULL, 0xa5a5a5a500007fffULL, 0xa5a5a5a500008000ULL, + 0xa5a5a5a57fffffffULL, 0xa5a5a5a580000000ULL, 0xa5a5a5a5ffffffffULL}; + for(unsigned i=0; i<sizeof(values)/sizeof(values[0]); i++) { + c_bits=values[i]; + int64_t actual[]={test_i8(),test_u8(),test_i16(),test_u16(),test_i32(),test_u32(),test_i64(),test_cmp()}; + int64_t expected[]={(int8_t)c_bits,(uint8_t)c_bits,(int16_t)c_bits,(uint16_t)c_bits, + (int32_t)c_bits,(uint32_t)c_bits,(int64_t)c_bits,(int32_t)c_bits==-1}; + for(unsigned j=0; j<8; j++) + if(actual[j]!=expected[j]) { + printf("case %u type %u: %lld != %lld\n",i,j,(long long)actual[j],(long long)expected[j]); + return 1; + } + } + return 0; +} diff --git a/src/test/java/com/seaofnodes/simple/progs/echo.smp b/src/test/java/com/seaofnodes/simple/progs/echo.smp new file mode 100644 index 00000000..8eafd73b --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/echo.smp @@ -0,0 +1,3 @@ +// -*- mode: java; -*- +// Echo stdin to stdout. +return sys.io.p( sys.io.stdin() ); diff --git a/src/test/java/com/seaofnodes/simple/progs/funptr.c b/src/test/java/com/seaofnodes/simple/progs/funptr.c new file mode 100644 index 00000000..7545992d --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/funptr.c @@ -0,0 +1,15 @@ +#include <assert.h> +#include <stdint.h> + +typedef int64_t (*Function)(void); +typedef int64_t (*Printer)(void *); +extern Function factory(int64_t); +extern Printer printer(void); + +int main(void) { + assert(factory(1)() == 42); + assert(factory(0)() == 43); + struct { uint32_t length; char data[2]; } text = {2, {'o','k'}}; + printer()(&text); + return 0; +} diff --git a/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp b/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp new file mode 100644 index 00000000..07ff935f --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp @@ -0,0 +1,3 @@ +// -*- mode: java; -*- +sys.io.p("Hello, World!"); +return 0; diff --git a/src/test/java/com/seaofnodes/simple/progs/jig.smp b/src/test/java/com/seaofnodes/simple/progs/jig.smp index 1daeee09..29987d8e 100644 --- a/src/test/java/com/seaofnodes/simple/progs/jig.smp +++ b/src/test/java/com/seaofnodes/simple/progs/jig.smp @@ -1,2 +1,16 @@ // -*- mode: java; -*- -return 0; +val _fib = {int n -> + int temp=0; + int f1=1; + int f2=1; + int i=n; + while( i>1 ){ + temp = f1+f2; + f1=f2; + f2=temp; + i=i-1; + } + return f2; +}; + +_fib(10); diff --git a/src/test/java/com/seaofnodes/simple/progs/person.smp b/src/test/java/com/seaofnodes/simple/progs/person.smp index b5e6ee90..94e2cdde 100644 --- a/src/test/java/com/seaofnodes/simple/progs/person.smp +++ b/src/test/java/com/seaofnodes/simple/progs/person.smp @@ -1,6 +1,6 @@ // -*-java-*- struct Person { - int age; + i32 age; }; val fcn = { !Person?[] ps, int x -> diff --git a/src/test/java/com/seaofnodes/simple/progs/person21.c b/src/test/java/com/seaofnodes/simple/progs/person21.c new file mode 100644 index 00000000..df95e1ca --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/person21.c @@ -0,0 +1,34 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> + +typedef struct Person { + int age; +} Person; + +typedef struct PersonArray { + unsigned int length; + int pad; + Person* data[]; +} PersonArray; + +__attribute__ ((CALL_CONV)) +extern void fcn(PersonArray* people, int index); + +int main() { + Person base[5] = {{25},{1},{18},{45},{60}}; + unsigned char buf[8+8*5]; + PersonArray* people_array = (PersonArray*)buf; + people_array->length=5; + for (int i=0; i<people_array->length; i++) people_array->data[i] = &base[i]; + + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + + printf("%d\n", people_array->data[1]->age); // expected: 6 + + return 0; +}
\ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/person21.smp b/src/test/java/com/seaofnodes/simple/progs/person21.smp new file mode 100644 index 00000000..b5e6ee90 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/person21.smp @@ -0,0 +1,9 @@ +// -*-java-*- +struct Person { + int age; +}; + +val fcn = { !Person?[] ps, int x -> + if( ps[x] ) + ps[x].age++; +};
\ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/riscv_load.smp b/src/test/java/com/seaofnodes/simple/progs/riscv_load.smp new file mode 100644 index 00000000..1a36fb99 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/riscv_load.smp @@ -0,0 +1,4 @@ +struct Person { i32 age;}; +!Person !p = new Person; +p.age += arg; +return p.age; diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp new file mode 100644 index 00000000..57b37477 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp @@ -0,0 +1,4 @@ +struct Person {i64 age;} +!Person !p = new Person; +p.age += arg; +return p.age; diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp new file mode 100644 index 00000000..7d7fdce7 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp @@ -0,0 +1,5 @@ +// Should fold away sign extend +struct Person { i8 age;}; +!Person !p = new Person; +p.age = (arg<<48)>>48; +return 0;
\ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp new file mode 100644 index 00000000..4d6ca3a3 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp @@ -0,0 +1,4 @@ +struct Person { i32 age;}; +!Person !p = new Person; +p.age = (arg<<17)>>17; +return 0;
\ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp new file mode 100644 index 00000000..5c96c060 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp @@ -0,0 +1,4 @@ +struct Person { i32 age;}; +!Person !p = new Person; +p.age = (arg<<48)>>48; +return 0;
\ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/store_peep.smp b/src/test/java/com/seaofnodes/simple/progs/store_peep.smp new file mode 100644 index 00000000..c3feddd5 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/store_peep.smp @@ -0,0 +1,3 @@ +i64 a = 123456789012345; +i32 b = a; // truncate high order bits +return b;
\ No newline at end of file |
