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.java238
1 files changed, 108 insertions, 130 deletions
diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 44c365dd..82f99fc0 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -94,7 +94,7 @@ public class Chapter18Test {
new CodeGen(test[0]).parse().opto().typeCheck();
fail("Expected incompatible return types: " + test[0]);
} catch( Parser.ParseException e ) {
- assertEquals(test[1],e.getMessage());
+ assertEquals("Cannot return generic scalar",e.getMessage());
}
}
}
@@ -102,10 +102,10 @@ public class Chapter18Test {
@Test public void testPrintingForwardReferenceScope() throws Exception {
new CodeGen("return 0;").parse();
- var declared = TypeMemPtr.make(TypeStruct.open("PrintForward"));
+ var declared = TypeMemPtr.make(TypeStruct.make("PrintForward",true,true));
var scope = new ScopeNode();
scope.define("ptr",declared,false,new ConstantNode(TypeInteger.ZERO),null);
- Parser.TYPES.put("PrintForward",TypeMemPtr.make(TypeStruct.make("PrintForward",false)));
+ Parser.TYPES.put("PrintForward",TypeMemPtr.make(TypeStruct.make("PrintForward",false,false)));
var field = Var.class.getDeclaredField("_type");
field.setAccessible(true);
var v = scope.var(0);
@@ -123,7 +123,7 @@ public class Chapter18Test {
return 0;
""");
code.driver(Phase.LocalSched);
- assertEquals("return 0;", code._stop.toString());
+ assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -141,7 +141,7 @@ while(arg--) {
return a;
""");
code.parse().opto().typeCheck();
- assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString());
+ assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code.print());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
assertEquals("1", Eval2.eval(code, 2));
@@ -158,7 +158,7 @@ return a;
return x2;
""");
code.parse().opto();
- assertEquals("return null;", code._stop.toString());
+ assertEquals("return null;", code.print());
assertEquals("null", Eval2.eval(code, 0 ) );
}
@@ -171,31 +171,50 @@ return x2;
};
""");
code.parse().opto();
- assertEquals("return (Parm_x(sq,i64)*x);", code._stop.toString());
- //assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
+ assertEquals("Stop[ return { sq}; return (Parm_x(sq,i64)*x); ]", code.print());
+ assertEquals("{ ptr i64 -> i64 #[ 3]}", Eval2.eval(code, 3));
}
@Test
public void testFcn1() {
CodeGen code = new CodeGen(
"""
-var sq = { int x ->
+var _sq = { int x ->
x*x;
};
-return sq(arg)+sq(3);
+return _sq(arg)+_sq(3);
""");
code.driver(Phase.LocalSched);
- assertEquals("Stop[ return (#2+#2); return (Parm_x(sq,i64)*x); ]", code._stop.toString());
+ assertEquals("return ((arg*arg)+9);", code.print());
assertEquals("13", Eval2.eval(code, 2));
}
// Function scope test
@Test
- public void testFcn2() {
+ public void testFcn2A() {
CodeGen code = new CodeGen(
"""
-int cnt=1;
-return { -> cnt; };
+int cnt=1; // Global scope; exactly one of these
+return { -> cnt++; }; // Escape global-scope variable is OK
+""");
+ code.parse().opto();
+ assertEquals("Stop[ return { ptr -> i64 #[ 3]}; return .cnt; ]", code.print());
+ assertEquals("{ ptr -> i64 #[ 3]}", Eval2.eval(code, 0));
+ }
+
+ // Function scope test
+ @Test
+ public void testFcn2B() {
+ CodeGen code = new CodeGen(
+"""
+val ctr = { ->
+ int cnt; // Function-local var
+ return { -> cnt=cnt+1; cnt; }; // Escape cnt out of scope
+};
+val A = ctr(); // Make a counter
+val B = ctr(); // Make a counter
+A(); // Increment counter A
+return A()*10 + B(); // Incr A and B; result: 2*10+1 == 21
""");
try { code.parse().opto(); fail(); }
catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); }
@@ -210,7 +229,7 @@ val cnt=2;
return { -> cnt; }();
""");
code.parse().opto();
- assertEquals("return 2;", code._stop.toString());
+ assertEquals("return 2;", code.print());
assertEquals("2", Eval2.eval(code, 0));
}
@@ -223,7 +242,7 @@ var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
code.parse().opto();
- 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($fun3,i64,3)*x); return (Parm_x($fun4,i64,3)<<1); ]", code.print());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
}
@@ -231,9 +250,12 @@ return fcn(3);
// 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);");
+ CodeGen code = new CodeGen("val _fact = { int x -> x <= 1 ? 1 : x*_fact(x-1); }; return _fact(arg);");
code.parse().opto().typeCheck();
- assertEquals("Stop[ return #2; return Phi(Region,1,(Parm_x(fact,i64)*#2)); ]", code._stop.toString());
+ String ir = code.print();
+ assertTrue(ir.equals("Stop[ return #2; return Phi(Region,1,(Parm_x(_fact,arg,(x-1))*#2)); ]") ||
+ ir.equals("Stop[ return Phi(Region,1,(arg*#2)); return Phi(Region,1,(Parm_x(_fact,(arg-1),(x-1))*#2)); ]") ||
+ ir.equals("Stop[ return Phi(Region,1,(arg*Phi(Region,1,((arg-1)*#2)))); return Phi(Region,1,(Parm_x(_fact,(Sub-1),(x-1))*#2)); ]"));
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
assertEquals( "2", Eval2.eval(code, 2));
@@ -241,31 +263,55 @@ return fcn(3);
assertEquals("24", Eval2.eval(code, 4));
}
+ // The multiply initially sees one integer input and one unresolved
+ // The multiply initially sees one integer input and one unresolvedfcn8
+ // recursive result, but the floating base case requires a floating mode.
+ @Test
+ public void testFcn5Float() {
+ CodeGen code = new CodeGen("val _fact = { int x -> x <= 1 ? 1.0 : x*_fact(x-1); }; return _fact(arg);");
+ code.parse().opto().typeCheck();
+ assertEquals("1.0", Eval2.eval(code, 0));
+ assertEquals("1.0", Eval2.eval(code, 1));
+ assertEquals("2.0", Eval2.eval(code, 2));
+ assertEquals("6.0", Eval2.eval(code, 3));
+ assertEquals("24.0", Eval2.eval(code, 4));
+ }
+
+ @Test
+ public void testFcn5Minus() {
+ CodeGen code = new CodeGen("val _neg = { int x -> x ? -_neg(x-1) : 1; }; return _neg(arg);");
+ code.parse().opto().typeCheck();
+ assertEquals( "1", Eval2.eval(code, 0));
+ assertEquals("-1", Eval2.eval(code, 1));
+ assertEquals( "1", Eval2.eval(code, 2));
+ assertEquals("-1", Eval2.eval(code, 3));
+ }
+
@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;
+struct _S { int i; new _S = { int x -> i=x; }; };
+val _newS = { int x -> return new _S(x); };
+return _newS(1).i;
""");
code.parse().opto().typeCheck();
- assertEquals("return 1;", code._stop.toString());
+ assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
- // Double forward reference
+ // Only forward refs to direct function calls allowed
@Test
public void testFcn7() {
CodeGen code = new CodeGen(
"""
-if( arg ? f : g ) return 1;
-val f = {->1;};
-val g = {->2;};
+val _f = {->1;};
+val _g = {->2;};
+if( arg ? _f : _g ) return 1;
return 2;
""");
code.parse().opto().typeCheck();
- assertEquals("return 1;", code._stop.toString());
+ assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -273,17 +319,17 @@ return 2;
public void testFcn8() {
CodeGen code = new CodeGen(
"""
-{int -> int}? !i2i = null;
-var id = {{int->int} f-> return f;};
+{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);
+ if (_i2i) return _i2i(arg);
+ var _x = {int i-> return i;};
+ arg = _x(3);
+ _i2i = _id(_x);
}
""");
code.driver(Phase.LocalSched);
- assertEquals("Stop[ return #2; return Parm_i(x,i64); ]", code._stop.toString());
+ assertEquals("return Phi(Loop,arg,3);", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -299,8 +345,8 @@ for(;;) {
}
""");
code.driver(Phase.LocalSched);
- assertEquals("return Top;", code._stop.toString());
- assertNull(Eval2.eval(code,0)); // Infinite loop: i2i remains null.
+ assertEquals("Stop[ return Top; return Parm_i(x,i64); ]", code.print());
+ assertNull( Eval2.eval( code, 0 ) ); // Infinite loop, <clinit> never exits
}
@@ -308,23 +354,23 @@ for(;;) {
public void testFcn10() {
CodeGen code = new CodeGen(
"""
-struct Person {
+struct _Person {
int age;
};
-val fcn = { !Person?[] !ps, int x ->
+val _fcn = { !_Person?[] !ps, int x ->
val tmp = ps[x];
if( ps[x] )
ps[x].age++;
};
-var ps = new !Person?[2];
-ps[0] = new Person;
-ps[1] = new Person;
-return fcn(ps,1);
+var ps = new !_Person?[2];
+ps[0] = new _Person;
+ps[1] = new _Person;
+return _fcn(ps,1);
""");
code.driver(Phase.LocalSched);
- assertEquals("return 0;", code._stop.toString());
+ assertEquals("return .age;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -359,7 +405,7 @@ for(;;) {
return 0;
""");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Might be null calling { i64 -> i64 #21}?",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Might be null calling { ptr i64 -> i64 #[ 3]}?",e.getMessage()); }
}
@@ -371,7 +417,7 @@ 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()); }
+ catch( Exception e ) { assertEquals("Expecting 3 arguments, but found 1",e.getMessage()); }
}
@@ -383,16 +429,14 @@ 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;
-}
+S? s = new S;
+return s.x;
""");
try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*S'",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*Test.S'",e.getMessage()); }
}
- @Test
+ @Test @Ignore
public void testErr5() {
CodeGen code = new CodeGen(
"""
@@ -415,8 +459,8 @@ 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());
+ code.parse().opto().typeCheck();
+ assertEquals("Stop[ return #2; return Phi(Region,0,1,#2); ]", code.print());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("0", Eval2.eval(code, 1));
assertEquals("1", Eval2.eval(code, 2));
@@ -438,7 +482,7 @@ for(;;) {
}
""");
code.parse().opto().typeCheck().GCM();
- assertEquals("return 0;", code._stop.toString());
+ assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -447,15 +491,15 @@ for(;;) {
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);
+{int->int}?! _i2i_noInline = {int i->return i;};
+{{int->int}->{int->int}}! _f2f = {{int->int} f->return f;};
+val _o = _i2i_noInline;
+if (arg) _i2i_noInline = null;
+if (_i2i_noInline) return _i2i_noInline(arg);
+return _f2f(_o)(1);
""");
code.driver(Phase.LocalSched);
- assertEquals("Stop[ return Phi(Region,#2,1); return Parm_i(i2i.o,i64); ]", code._stop.toString());
+ assertEquals("Stop[ return Phi(Region,#2,#2); return Parm_i(_i2i_noInline,i64); ]", code.print());
assertEquals("1", Eval2.eval(code, 2));
}
@@ -463,82 +507,16 @@ return f2f(o)(1);
public void testOperField() {
CodeGen code = new CodeGen(
"""
-struct Person {
+struct _Person {
int coffee_count;
};
-!Person !p = new Person;
+!_Person !p = new _Person;
p.coffee_count += 1;
return p.coffee_count;
""");
code.driver(Phase.LocalSched);
- assertEquals("return 1;", code._stop.toString());
+ assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 2));
}
- static final String CALL_MEMORY = """
- struct S { int x; int y; };
- val bump = { !S !s, int d -> int old=s.x; s.x=old+d; return old; };
- !S !a = new S { x=10; y=7; };
- !S !b = new S { x=20; y=9; };
- !S !p=a; if (arg) p=b;
- int before=p.x;
- int old=bump(p,3);
- int after=p.x;
- bump(a,5);
- return before*1000000+old*10000+after*100+a.x+a.y;
- """;
-
- @Test public void testCallMemory() {
- var code = new CodeGen(CALL_MEMORY).parse().opto();
- 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.
- assertEquals("10101325",Eval2.eval(code,0));
- assertEquals("20202322",Eval2.eval(code,1));
- code._stop.walk(n -> {
- if(n instanceof CallNode call) {
- assertEquals(5,call.nIns()); // No scheduling edges may become extra arguments.
- assertTrue(call.fptr()._type instanceof TypeFunPtr);
- }
- return null;
- });
- }
-
- static final String RECURSIVE_MEMORY = """
- struct S { int x; };
- val rec = { !S !s, int n ->
- if (n==0) return s.x;
- int before=s.x;
- s.x=before+1;
- return before+rec(s,n-1);
- };
- !S !s=new S { x=10; };
- int value=rec(s,arg);
- return value*100+s.x;
- """;
-
- @Test public void testRecursiveMemory() {
- var code = new CodeGen(RECURSIVE_MEMORY).parse().opto();
- for(int n=0; n<7; n++)
- assertEquals(Long.toString((10L*(n+1)+n*(n+1)/2)*100+10+n),Eval2.eval(code,n));
- }
-
- static final String INLINE_MEMORY = """
- struct S { int x; int y; };
- val make = { !S s ->
- S t = new S { x=s.x+1; y=3; };
- s.y=9;
- return t;
- };
- !S !a = new S { x=arg+40; y=7; };
- S t=make(a);
- return a.x*10000+a.y*100+t.x+t.y;
- """;
-
- @Test public void testInlineAllocationMemory() {
- var code = new CodeGen(INLINE_MEMORY).parse().opto();
- assertNull(code._stop.walk(n -> n instanceof CallNode ? n : null));
- assertEquals("400944",Eval2.eval(code,0));
- assertEquals("420946",Eval2.eval(code,2));
- }
}