summaryrefslogtreecommitdiff
path: root/src/test/java/com/seaofnodes/simple/Chapter18Test.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/com/seaofnodes/simple/Chapter18Test.java')
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter18Test.java328
1 files changed, 328 insertions, 0 deletions
diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
new file mode 100644
index 00000000..1d7abde5
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -0,0 +1,328 @@
+package com.seaofnodes.simple;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+import static org.junit.Assert.fail;
+import org.junit.Ignore;
+
+public class Chapter18Test {
+
+ @Test
+ public void testJig() {
+ CodeGen code = new CodeGen(
+"""
+return 0;
+""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("return 0;", code._stop.toString());
+ assertEquals("0", Eval2.eval(code, 2));
+ }
+
+ @Test
+ public void testPhiParalleAssign() {
+ CodeGen code = new CodeGen(
+"""
+int a = 1;
+int b = 2;
+while(arg--) {
+ int t = a;
+ a = b;
+ b = t;
+}
+return a;
+""");
+ code.parse().opto().typeCheck().GCM();
+ 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));
+ assertEquals("1", Eval2.eval(code, 2));
+ assertEquals("2", Eval2.eval(code, 3));
+ }
+
+
+ // ---------------------------------------------------------------
+ @Test
+ public void testType0() {
+ CodeGen code = new CodeGen(
+"""
+{int -> int}? x2 = null; // null function ptr
+return x2;
+""");
+ code.parse().opto();
+ assertEquals("return null;", code._stop.toString());
+ assertEquals("null", Eval2.eval(code, 0 ) );
+ }
+
+ @Test
+ public void testFcn0() {
+ CodeGen code = new CodeGen(
+"""
+{int -> int}? sq = { int x ->
+ x*x;
+};
+return sq;
+""");
+ code.parse().opto();
+ assertEquals("Stop[ return { sq}; return (Parm_x(sq,int)*x); ]", code._stop.toString());
+ assertEquals("{ sq}", Eval2.eval(code, 3));
+ }
+
+ @Test
+ public void testFcn1() {
+ CodeGen code = new CodeGen(
+"""
+var sq = { int x ->
+ x*x;
+};
+return sq(arg)+sq(3);
+""");
+ code.parse().opto();
+ assertEquals("Stop[ return (sq( 3)+sq( arg)); return (Parm_x(sq,int,3,arg)*x); ]", code._stop.toString());
+ assertEquals("13", Eval2.eval(code, 2));
+ }
+
+ // Function scope test
+ @Test
+ public void testFcn2() {
+ CodeGen code = new CodeGen(
+"""
+int cnt=1;
+return { -> cnt; };
+""");
+ try { code.parse().opto(); fail(); }
+ catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); }
+ }
+
+ // Function scope test
+ @Test
+ public void testFcn3() {
+ CodeGen code = new CodeGen(
+"""
+val cnt=2;
+return { -> cnt; }();
+""");
+ code.parse().opto();
+ assertEquals("return 2;", code._stop.toString());
+ assertEquals("2", Eval2.eval(code, 0));
+ }
+
+ // Function variables
+ @Test
+ public void testFcn4() {
+ CodeGen code = new CodeGen(
+"""
+var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
+return fcn(3);
+""");
+ code.parse().opto();
+ assertEquals("Stop[ return Phi(Region,{ int -> int #1},{ int -> int #2})( 3); return (Parm_x($fun,int,3)*x); return (Parm_x($fun,int,3)*2); ]", code._stop.toString());
+ assertEquals("6", Eval2.eval(code, 0));
+ assertEquals("9", Eval2.eval(code, 1));
+ }
+
+ // Recursive factorial test
+ @Test
+ public void testFcn5() {
+ CodeGen code = new CodeGen("val fact = { int x -> x <= 1 ? 1 : x*fact(x-1); }; return fact(arg);");
+ code.parse().opto();
+ assertEquals("Stop[ return fact( arg); return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*fact( Sub))); ]", code._stop.toString());
+ assertEquals( "1", Eval2.eval(code, 0));
+ assertEquals( "1", Eval2.eval(code, 1));
+ assertEquals( "2", Eval2.eval(code, 2));
+ assertEquals( "6", Eval2.eval(code, 3));
+ assertEquals("24", Eval2.eval(code, 4));
+ }
+
+ @Test
+ public void testFcn6() {
+ CodeGen code = new CodeGen(
+"""
+struct S { int i; };
+val newS = { int x -> return new S { i=x; }; };
+return newS(1).i;
+""");
+ code.parse().opto().typeCheck().GCM();
+ assertEquals("return 1;", code._stop.toString());
+ assertEquals("1", Eval2.eval(code, 0));
+ }
+
+ // Double forward reference
+ @Test
+ public void testFcn7() {
+ CodeGen code = new CodeGen(
+"""
+if( arg ? f : g ) return 1;
+val f = {->1;};
+val g = {->2;};
+return 2;
+""");
+ code.parse().opto().typeCheck().GCM();
+ assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString());
+ assertEquals("1", Eval2.eval(code, 0));
+ }
+
+ @Test
+ public void testFcn8() {
+ CodeGen code = new CodeGen(
+"""
+{int -> int}? i2i = null;
+var id = {{int->int} f-> return f;};
+for(;;) {
+ if (i2i) return i2i(arg);
+ var x = {int i-> return i;};
+ arg = x(3);
+ i2i = id(x);
+}
+""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("Stop[ return x( Phi(Loop,arg,x( 3))); return Parm_i(x,int,3,Phi_arg); ]", code._stop.toString());
+ assertEquals("3", Eval2.eval(code, 0));
+ }
+
+
+ @Test
+ public void testFcn9() {
+ CodeGen code = new CodeGen(
+"""
+{int -> int}? i2i = null;
+for(;;) {
+ if (i2i) return i2i(arg);
+ var x = {int i-> return i;};
+ arg = x(3);
+}
+""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("return Top;", code._stop.toString());
+ assertEquals(null, Eval2.eval(code, 0));
+ }
+
+ // Function break
+ @Test
+ public void testErr1() {
+ CodeGen code = new CodeGen(
+"""
+for(;;) {
+ val f = { ->
+ break;
+ };
+ f();
+ return 2;
+}
+return 1;
+""");
+ try { code.parse().opto(); fail(); }
+ catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); }
+ }
+
+ // Calling and inlining a null function
+ @Test
+ public void testErr2() {
+ CodeGen code = new CodeGen(
+"""
+{int -> int}? !i2i = { int i -> return i; };
+for(;;) {
+ if (i2i(2) == arg) break;
+ i2i = null;
+}
+""");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Might be null calling { int -> int #1}?",e.getMessage()); }
+ }
+
+
+ @Test
+ public void testErr3() {
+ CodeGen code = new CodeGen(
+"""
+val f = { int i, int j -> return i+j; };
+return f();
+""");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Expecting 2 arguments, but found 0",e.getMessage()); }
+ }
+
+
+ @Test
+ public void testErr4() {
+ CodeGen code = new CodeGen(
+"""
+struct S {
+ {int} f = { -> x(); return 0; }; // Do not let fref x be a field
+};
+val x = { -> return 1; };
+S? s = null;
+for(;;) {
+ if (s) return s.x;
+}
+""");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*S'",e.getMessage()); }
+ }
+
+ @Test
+ public void testErr5() {
+ CodeGen code = new CodeGen(
+"""
+val g = { ->
+ g();
+};
+return 0;
+""");
+ try { code.parse().opto().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); }
+ }
+
+
+ // Mutual recursion. Fails without SCCP to lift the recursive return types.
+ @Ignore @Test
+ public void testFcnMutRec() {
+ CodeGen code = new CodeGen(
+"""
+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().opto();
+ 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));
+ assertEquals("1", Eval2.eval(code, 2));
+ assertEquals("0", Eval2.eval(code, 3));
+ }
+
+ // Forward ref to 'x' means that
+ @Ignore @Test
+ public void testForwardRef1() {
+ CodeGen code = new CodeGen(
+"""
+struct S {
+ { int } f = { -> return x(); };
+};
+val x = { -> return 1; };
+S? s = null;
+for(;;) {
+ if (s) return s.f;
+}
+""");
+ code.parse().opto().typeCheck().GCM();
+ assertEquals("return 0;", code._stop.toString());
+ assertEquals("0", Eval2.eval(code, 0));
+ }
+
+ // Inline hidden called more than once
+ @Test
+ public void testInline() {
+ CodeGen code = new CodeGen(
+"""
+{int->int}?! i2i = {int i->return i;};
+{{int->int}->{int->int}}! f2f = {{int->int} f->return f;};
+val o = i2i;
+if (arg) i2i = null;
+if (i2i) return i2i(arg);
+return f2f(o)(1);
+""");
+ code.parse().opto().typeCheck().GCM().localSched();
+ assertEquals("Stop[ return Phi(Region,o( arg),o( 1)); return Parm_i(o,int,arg,1); ]", code._stop.toString());
+ assertEquals("1", Eval2.eval(code, 2));
+ }
+}