summaryrefslogtreecommitdiff
path: root/src/test/java/com/seaofnodes/simple
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/com/seaofnodes/simple')
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter09Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter17aTest.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter17bTest.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter18Test.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter19Test.java7
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter20Test.java3
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter23Test.java24
-rw-r--r--src/test/java/com/seaofnodes/simple/ConstructionTest.java98
-rw-r--r--src/test/java/com/seaofnodes/simple/Eval2.java3
-rw-r--r--src/test/java/com/seaofnodes/simple/FinalConstructionTest.java63
-rw-r--r--src/test/java/com/seaofnodes/simple/MergeSortTest.java10
-rw-r--r--src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java1
12 files changed, 192 insertions, 29 deletions
diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index 50410618..f24d96eb 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -255,7 +255,7 @@ return 0!=0;
int v0=0;
while(0==1) while(v0)
v0=1+v0;
-return 0;
+return 0;
""");
code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
diff --git a/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
index d03248c2..64ecb235 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
@@ -73,7 +73,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).driver(CodeGen.Phase.FinalizeIR); fail(fields); }
+ try { new CodeGen(src).driver(CodeGen.Phase.TypeCheck); fail(fields); }
catch( Exception e ) { assertEquals("'Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); }
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index d2fa40f3..3fbf0daa 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -289,7 +289,7 @@ A x = new A {
b = new B;
};
""");
- try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot read uninitialized field 'b'",e.getMessage()); }
}
@@ -302,7 +302,7 @@ return new A {
if (arg) b = new B; // Constructor ends with partial init of b
}.b;
""");
- try { code.parse(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("'A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); }
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 4927f7e2..a8cf3f11 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -223,7 +223,7 @@ var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return #2; return (Parm_x($fun21,i64)*x); return (Parm_x($fun22,i64)<<1); ]", code._stop.toString());
+ assertEquals("Stop[ return #2; return (Parm_x($fun35,i64)*x); return (Parm_x($fun36,i64)<<1); ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
}
@@ -359,7 +359,7 @@ for(;;) {
return 0;
""");
try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Might be null calling { i64 -> i64 #21}?",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Might be null calling { i64 -> i64 #35}?",e.getMessage()); }
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index 41ec6cef..88155e5b 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -15,7 +15,7 @@ import static org.junit.Assert.*;
public class Chapter19Test {
@Test public void testPrintingFunctionLookup() {
var code = new CodeGen("return 0;").parse();
- var tfp = TypeFunPtr.make((byte)2,false,new Type[]{TypeInteger.constant(987654)},TypeInteger.constant(123456),1L<<30);
+ var tfp = TypeFunPtr.make((byte)2,false,new Type[]{TypeInteger.constant(987654)},TypeInteger.constant(123456),XInt.make(1000));
var con = new ConstantNode(tfp);
var call = new CallNode(null,code._start,con,con);
// Diagnostic lookup may construct/intern the return-erased linker key.
@@ -415,7 +415,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($fun21,i64),x); return (shli,Parm_x($fun22,i64)); ]", code.print());
+ assertEquals("Stop[ return (add,#2,(muli,#2)); return (mul,Parm_x($fun35,i64),x); return (shli,Parm_x($fun36,i64)); ]", code.print());
}
@Test
@@ -443,8 +443,7 @@ return sq(arg) + sq(3);
assertTrue(n.in(0) instanceof CFGNode);
if( n instanceof NewNode nn ) {
assertEquals(2,((TypeTuple)nn._type)._types.length);
- assertTrue(nn.mem() instanceof MemMergeNode);
- assertNull(nn.mem().in(1)); // Partial allocation input.
+ assertNull(nn.mem()); // The allocation starts with private memory.
nn.cacheRegs(code);
MachNode mach = (MachNode)nn;
assertNull(mach.regmap(1));
diff --git a/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index fe69e5ad..4800b180 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -199,7 +199,8 @@ 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",5,"return .[];");
+ // Array publication changes memory scheduling and the live ranges.
+ testTarget(src,"x86_64_v2", "SystemV",7,"return .[];");
testTarget(src,"riscv" , "SystemV",7,"return (add,.[],(mul,.[],1000));");
testTarget(src,"arm" , "SystemV",5,"return (add,.[],(mul,.[],1000));");
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/src/test/java/com/seaofnodes/simple/Chapter23Test.java
index 1dd21d08..22be3af1 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter23Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter23Test.java
@@ -115,7 +115,7 @@ var apply = { i64 x, { i64 -> u32 } fcn ->
"""
int a = 1;
int b = 0;
-
+
if(a++ || b++ ) {
if(b == 0 && a == 2) {
sys.io.p("Or");
@@ -149,7 +149,7 @@ var apply = { i64 x, { i64 -> u32 } fcn ->
"""
int a = 1;
int b = 1;
-
+
if(a && b) {
sys.io.p("And");
} else {
@@ -186,14 +186,14 @@ var apply = { i64 x, { i64 -> u32 } fcn ->
String src2 =
"""
struct S { !S? !fld; };
-
+
val ptr = arg == 1 ? null : new S{fld = arg==1 ? null : new S{fld = null;};};
if( ptr && ptr.fld ) {
sys.io.p("true");
} else {
sys.io.p("false");
}
-
+
return 0;
""";
CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
@@ -221,13 +221,13 @@ var apply = { i64 x, { i64 -> u32 } fcn ->
"""
int a = 1;
int b = 1;
-
+
int x=1;
int y=1;
int z=0;
-
+
int g = x++ && y++ && z++;
-
+
if(x == 2 && y == 2 && z == 1 && g == 0) {
sys.io.p("Effected");
} else {
@@ -259,13 +259,13 @@ var apply = { i64 x, { i64 -> u32 } fcn ->
"""
int a = 1;
int b = 1;
-
+
int x = -1;
int y=1;
int z= -1;
-
+
int g = x++ || y++ || z++;
-
+
int switch = 0;
if(x == 4 || y == 4 || z == 4) {
switch = -1;
@@ -305,11 +305,11 @@ var apply = { i64 x, { i64 -> u32 } fcn ->
// -*- mode: java; -*-
int a = 1;
int b = 1;
-
+
var sq = { int x ->
x*x;
};
-
+
if(a && sq(0)) {
sys.io.p("And");
} else {
diff --git a/src/test/java/com/seaofnodes/simple/ConstructionTest.java b/src/test/java/com/seaofnodes/simple/ConstructionTest.java
new file mode 100644
index 00000000..dd20b746
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/ConstructionTest.java
@@ -0,0 +1,98 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.isa.eval.EvalArm64;
+import com.seaofnodes.isa.eval.EvalRisc5;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class ConstructionTest {
+ private static final String CONSTRUCT = """
+ struct Box {
+ int x=7;
+ int[] data;
+ new Box = { int n ->
+ data=new int[2];
+ x=n;
+ data[0]=x+1;
+ };
+ val get = { -> x+data[0]; };
+ };
+ val a=new Box(arg);
+ val b=new Box(arg+10);
+ return a.get()*100+b.get();
+ """;
+
+ @Test public void testConstructorCalls() throws java.io.IOException {
+ var code=new CodeGen(CONSTRUCT).driver(CodeGen.Phase.FinalizeIR);
+ assertEquals("525",Eval2.eval(code,2));
+ for( String cpu : new String[]{"arm","riscv"} ) {
+ code=new CodeGen(CONSTRUCT).driver(cpu,"SystemV",null);
+ byte[] memory=new byte[1<<20];
+ byte[] bits=code._encoding.bits();
+ System.arraycopy(bits,0,memory,0,bits.length);
+ if( cpu.equals("arm") ) {
+ EvalArm64 eval=new EvalArm64(memory,1<<16);
+ eval.regs[0]=2;
+ assertEquals(0,eval.step(10000));
+ assertEquals(525,eval.regs[0]);
+ } else {
+ EvalRisc5 eval=new EvalRisc5(memory,1<<16);
+ eval.regs[10]=2;
+ assertEquals(0,eval.step(10000));
+ assertEquals(525,eval.regs[10]);
+ }
+ }
+ // Field updates are local SSA values, including the old x used after
+ // updating y and x. Only constructor exits should write these fields.
+ String counter="struct Counter { int x; int y; int z; new Counter={int n -> while(x<n) { int old=x; y=x+1; x=y+1; z=old; }}; }; val c=new Counter(arg); return c.x*100+c.z;";
+ var loop=new CodeGen(counter)
+ .driver(CodeGen.Phase.LocalSched);
+ assertEquals("0",Eval2.eval(loop,0));
+ assertEquals("2018",Eval2.eval(loop,19));
+ loop._stop.walk(n -> {
+ if( n instanceof com.seaofnodes.simple.node.StoreNode st )
+ assertEquals("Constructor Stores belong outside the loop",0,st.cfg0().loopDepth());
+ return null;
+ });
+ var defaults=new CodeGen("struct Buffer { int[] data=new int[1]; }; val a=new Buffer; val b=new Buffer; a.data[0]=7; return b.data[0];")
+ .driver(CodeGen.Phase.TypeCheck);
+ assertEquals("0",Eval2.eval(defaults,0));
+ java.nio.file.Path dir=java.nio.file.Path.of("build/objs");
+ java.nio.file.Files.createDirectories(dir);
+ java.nio.file.Files.writeString(dir.resolve("construction.smp"),"val construct={int arg ->"+CONSTRUCT+"}; val count={int arg ->"+counter+"};");
+ java.nio.file.Files.writeString(dir.resolve("construction.c"),"""
+ extern long long construct(long long);
+ extern long long count(long long);
+ int main(void) { return construct(2)!=525 || construct(0)!=121 || count(19)!=2018 || count(0)!=0; }
+ """);
+ TestC.run("build/objs","construction","",-1);
+ }
+
+ @Test public void testCompletionBeforePublication() {
+ String src="struct Box { int[] data; new Box={int n -> if(n) data=new int[2];}; }; return new Box(arg);";
+ try { new CodeGen(src).driver(CodeGen.Phase.TypeCheck); fail(); }
+ catch( Parser.ParseException e ) {
+ assertEquals("'Box' is not fully initialized, field 'data' needs to be set in a constructor",e.getMessage());
+ }
+ for( String early : new String[]{
+ "val use={!Box b -> b.x;}; struct Box { int x; new Box={->use(self);}; }; return new Box;",
+ "struct Box { Box? next; new Box={->next=self;}; }; return new Box;",
+ "struct Wrap { Box? item; }; struct Box { new Box={->new Wrap { item=self; };}; }; return new Box;"
+ } ) {
+ try { new CodeGen(early).driver(CodeGen.Phase.TypeCheck); fail(); }
+ catch( Parser.ParseException e ) {
+ assertEquals("Cannot publish self before the constructor finishes",e.getMessage());
+ }
+ }
+ }
+
+ @Test public void testForwardMethod() {
+ var code=new CodeGen("val run={!Box b -> b.get();}; struct Box { int x=7; val get={->x;}; }; return run(new Box);")
+ .driver(CodeGen.Phase.FinalizeIR);
+ assertEquals("7",Eval2.eval(code,0));
+ code=new CodeGen("struct Byte { new Byte={int n -> self.x=n; self.x+=3;}; u8 x; }; return new Byte(255).x;")
+ .driver(CodeGen.Phase.FinalizeIR);
+ assertEquals("2",Eval2.eval(code,0));
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/Eval2.java b/src/test/java/com/seaofnodes/simple/Eval2.java
index 58b3b74e..b32a5175 100644
--- a/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -278,7 +278,8 @@ public abstract class Eval2 {
case ProjNode proj -> proj._type instanceof TypeMem ? "$mem" : val(proj.in(0));
case ReadOnlyNode read -> val(read.in(1));
case SarNode sar -> x(sar.in(1)) >> x(sar.in(2));
- case MemMergeNode merge-> "$mem";
+ case EscapeNode esc -> "$mem";
+ case MemMergeNode merge-> "$mem";
case ShlNode shl -> x(shl.in(1)) << x(shl.in(2));
case ShrNode shr -> x(shr.in(1)) >>>x(shr.in(2));
case StoreNode st -> store(st);
diff --git a/src/test/java/com/seaofnodes/simple/FinalConstructionTest.java b/src/test/java/com/seaofnodes/simple/FinalConstructionTest.java
new file mode 100644
index 00000000..e91dc384
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/FinalConstructionTest.java
@@ -0,0 +1,63 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.StoreNode;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class FinalConstructionTest {
+ private static final String LOOP = """
+ val step={int x -> x*2+1;};
+ struct C {
+ val x=1;
+ new C={int n ->
+ x=0; x=1;
+ for(int i=0; i<n; i++) x=step(x);
+ };
+ val get={->x;};
+ };
+ return new C(arg).get();
+ """;
+
+ @Test public void testFinalConstructorLocals() throws java.io.IOException {
+ var code=new CodeGen(LOOP).driver(CodeGen.Phase.LocalSched);
+ assertEquals("1",Eval2.eval(code,0));
+ assertEquals("15",Eval2.eval(code,3));
+ assertEquals("63",Eval2.eval(code,5));
+ code._stop.walk(n -> {
+ if( n instanceof StoreNode st && st._name.equals("x") )
+ assertEquals("Final field writes belong after the loop",0,st.cfg0().loopDepth());
+ return null;
+ });
+ String[] sources={
+ "struct C { val x=1; new C={->x=1; x=2;}; }; return new C().x;",
+ "struct C { int ~x; new C={int n -> self.x=1; for(int i=0;i<n;i++) self.x*=2;}; }; return new C(arg).x;",
+ "struct C { val x=1; new C={int n -> for(int i=0;i<n;i++) ++x;}; }; return new C(arg).x;",
+ "struct C { val x=1.5; new C={->x=2.5;}; }; return new C().x;",
+ "struct C { val x=\"a\"; new C={int n -> for(int i=0;i<n;i++) x=\"b\";}; }; return new C(arg).x[0];",
+ "struct C { val x=new int[1]; new C={->x=new int[2]; x=new int[3];}; }; return new C().x#;"
+ };
+ String[] results={"2","8","4","2.5","98","3"};
+ for( int i=0; i<sources.length; i++ )
+ assertEquals(results[i],Eval2.eval(new CodeGen(sources[i]).driver(CodeGen.Phase.TypeCheck),3));
+
+ String[] rejected={
+ "struct C { val x=1; }; val c=new C(); c.x=2; return c.x;",
+ "struct C { new C={->val x=1; x=2;}; }; return new C();",
+ "struct C { val f={->1;}; new C={->f={->2;};}; }; return new C().f();"
+ };
+ for( String src : rejected ) {
+ try { new CodeGen(src).driver(CodeGen.Phase.TypeCheck); fail(src); }
+ catch( Parser.ParseException e ) { assertTrue(e.getMessage(),e.getMessage().startsWith("Cannot")); }
+ }
+
+ var dir=java.nio.file.Path.of("build/objs");
+ java.nio.file.Files.createDirectories(dir);
+ java.nio.file.Files.writeString(dir.resolve("finalConstruction.smp"),"val buildFinal={int arg ->"+LOOP+"};");
+ java.nio.file.Files.writeString(dir.resolve("finalConstruction.c"),"""
+ extern long long buildFinal(long long);
+ int main(void) { return buildFinal(0)!=1 || buildFinal(3)!=15 || buildFinal(5)!=63; }
+ """);
+ TestC.run("build/objs","finalConstruction","",-1);
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/src/test/java/com/seaofnodes/simple/MergeSortTest.java
index 324790a0..453bd35c 100644
--- a/src/test/java/com/seaofnodes/simple/MergeSortTest.java
+++ b/src/test/java/com/seaofnodes/simple/MergeSortTest.java
@@ -17,13 +17,13 @@ public class MergeSortTest {
@Test public void testMergeSort0() throws IOException {
// Test merge sort on a small array of primes
String src =
-"""
+"""
val merge_sort = { int[] a ->
int[] !b = new int[a#];
copy_array (a, 0, a#, b);
split_merge(a, 0, a#, b);
};
-
+
val split_merge = { int[] !b, int begin, int end, int[] a ->
if (end - begin <= 1)
return 0;
@@ -33,10 +33,10 @@ public class MergeSortTest {
merge(b, begin, middle, end, a);
return 0;
};
-
+
val merge = { int[] !b, int begin, int middle, int end, int[] a ->
int i = begin, j = middle;
-
+
for (int k = begin; k < end; k++) {
// && and ||
bool cond = false;
@@ -48,7 +48,7 @@ public class MergeSortTest {
else b[k] = a[j++];
}
};
-
+
val copy_array = { int[] !a, int begin, int end, int[] b ->
for (int k = begin; k < end; k++)
b[k] = a[k];
diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 20aac367..3f4613ff 100644
--- a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -306,6 +306,7 @@ public class Evaluator {
case NewNode alloc -> alloc(alloc);
case CProjNode cproj -> ((Object[])val(cproj.ctrl()))[cproj._idx];
case ProjNode proj -> ((Object[])val( proj.in(0) ))[ proj._idx];
+ case EscapeNode esc -> null;
case MemMergeNode mem -> null;
case ReadOnlyNode ro -> val(ro.in(1));
case CallNode call -> Utils.TODO(); // should not reach here, go the IfNode route