summaryrefslogtreecommitdiff
path: root/src/test/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/com')
-rw-r--r--src/test/java/com/seaofnodes/simple/BrainFuckTest.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter01Test.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter05Test.java16
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter06Test.java32
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter07Test.java14
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter08Test.java22
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter09Test.java51
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter10Test.java58
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter11Test.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter12Test.java35
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter13Test.java26
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java14
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter14Test.java32
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter15Test.java48
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter16Test.java18
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter17aTest.java16
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter17bTest.java92
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter18Test.java49
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter19Test.java13
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter20Test.java35
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter21AllocTest.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter21Test.java91
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter22Test.java411
-rw-r--r--src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Eval2.java18
-rw-r--r--src/test/java/com/seaofnodes/simple/LiteralTest.java65
-rw-r--r--src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java10
-rw-r--r--src/test/java/com/seaofnodes/simple/MutabilityTest.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/PhaseTest.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/SchedulerTest.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/Simple.java171
-rw-r--r--src/test/java/com/seaofnodes/simple/SpillStats.java12
-rw-r--r--src/test/java/com/seaofnodes/simple/TestArm64.java7
-rw-r--r--src/test/java/com/seaofnodes/simple/TestC.java98
-rw-r--r--src/test/java/com/seaofnodes/simple/TestRisc5.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/TypeTest.java14
-rw-r--r--src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java10
-rw-r--r--src/test/java/com/seaofnodes/simple/codegen/X86EncodingTest.java28
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/c_returns.c28
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/echo.smp3
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/funptr.c15
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/helloWorld.smp3
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/jig.smp16
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/person.smp2
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/person21.c34
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/person21.smp9
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/riscv_load.smp4
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp4
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp5
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp4
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp4
-rw-r--r--src/test/java/com/seaofnodes/simple/progs/store_peep.smp3
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