diff options
| author | Cliff Click <cliffc@acm.org> | 2026-10-03 14:03:48 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-10-03 14:03:48 -0700 |
| commit | 22df891893d30e9906d0b14fc3ae5d8e67d500b2 (patch) | |
| tree | 2bda142f92febc8b26f1cabd4bb0c46d1864f34b /chapter17/src/test | |
| parent | c3958e34c0de21df52e57e47e4f47ffe6464f138 (diff) | |
| download | Simple-22df891893d30e9906d0b14fc3ae5d8e67d500b2.tar.gz Simple-22df891893d30e9906d0b14fc3ae5d8e67d500b2.zip | |
Chapter renumber, and split ch 17
Renumber chapters 10a, 10b, 11, 12, 13, 14 -> 10, 11, 13, 12, [11,14].
Split ch 17 into a 17a and 17b; shuffle around im/mutable syntax to be consistent, and present it more slowly.
THe new immutable syntax is pushed through all later chapters
Diffstat (limited to 'chapter17/src/test')
29 files changed, 0 insertions, 7312 deletions
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/chapter17/src/test/java/com/seaofnodes/simple/BrainFuckTest.java deleted file mode 100644 index 154b74a2..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/BrainFuckTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.StopNode; -import org.junit.Test; -import org.junit.Ignore; - -import java.nio.charset.StandardCharsets; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class BrainFuckTest { - - @Test - public void testBrainfuck() { - var program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.".getBytes(StandardCharsets.UTF_8); - var encoded = new StringBuilder("u8[] !program = new u8[").append(program.length).append("];"); - for (int i = 0; i < program.length; i++) { - int value = program[i] & 0xFF; - encoded.append("program[").append(i).append("] = ").append(value).append(";"); - } - - Parser parser = new Parser( - encoded + """ -var d = 0; -u8[] !output = new u8[0]; -u8[] !data = new u8[100]; - -for( int pc = 0; pc < program#; pc++ ) { - var command = program[pc]; - if (command == 62) { - d++; - } else if (command == 60) { - d--; - } else if (command == 43) { - data[d]++; - } else if (command == 45) { - data[d]--; - } else if (command == 46) { - // Output a byte; increase the output array size - var old = output; - output = new u8[output# + 1]; - for( var i = 0; i < old#; i++ ) - output[i] = old[i]; - output[old#] = data[d]; // Add the extra byte on the end - } else if (command == 44) { - data[d] = 42; - } else if (command == 91) { - if (data[d] == 0) { - for( var d = 1; d > 0; ) { - command = program[++pc]; - if (command == 91) d++; - if (command == 93) d--; - } - } - } else if (command == 93) { - if (data[d]) { - for( var d = 1; d > 0; ) { - command = program[--pc]; - if (command == 93) d++; - if (command == 91) d--; - } - } - } -} -return output; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("Hello World!\n", Evaluator.evaluate(stop, 0, 10000).toString()); - } -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter01Test.java deleted file mode 100644 index f7524c17..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter01Test.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class Chapter01Test { - - @Test - public void testSimpleProgram() { - Parser parser = new Parser("return 1;"); - StopNode stop = parser.parse(); - StartNode start = Parser.START; - ReturnNode ret = (ReturnNode)stop.in(0); - - assertTrue(ret.ctrl() instanceof CProjNode); - Node expr = ret.expr(); - if( expr instanceof ConstantNode con ) { - assertEquals(start,con.in(0)); - assertEquals(TypeInteger.constant(1), con._type); - } else { - fail(); - } - } - - @Test - public void testZero() { - Parser parser = new Parser("return 0;"); - parser.parse(); - StartNode start = Parser.START; - for( Node use : start._outputs ) - if( use instanceof ConstantNode con && con._type instanceof TypeInteger ) - assertEquals(TypeInteger.constant(0),con._type); - } - - @Test - public void testBad1() { - try { - new Parser("ret").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Undefined name 'ret'",e.getMessage()); - } - } - - @Test - public void testBad2() { - try { - new Parser("return 0123;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Syntax error: integer values cannot start with '0'",e.getMessage()); - } - } - - @Test - public void testNotBad3() { - // this test used to fail in chapter 1 - assertEquals("return 12;", new Parser("return - -12;").parse().print()); - } - - @Test - public void testBad4() { - try { - new Parser("return 100").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Syntax error, expected ;: ",e.getMessage()); - } - } - - @Test - public void testNotBad5() { - // this test used to fail in chapter 1 - assertEquals("return -100;", new Parser("return -100;").parse().print()); - } - - @Test - public void testBad6() { - try { - new Parser("return100").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Undefined name 'return100'",e.getMessage()); - } - } - - @Test - public void testBad7() { - try { - new Parser("return 1;}").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Syntax error, unexpected }",e.getMessage()); - } - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter02Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter02Test.java deleted file mode 100644 index a6075b75..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter02Test.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class Chapter02Test { - - @Test - public void testParseGrammar() { - Parser parser = new Parser("return 1+2*3+-5;"); - StopNode ret = parser.parse(); - assertEquals("return 2;", ret.print()); - } - - @Test - public void testAddPeephole() { - Parser parser = new Parser("return 1+2;"); - StopNode ret = parser.parse(); - assertEquals("return 3;", ret.print()); - } - - @Test - public void testSubPeephole() { - Parser parser = new Parser("return 1-2;"); - StopNode ret = parser.parse(); - assertEquals("return -1;", ret.print()); - } - - @Test - public void testMulPeephole() { - Parser parser = new Parser("return 2*3;"); - StopNode ret = parser.parse(); - assertEquals("return 6;", ret.print()); - } - - @Test - public void testDivPeephole() { - Parser parser = new Parser("return 6/3;"); - StopNode ret = parser.parse(); - assertEquals("return 2;", ret.print()); - } - - @Test - public void testMinusPeephole() { - Parser parser = new Parser("return 6/-3;"); - StopNode ret = parser.parse(); - assertEquals("return -2;", ret.print()); - } - - @Test - public void testExample() { - Parser parser = new Parser("return 1+2*3+-5;"); - StopNode ret = parser.parse(); - assertEquals("return 2;", ret.print()); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter03Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter03Test.java deleted file mode 100644 index f06e5dcf..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter03Test.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class Chapter03Test { - - @Test - public void testVarDecl() { - Parser parser = new Parser("int a=1; return a;"); - StopNode ret = parser.parse(); - assertEquals("return 1;", ret.print()); - } - - @Test - public void testVarAdd() { - Parser parser = new Parser("int a=1; int b=2; return a+b;"); - StopNode ret = parser.parse(); - assertEquals("return 3;", ret.print()); - } - - @Test - public void testVarScope() { - Parser parser = new Parser("int a=1; int b=2; int c=0; { int b=3; c=a+b; } return c;"); - StopNode ret = parser.parse(); - assertEquals("return 4;", ret.print()); - } - - @Test - public void testVarDist() { - Parser parser = new Parser("int x0=1; int y0=2; int x1=3; int y1=4; return (x0-x1)*(x0-x1) + (y0-y1)*(y0-y1); "); - StopNode ret = parser.parse(); - assertEquals("return 8;", ret.print()); - } - - @Test - public void testSelfAssign() { - try { - new Parser("int a=a; return a;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Undefined name 'a'",e.getMessage()); - } - } - - @Test - public void testRedeclareVar() { - try { - new Parser("int a=1; int b=2; int c=0; int b=3; c=a+b;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Redefining name 'b'",e.getMessage()); - } - } - - @Test - public void testBad1() { - try { - new Parser("int a=1; int b=2; int !c=0; { int b=3; c=a+b;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Syntax error, expected }: ",e.getMessage()); - } - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter04Test.java deleted file mode 100644 index 36727a8f..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter04Test.java +++ /dev/null @@ -1,165 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class Chapter04Test { - - @Test - public void testPeephole() { - Parser parser = new Parser("return 1+arg+2; "); - StopNode ret = parser.parse(); - assertEquals("return (arg+3);", ret.print()); - } - - @Test - public void testPeephole2() { - Parser parser = new Parser("return (1+arg)+2;"); - StopNode ret = parser.parse(); - assertEquals("return (arg+3);", ret.print()); - } - - @Test - public void testAdd0() { - Parser parser = new Parser("return 0+arg;"); - StopNode ret = parser.parse(); - assertEquals("return arg;", ret.print()); - } - - @Test - public void testAddAddMul() { - Parser parser = new Parser("return arg+0+arg;"); - StopNode ret = parser.parse(); - assertEquals("return (arg*2);", ret.print()); - } - - @Test - public void testPeephole3() { - Parser parser = new Parser("return 1+arg+2+arg+3; "); - StopNode ret = parser.parse(); - assertEquals("return ((arg*2)+6);", ret.print()); - } - - @Test - public void testMul1() { - Parser parser = new Parser("return 1*arg;"); - StopNode ret = parser.parse(); - assertEquals("return arg;", ret.print()); - } - - @Test - public void testSub0() { - Parser parser = new Parser("return arg-0;"); - StopNode ret = parser.parse(); - assertEquals("return arg;", ret.print()); - } - - @Test - public void test0Sub() { - Parser parser = new Parser("return 0-arg;"); - StopNode ret = parser.parse(); - assertEquals("return (-arg);", ret.print()); - } - - @Test - public void testSubNeg() { - Parser parser = new Parser("return 3--arg;"); - StopNode ret = parser.parse(); - assertEquals("return (arg+3);", ret.print()); - } - - @Test - public void testNegSub() { - Parser parser = new Parser("return -arg-3;"); - StopNode ret = parser.parse(); - assertEquals("return (-(arg+3));", ret.print()); - } - - @Test - public void testVarArg() { - Parser parser = new Parser("return arg; "); - StopNode stop = parser.parse(); - ReturnNode ret = stop.ret(); - assertTrue(ret.in(0) instanceof CProjNode); - assertTrue(ret.in(1) instanceof ProjNode); - assertEquals(1, ((ProjNode)ret.in(1))._idx); - assertTrue(ret.in(2) instanceof ProjNode); - assertEquals(2, ((ProjNode)ret.in(2))._idx); - } - - @Test - public void testConstantArg() { - Parser parser = new Parser("return arg; ", TypeInteger.constant(2)); - StopNode ret = parser.parse(); - assertEquals("return 2;", ret.print()); - } - - @Test - public void testCompEq() { - Parser parser = new Parser("return 3==3; "); - StopNode ret = parser.parse(); - assertEquals("return 1;", ret.print()); - } - - @Test - public void testCompEq2() { - Parser parser = new Parser("return 3==4; "); - StopNode ret = parser.parse(); - assertEquals("return 0;", ret.print()); - } - - @Test - public void testCompNEq() { - Parser parser = new Parser("return 3!=3; "); - StopNode ret = parser.parse(); - assertEquals("return 0;", ret.print()); - } - - @Test - public void testCompNEq2() { - Parser parser = new Parser("return 3!=4; "); - StopNode ret = parser.parse(); - assertEquals("return 1;", ret.print()); - } - - @Test - public void testBug1() { - Parser parser = new Parser("int a=arg+1; int !b=a; b=1; return a+2; "); - StopNode ret = parser.parse(); - assertEquals("return (arg+3);", ret.print()); - } - - @Test - public void testBug2() { - Parser parser = new Parser("int !a=arg+1; a=a; return a; "); - StopNode ret = parser.parse(); - assertEquals("return (arg+1);", ret.print()); - } - - @Test - public void testBug3() { - try { - new Parser("inta=1; return a;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Undefined name 'inta'",e.getMessage()); - } - } - - @Test - public void testBug4() { - Parser parser = new Parser("return -arg;"); - StopNode ret = parser.parse(); - assertEquals("return (-arg);", ret.print()); - } - - @Test - public void testBug5() { - Parser parser = new Parser("return arg--2;"); - StopNode ret = parser.parse(); - assertEquals("return (arg--2);", ret.print()); - } -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter05Test.java deleted file mode 100644 index 7ea75f49..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter05Test.java +++ /dev/null @@ -1,227 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class Chapter05Test { - - @Test - public void testIfStmt() { - Parser parser = new Parser( -""" -int a = 1; -if (arg == 1) - a = arg+2; -else { - a = arg-3; -} -return a; -"""); - StopNode ret = parser.parse().iterate(); - assertEquals("return Phi(Region,(arg+2),(arg-3));", ret.toString()); - } - - @Test - public void testTest() { - Parser parser = new Parser( -""" -int c = 3; -int b = 2; -if (arg == 1) { - b = 3; - c = 4; -} -return c;""", TypeInteger.BOT); - StopNode ret = parser.parse().iterate(); - assertEquals("return Phi(Region,4,3);", ret.toString()); - } - - @Test - public void testReturn2() { - Parser parser = new Parser( -""" -if( arg==1 ) - return 3; -else - return 4; -""", TypeInteger.BOT); - StopNode stop = parser.parse(); - assertEquals("Stop[ return 3; return 4; ]", stop.toString()); - } - - @Test - public void testIfMergeB() { - Parser parser = new Parser( -""" -int a=arg+1; -int b=0; -if( arg==1 ) - b=a; -else - b=a+1; -return a+b;"""); - StopNode ret = parser.parse().iterate(); - assertEquals("return ((arg*2)+Phi(Region,2,3));", ret.toString()); - } - - @Test - public void testIfMerge2() { - Parser parser = new Parser( -""" -int a=arg+1; -int b=arg+2; -if( arg==1 ) - b=b+a; -else - a=b+1; -return a+b;"""); - StopNode ret = parser.parse().iterate(); - assertEquals("return ((Phi(Region,(arg*2),arg)+arg)+Phi(Region,4,5));", ret.toString()); - } - - @Test - public void testMerge3() { - Parser parser = new Parser( -""" -int a=1; -if( arg==1 ) - if( arg==2 ) - a=2; - else - a=3; -else if( arg==3 ) - a=4; -else - a=5; -return a; -""", TypeInteger.BOT); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,Phi(Region,2,3),Phi(Region,4,5));", stop.toString()); - } - - @Test - public void testMerge4() { - Parser parser = new Parser( -""" -int a=0; -int b=0; -if( arg ) - a=1; -if( arg==0 ) - b=2; -return arg+a+b; -""", TypeInteger.BOT); - StopNode stop = parser.parse(); - assertEquals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));", stop.toString()); - } - - @Test - public void testMerge5() { - Parser parser = new Parser( -""" -int a=arg==2; -if( arg==1 ) -{ - a=arg==3; -} -return a;"""); - StopNode ret = parser.parse().iterate(); - assertEquals("return (arg==Phi(Region,3,2));", ret.toString()); - } - - @Test - public void testMergeSameCon() { - Parser parser = new Parser( -""" -int a=arg; -if( arg==1 ) - a=3; -else - a=3; -return a; -"""); - StopNode ret = parser.parse(); - assertEquals("return 3;", ret.toString()); - } - - @Test - public void testTrue() { - StopNode stop = new Parser("return true;").parse(); - assertEquals("return 1;",stop.toString()); - } - - @Test - public void testHalfDef() { - try { - new Parser("if( arg==1 ) int b=2; return b;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Cannot define a new name on one arm of an if",e.getMessage()); - } - } - - @Test - public void testHalfDef2() { - try { - new Parser("if( arg==1 ) { int b=2; } else { int b=3; } return b;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Undefined name 'b'",e.getMessage()); - } - } - - @Test - public void testRegress1() { - try { - new Parser("if(arg==2) int a=1; else int b=2; return a;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Cannot define a new name on one arm of an if",e.getMessage()); - } - } - - - @Test - public void testBadNum() { - try { - new Parser("return 1-;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Syntax error, expected an identifier or expression: ;",e.getMessage()); - } - } - - @Test - public void testKeyword1() { - try { - new Parser("int true=0; return true;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Expected an identifier, found 'true'",e.getMessage()); - } - } - - @Test - public void testKeyword2() { - try { - new Parser("int else=arg; if(else) else=2; else else=1; return else;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Expected an identifier, found 'else'",e.getMessage()); - } - } - - @Test - public void testKeyword3() { - try { - new Parser("int a=1; ififif(arg)inta=2;return a;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Undefined name 'ififif'",e.getMessage()); - } - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter06Test.java deleted file mode 100644 index 50c0580d..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter06Test.java +++ /dev/null @@ -1,294 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class Chapter06Test { - - @Test - public void testPeepholeReturn() { - Parser parser = new Parser( -""" -if( true ) return 2; -return 1; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 2;", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testPeepholeRotate() { - Parser parser = new Parser( -""" -int a = 1; -if (arg) - a = 2; -return (arg < a) < 3; // Because (arg < a) is a bool/uint1/[0-1], its always less than 3 -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 1;", stop.toString()); - } - - @Test - public void testPeepholeCFG() { - Parser parser = new Parser( -""" -int a=1; -if( true ) - a=2; -else - a=3; -return a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 2;", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testIfIf() { - Parser parser = new Parser( -""" -int a=1; -if( arg!=1 ) - a=2; -else - a=3; -int b=4; -if( a==2 ) - b=42; -else - b=5; -return b;""", TypeInteger.BOT); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,42,5);", stop.toString()); - } - - @Test - public void testIfArgIf() { - Parser parser = new Parser( -""" -int a=1; -if( 1==1 ) - a=2; -else - a=3; -int b=4; -if( arg==2 ) - b=a; -else - b=5; -return b;""", TypeInteger.BOT); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,2,5);", stop.toString()); - } - - @Test - public void testMerge3With2() { - Parser parser = new Parser( -""" -int a=1; -if( arg==1 ) - if( arg==2 ) - a=2; - else - a=3; -else if( arg==3 ) - a=4; -else - a=5; -return a; -""", TypeInteger.constant(2)); - StopNode stop = parser.parse(); - assertEquals("return 5;", stop.toString()); - } - - @Test - public void testMerge3With1() { - Parser parser = new Parser( -""" -int a=1; -if( arg==1 ) - if( arg==2 ) - a=2; - else - a=3; -else if( arg==3 ) - a=4; -else - a=5; -return a; -""", TypeInteger.constant(1)); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3;", stop.toString()); - } - - @Test - public void testMerge3Peephole() { - Parser parser = new Parser( -""" -int a=1; -if( arg==1 ) - if( 1==2 ) - a=2; - else - a=3; -else if( arg==3 ) - a=4; -else - a=5; -return a; -""", TypeInteger.BOT); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,3,Phi(Region,4,5));", stop.toString()); - } - - @Test - public void testMerge3Peephole1() { - Parser parser = new Parser( -""" -int a=1; -if( arg==1 ) - if( 1==2 ) - a=2; - else - a=3; -else if( arg==3 ) - a=4; -else - a=5; -return a; -""", TypeInteger.constant(1)); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3;", stop.toString()); - } - - @Test - public void testMerge3Peephole3() { - Parser parser = new Parser( -""" -int a=1; -if( arg==1 ) - if( 1==2 ) - a=2; - else - a=3; -else if( arg==3 ) - a=4; -else - a=5; -return a; -""", TypeInteger.constant(3)); - StopNode stop = parser.parse().iterate(); - assertEquals("return 4;", stop.toString()); - } - - @Test - public void testDemo1NonConst() { - Parser parser = new Parser(""" -int a = 0; -int b = 1; -if( arg ) { - a = 2; - if( arg ) { b = 2; } - else b = 3; -} -return a+b; -"""); - StopNode ret = parser.parse().iterate(); - assertEquals("return Phi(Region,4,1);", ret.toString()); - } - - - @Test - public void testDemo1True() { - Parser parser = new Parser(""" -int a = 0; -int b = 1; -if( arg ) { - a = 2; - if( arg ) { b = 2; } - else b = 3; -} -return a+b; -""", TypeInteger.constant(1)); - StopNode ret = parser.parse().iterate(); - assertEquals("return 4;", ret.toString()); - } - - @Test - public void testDemo1False() { - Parser parser = new Parser(""" -int a = 0; -int b = 1; -if( arg ) { - a = 2; - if( arg ) { b = 2; } - else b = 3; -} -return a+b; -""", TypeInteger.constant(0)); - StopNode ret = parser.parse().iterate(); - assertEquals("return 1;", ret.toString()); - } - - @Test - public void testDemo2NonConst() { - Parser parser = new Parser(""" -int a = 0; -int b = 1; -int c = 0; -if( arg ) { - a = 1; - if( arg==2 ) { c=2; } else { c=3; } - if( arg ) { b = 2; } - else b = 3; -} -return a+b+c; -"""); - StopNode ret = parser.parse().iterate(); - assertEquals("return (Phi(Region,Phi(Region,2,3),0)+Phi(Region,3,1));", ret.toString()); - } - - - @Test - public void testDemo2True() { - Parser parser = new Parser(""" -int a = 0; -int b = 1; -int c = 0; -if( arg ) { - a = 1; - if( arg==2 ) { c=2; } else { c=3; } - if( arg ) { b = 2; } - else b = 3; -} -return a+b+c; -""", TypeInteger.constant(1)); - StopNode ret = parser.parse().iterate(); - assertEquals("return 6;", ret.toString()); - } - - @Test - public void testDemo2arg2() { - Parser parser = new Parser(""" -int a = 0; -int b = 1; -int c = 0; -if( arg ) { - a = 1; - if( arg==2 ) { c=2; } else { c=3; } - if( arg ) { b = 2; } - else b = 3; -} -return a+b+c; -""", TypeInteger.constant(2)); - StopNode ret = parser.parse().iterate(); - assertEquals("return 5;", ret.toString()); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter07Test.java deleted file mode 100644 index ad3622c9..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter07Test.java +++ /dev/null @@ -1,221 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.node.*; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class Chapter07Test { - - @Test - public void testExample() { - Parser parser = new Parser( -""" -while(arg < 10) { - arg = arg + 1; -} -return arg; -"""); - StopNode stop = parser.parse(); - assertEquals("return Phi(Loop,arg,(Phi_arg+1));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testRegression() { - Parser parser = new Parser( -""" -int a = 1; -if(arg){}else{ - while(a < 10) { - a = a + 1; - } -} -return a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,1,Phi(Loop,1,(Phi_a+1)));", stop.toString()); - } - - @Test - public void testWhileNested() { - Parser parser = new Parser( -""" -int sum = 0; -int i = 0; -while(i < arg) { - i = i + 1; - int j = 0; - while( j < arg ) { - sum = sum + j; - j = j + 1; - } -} -return sum; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,0,Phi(Loop,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", stop.toString()); - } - - @Test - public void testWhileScope() { - Parser parser = new Parser( -""" -int a = 1; -int b = 2; -while(a < 10) { - if (a == 2) a = 3; - else b = 4; -} -return b; -"""); - StopNode stop = parser.parse(); - assertEquals("return Phi(Loop,2,Phi(Region,Phi_b,4));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testWhileNestedIfAndInc() { - Parser parser = new Parser( -""" -int a = 1; -int b = 2; -while(a < 10) { - if (a == 2) a = 3; - else b = 4; - b = b + 1; - a = a + 1; -} -return b; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - - @Test - public void testWhile() { - Parser parser = new Parser( -""" -int a = 1; -while(a < 10) { - a = a + 1; - a = a + 2; -} -return a; -"""); - StopNode stop = parser.parse(); - assertEquals("return Phi(Loop,1,(Phi_a+3));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testWhilePeep() { - Parser parser = new Parser( -""" -int a = 1; -while(a < 10) { - a = a + 1; - a = a + 2; -} -return a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,1,(Phi_a+3));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testWhile2() { - Parser parser = new Parser( -""" -int a = 1; -while(arg) a = 2; -return a; -"""); - StopNode stop = parser.parse(); - assertEquals("return Phi(Loop,1,2);", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testWhile2Peep() { - Parser parser = new Parser( -""" -int a = 1; -while(arg) a = 2; -return a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,1,2);", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testWhile3() { - Parser parser = new Parser( -""" -int a = 1; -while(a < 10) { - int b = a + 1; - a = b + 2; -} -return a; -"""); - StopNode stop = parser.parse(); - assertEquals("return Phi(Loop,1,(Phi_a+3));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testWhile3Peep() { - Parser parser = new Parser( -""" -int a = 1; -while(a < 10) { - int b = a + 1; - a = b + 2; -} -return a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,1,(Phi_a+3));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testWhile4() { - Parser parser = new Parser( -""" -int a = 1; -int b = 2; -while(a < 10) { - int b = a + 1; - a = b + 2; -} -return a; -"""); - StopNode stop = parser.parse(); - assertEquals("return Phi(Loop,1,(Phi_a+3));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testWhile4Peep() { - Parser parser = new Parser( -""" -int a = 1; -int b = 2; -while(a < 10) { - int b = a + 1; - a = b + 2; -} -return a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,1,(Phi_a+3));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter08Test.java deleted file mode 100644 index bb3bd674..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter08Test.java +++ /dev/null @@ -1,208 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.*; -import org.junit.Assert; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class Chapter08Test { - - @Test - public void testEx6() { - Parser parser = new Parser( - """ -while(arg < 10) { - arg = arg + 1; - if (arg == 5) - break; - if (arg == 6) - break; -} -return arg; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add),Add);", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof RegionNode); - Assert.assertEquals(5L, Evaluator.evaluate(stop, 1)); - Assert.assertEquals(10L, Evaluator.evaluate(stop, 6)); - } - - - @Test - public void testEx5() { - Parser parser = new Parser( - """ -int a = 1; -while(arg < 10) { - arg = arg + 1; - if (arg == 5) - continue; - if (arg == 7) - continue; - a = a + 1; -} -return a; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1)));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - - @Test - public void testEx4() { - Parser parser = new Parser( - """ -while(arg < 10) { - arg = arg + 1; - if (arg == 5) - continue; - if (arg == 6) - break; -} -return arg; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof RegionNode); - } - - @Test - public void testEx3() { - Parser parser = new Parser( - """ -while(arg < 10) { - arg = arg + 1; - if (arg == 6) - break; -} -return arg; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof RegionNode); - } - - - @Test - public void testEx2() { - Parser parser = new Parser( - """ -while(arg < 10) { - arg = arg + 1; - if (arg == 5) - continue; - if (arg == 6) - continue; -} -return arg; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,arg,(Phi_arg+1));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - - @Test - public void testEx1() { - Parser parser = new Parser( - """ -while(arg < 10) { - arg = arg + 1; - if (arg == 5) - continue; -} -return arg; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,arg,(Phi_arg+1));", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode); - } - - @Test - public void testRegress1() { - Parser parser = new Parser( - """ -while( arg < 10 ) { - int a = arg+2; - if( a > 4 ) - break; -} -return arg; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return arg;", stop.toString()); - } - - @Test - public void testRegress2() { - Parser parser = new Parser("if(1) return 0; else while(arg>- -arg) arg=arg+1; return 0;"); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testBreakOutsideLoop() { - try { - new Parser(""" -if(arg <= 10) { - break; - arg = arg + 1; -} -return arg; -""").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("No active loop for a break or continue",e.getMessage()); - } - } - - @Test - public void testRegress3() { - Parser parser = new Parser(""" -while(arg < 10) { - break; -} -return arg; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return arg;", stop.toString()); - } - - @Test - public void testRegress4() { - Parser parser = new Parser(""" -int a = 1; -while(arg < 10) { - a = a + 1; - if (arg > 2) { - int a = 17; - break; - } -} -return a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,Phi(Loop,1,(Phi_a+1)),Add);", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof RegionNode); - } - - @Test - public void testRegress5() { - Parser parser = new Parser(""" -int a = 1; -while(1) { - a = a + 1; - if (a<10) continue; - break; -} -return a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return (Phi(Loop,1,Add)+1);", stop.toString()); - assertTrue(stop.ret().ctrl() instanceof CProjNode prj && prj._idx==1 ); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter09Test.java deleted file mode 100644 index c31afcd6..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter09Test.java +++ /dev/null @@ -1,301 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; -import com.seaofnodes.simple.type.TypeInteger; -import org.junit.Assert; -import org.junit.Test; -import org.junit.Ignore; - -import static org.junit.Assert.*; - -public class Chapter09Test { - - @Test - @Ignore - public void testJig() { - Parser parser = new Parser(""" -int v0=0; -arg=0; -while(v0) { - while(1) if(arg*arg*0==0) {} - while(0) {} - arg=1; -} -return 0; - """); - StopNode stop = parser.parse().iterate(); - } - - @Test - public void testGVN1() { - Parser parser = new Parser( - """ -int x = arg + arg; -if(arg < 10) { - return arg + arg; -} -else { - x = x + 1; -} -return x; - """); - StopNode stop = parser.parse(); - assertEquals("Stop[ return (arg*2); return (Mul+1); ]", stop.toString()); - Assert.assertEquals(2L, Evaluator.evaluate(stop, 1)); - Assert.assertEquals(23L, Evaluator.evaluate(stop, 11)); - } - - @Test - public void testGVN2() { - Parser parser = new Parser( - """ -return arg*arg-arg*arg; - """); - StopNode stop = parser.parse(); - assertEquals("return 0;", stop.toString()); - Assert.assertEquals(0L, Evaluator.evaluate(stop, 1)); - } - - @Test - public void testWorklist1() { - Parser parser = new Parser( - """ -int step = 1; -while (arg < 10) { - arg = arg + step + 1; -} -return arg; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,arg,(Phi_arg+2));", stop.toString()); - Assert.assertEquals(11L, Evaluator.evaluate(stop, 1)); - } - - @Test - public void testWorklist2() { - Parser parser = new Parser( - """ -int cond = 0; -int one = 1; -while (arg < 10) { - if (cond) one = 2; - arg = arg + one*3 + 1; -} -return arg; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,arg,(Phi_arg+4));", stop.toString()); - Assert.assertEquals(13L, Evaluator.evaluate(stop, 1)); - } - - @Test - public void testWorklist3() { - Parser parser = new Parser( - """ -int v1 = 0; -int v2 = 0; -int v3 = 0; -int v4 = 0; -int v5 = 0; -int v6 = 0; -int v7 = 0; -int v8 = 0; -while (arg) { - if (v1) v2 = 1; - if (v2) v3 = 1; - if (v3) v4 = 1; - if (v4) v5 = 1; - if (v5) v6 = 1; - if (v6) v7 = 1; - if (v7) v8 = 1; - arg = arg + v8 + 1; -} -return arg; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testRegionPeepBug() { - Parser parser = new Parser( - """ -int v0=0; -int v1=0; -while(v1+arg) { - arg=0; - int v2=v0; - while(arg+1) {} - v0=1; - v1=v2; -} - """); - StopNode stop = parser.parse().iterate(); - assertEquals("Stop[ return 0; return 0; ]", stop.toString()); - } - - @Test - public void testWhile0() { - Parser parser = new Parser("while(0) continue; if(0) arg=0;"); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testWhile1() { - Parser parser = new Parser(""" -if(0) while(0) { - int arg=arg; - while(0) {} -} -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - - @Test - public void testPrecedence() { - Parser parser = new Parser("return 3-1+2;"); - StopNode stop = parser.parse().iterate(); - assertEquals("return 4;", stop.toString()); - } - - @Test - public void testSwap2() { - Parser parser = new Parser("return 1+(1+1);"); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3;", stop.toString()); - } - - - @Test - public void testFuzz0() { - Parser parser = new Parser(""" -int one = 1; -int a = 0; -int zero = 0; -while(arg) { - a = -(one + a + 2); - arg = arg + 1; - one = one + zero; -} -return a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,0,(-(Phi_a+3)));", stop.toString()); - } - - @Test - public void testFuzz1() { - Parser parser = new Parser(""" -while(1) {} -while(arg) break; -while(arg) arg=0; -arg=0; -int v0=0!=0<-0; -return -0+0+0; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - - @Test - public void testFuzz2() { - Parser parser = new Parser("return 0+-0;"); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testFuzz3() { - Parser parser = new Parser("int v0=0; while(0==69) while(v0) return 0;"); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testFuzz4() { - Parser parser = new Parser(""" -while(1) { - arg=0<=0; - if(1<0) while(arg==-0) arg=arg-arg; -} -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testFuzz5() { - Parser parser = new Parser(""" -{ - int v0=0; - while(1) - int v1=0--0; - while(v0) - break; - while(-v0) { - while(0+0+v0) continue; - break; - } - if(-0!=-0+0+v0) while(0+0+0+0) - break; -} -return 0!=0; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testFuzz6() { - Parser parser = new Parser( -""" -int v0=0; -while(0==1) while(v0) - v0=1+v0; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testFuzz7() { - Parser parser = new Parser(""" -while(1) {} -int v0=0; -while(v0) - {} -int v1=0; -while(1) - v1=1; -return v1+v0; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testFuzz8() { - Parser parser = new Parser("while(arg) arg = arg - 1; return arg;"); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testMeet() { - Type t1 = Type.TOP; - Type t2 = TypeInteger.TOP; - Assert.assertEquals(TypeInteger.TOP, t1.meet(t2)); - Assert.assertEquals(TypeInteger.TOP, t2.meet(t1)); - t1 = Type.BOTTOM; - t2 = TypeInteger.BOT; - Assert.assertEquals(Type.BOTTOM, t1.meet(t2)); - Assert.assertEquals(Type.BOTTOM, t2.meet(t1)); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter10Test.java deleted file mode 100644 index 86a9937f..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter10Test.java +++ /dev/null @@ -1,572 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.*; -import java.util.BitSet; -import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class Chapter10Test { - @Test public void testDeadReferenceReturn() { - for( String body : new String[] { - "return new S; return 0;", - "if(1) return new S; else return 0;" - } ) { - String src = "struct S { int x; }; " + body; - var stop = new Parser(src).parse().iterate(); - org.junit.Assert.assertTrue(body, - stop.ret().expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr); - } - } - - - - // Issue #246: null-check guards start in Chapter 10; arrays arrive in Chapter 15. - private static final String NULLABLE_POINT_SOURCE = """ - struct Point { int x; }; - Point?[] !points = new Point?[2]; - points[arg] = new Point { x = 42; }; - Point? p = points[1]; - """; - - @Test - public void testNullGuards() { - for( String body : new String[] { - "if (p != null) return p.x; return -1;", - "if (null != p) return p.x; return -1;", - "if (!!!!p) return p.x; return -1;", - "if (!!!p) return -1; return p.x;", - "int b = !!p; if (b) return p.x + b - 1; return -1;" - } ) { - StopNode stop = new Parser(NULLABLE_POINT_SOURCE+body).parse().iterate(); - assertEquals(body,-1L,com.seaofnodes.simple.evaluator.Evaluator.evaluate(stop,0)); - assertEquals(body,42L,com.seaofnodes.simple.evaluator.Evaluator.evaluate(stop,1)); - } - } - - @Test - public void testNullGuardErrors() { - for( String body : new String[] { - "return p.x;", - "if (p == null) return p.x; return -1;", - "if (!!points) return p.x; return -1;", - "if (!!p) { int x = p.x; } return p.x;" - } ) { - try { - new Parser(NULLABLE_POINT_SOURCE+body).parse().iterate(); - fail(body); - } catch( RuntimeException e ) { - // Known null is rejected during parsing in these chapters. - String expected = body.startsWith("if (p == null)") - ? "Accessing unknown field 'x' from 'null'" : "Might be null accessing 'x'"; - assertEquals(body,expected,e.getMessage()); - } - } - } - - @Test - public void testFuzzer() { - Parser parser = new Parser( -""" -int a = arg/3; -int b = arg*5; -int x = arg*7; -int y = arg/11; -int p; int g; int h; -if( (arg/13)==0 ) { - p = x + y; - g = x; - h = y; -} else { - p = a + b; - g = a; - h = b; -} -int r = g+h; -return p-r; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testStruct() { - Parser parser = new Parser(""" -struct Bar { - int a; - int b; -}; -struct Foo { - int x; -}; -Foo? foo = null; -Bar !bar = new Bar; -bar.a = 1; -bar.a = 2; -return bar.a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 2;", stop.toString()); - } - - @Test - public void testExample() { - Parser parser = new Parser(""" -struct Vector2D { int x; int y; }; -Vector2D !v = new Vector2D; -v.x = 1; -if (arg) - v.y = 2; -else - v.y = 3; -return v; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Vector2D;", stop.toString()); - } - - @Test - public void testBug() { - Parser parser = new Parser(""" -struct s0 { - int v0; -}; -s0? v1=null; -int v3=v1.zAicm; -"""); - try { parser.parse(); fail(); } - catch( Exception e ) { assertEquals("Accessing unknown field 'zAicm' from 'null'",e.getMessage()); } - } - - @Test - public void testBug2() { - Parser parser = new Parser(""" -struct s0 { int v0; }; -arg=0+new s0.0; -"""); - try { parser.parse(); fail(); } - catch( Exception e ) { assertEquals("Expected an identifier, found 'null'",e.getMessage()); } - } - - @Test - public void testLoop() { - Parser parser = new Parser(""" -struct Bar { int a; }; -Bar !bar = new Bar; -while (arg) { - bar.a = bar.a + 2; - arg = arg + 1; -} -return bar.a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,0,(Phi_a+2));", stop.toString()); - } - - @Test - public void testIf() { - Parser parser = new Parser(""" -struct Bar { int a; }; -Bar !bar = new Bar; -if (arg) bar = null; -bar.a = 1; -return bar.a; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Type null is not of declared type *Bar",e.getMessage()); } - } - - @Test - public void testIf2() { - Parser parser = new Parser(""" -struct Bar { int a; }; -Bar? !bar = null; -if (arg) bar = new Bar; -bar.a = 1; -return bar.a; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); } - } - - @Test - public void testIf3() { - Parser parser = new Parser(""" -struct Bar { int a; }; -Bar bar = null; -if (arg) bar = null; -bar.a = 1; -return bar.a; -"""); - try { parser.parse(); fail(); } - catch( Exception e ) { assertEquals("Type null is not of declared type *Bar", e.getMessage()); } - } - - @Test - public void testIfOrNull() { - Parser parser = new Parser(""" -struct Bar { int a; }; -Bar? !bar = new Bar; -if (arg) bar = null; -if( bar ) bar.a = 1; -return bar; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,(*void)Phi(Region,null,Bar),null);", stop.toString()); - } - - @Test - public void testIfOrNull2() { - Parser parser = new Parser( -""" -struct Bar { int a; }; -Bar? !bar = new Bar; -if (arg) bar = null; -int rez = 3; -if( !bar ) rez=4; -else bar.a = 1; -return rez; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,4,3);", stop.toString()); - } - - @Test - public void testWhileWithNullInside() { - Parser parser = new Parser(""" -struct s0 {int v0;}; -s0? !v0 = new s0; -int ret = 0; -while(arg) { - ret = v0.v0; - v0 = null; - arg = arg - 1; -} -return ret; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { - assertEquals("Might be null accessing 'v0'", e.getMessage()); } - } - - @Test - public void testRedeclareStruct() { - Parser parser = new Parser(""" -struct s0 { - int v0; -}; -s0? v1=new s0; -s0? v1; -v1=new s0; -"""); - try { parser.parse(); fail(); } - catch( Exception e ) { assertEquals("Redefining name 'v1'", e.getMessage()); } - } - - @Test - public void testIter() { - // Build and use an iterator - Parser parser = new Parser( -""" -struct Iter { - int x; - int len; -}; -Iter !i = new Iter; -i.len = arg; -int sum=0; -while( i.x < i.len ) { - sum = sum + i.x; - i.x = i.x + 1; -} -return sum; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", stop.toString()); - } - - - @Test - public void test1() { - Parser parser = new Parser(""" -struct s0 {int v0;}; -s0 !ret = new s0; -while(arg) { - s0 !v0 = new s0; - v0.v0 = arg; - arg = arg-1; - if (arg==5) ret=v0; - -} -return ret; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", stop.toString()); - } - - @Test - public void test2() { - Parser parser = new Parser(""" -struct s0 {int v0;}; -s0 !ret = new s0; -s0 !v0 = new s0; -while(arg) { - v0.v0 = arg; - arg = arg-1; - if (arg==5) ret=v0; - -} -return ret; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", stop.toString()); - } - - - @Test - public void test3() { - Parser parser = new Parser(""" -struct s0 {int v0;}; -s0 !ret = new s0; -while(arg < 10) { - s0 !v0 = new s0; - if (arg == 5) ret=v0; - arg = arg + 1; -} -return ret; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", stop.toString()); - } - - @Test - public void testBug3() { - Parser parser = new Parser(""" -struct s0 { int f0; }; -return new s0; -int v0=null.f0; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return s0;", stop.toString()); - assertEquals("Obj<s0>{f0=0}", Evaluator.evaluate(stop, 0).toString()); - } - - @Test - public void testBug4() { - Parser parser = new Parser(""" -if(0) { - while(0) if(arg) continue; - int v0=0; - while(1) { - int arg=-arg; - v0=arg; - } -} - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testBug5() { - Parser parser = new Parser(""" -struct s0 { - int f0; -}; -if(0) return 0; -else return new s0; -if(new s0.f0) return 0; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return s0;", stop.toString()); - } - - @Test - public void testBug6MissedWorklist() { - Parser parser = new Parser(""" -while(0) {} -int v4=0; -while(0<arg) { - v4=v4+1; - while(1) v4=-v4; - while(0) arg=-1; -} -return 0; - """); - StopNode stop = parser.parse().iterate(); - } - - @Test - public void testBug7() { - Parser parser = new Parser(""" -struct s0 { int f0; }; -s0 v0 = new s0; -while(v0.f0) {} -s0 v1 = v0; -return v1; - """); - StopNode stop = parser.parse().iterate(); - assertEquals("return (const)s0;", stop.toString()); - } - - - @Test - public void testBug8() { - Parser parser = new Parser(""" -int v2=0; -while(0) -while(0) {} -{ - { - { - int v36=0; - { - while(0) { - { - while(-v2) { - { - while(v36) { - while(v2) return 0; - break; - } } - if(-v2) break; - } - } - } - } - } } -} -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testBug9() { - Parser parser = new Parser(""" -int v0=arg==0; -while(v0) continue; -return 0; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test public void testReadBeforeStores() { - var stop = new Parser(""" - struct S { int x; int y; }; - S !a = new S; S !b = new S; - a.x = 11; b.x = 22; - S !p = a; if (arg) p = b; - int before = p.x; - a.y = 55; b.y = 66; - a.x = 33; b.x = 44; - return before*100 + p.x; - """).parse().iterate(); - assertEquals(1133L, com.seaofnodes.simple.evaluator.Evaluator.evaluate(stop,0)); - assertEquals(2244L, com.seaofnodes.simple.evaluator.Evaluator.evaluate(stop,1)); - } - - static final String NESTED_MEMORY = """ - struct S { int x; int y; int z; }; - S !s = new S; - s.x = 5; s.y = 7; s.z = 11; - while (arg > 0) { - s.x = s.x + 1; - int j = 3; - while (j > 0) { - j = j - 1; - if (j == 1) continue; - s.y = s.y + arg; - if (arg == 2) break; - s.z = s.z + 1; - } - arg = arg - 1; - } - return s; - """; - - @Test public void testMemoryAcrossNestedLoops() { - var stop = new Parser(NESTED_MEMORY).parse().iterate(); - for (int arg=0; arg<7; arg++) { - var obj = (com.seaofnodes.simple.evaluator.Evaluator.Obj) - com.seaofnodes.simple.evaluator.Evaluator.evaluate(stop,arg); - org.junit.Assert.assertArrayEquals(new Object[] { - 5L+arg, 7L+arg*(arg+1)-(arg>=2 ? 2 : 0), 11L+2*arg-(arg>=2 ? 2 : 0) - }, obj.fields()); - } - } - - @Test public void testMemoryAtEarlyReturns() { - var stop = new Parser(""" - struct S { int x; int y; int z; }; - S !s = new S; - s.x = 3; s.y = 5; s.z = 7; - if (arg) { s.x = 11; s.z = 13; return s; } - s.y = 17; - return s; - """).parse().iterate(); - for (int arg=0; arg<2; arg++) { - var obj = (com.seaofnodes.simple.evaluator.Evaluator.Obj) - com.seaofnodes.simple.evaluator.Evaluator.evaluate(stop,arg); - org.junit.Assert.assertArrayEquals(arg==0 ? new Object[]{3L,17L,7L} : new Object[]{11L,5L,13L}, obj.fields()); - } - } - - @Test public void testKeepLoadsAtMerge() { - StopNode stop = new Parser(""" - struct S { int x; }; - S !a = new S; S !b = new S; - a.x = arg; b.x = arg+1; - S !p = a; S !q = b; - if (arg<0) { p=b; q=a; } - int v; - if (arg>1) v=p.x; else v=q.x; - return v; - """).parse().iterate(); - // The one-step safety check stops at the memory aggregate. - assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet())); - assertEquals(-1L,Evaluator.evaluate(stop,-1)); - assertEquals(1L,Evaluator.evaluate(stop,0)); - assertEquals(3L,Evaluator.evaluate(stop,3)); - } - - @Test public void testDropStores() { - StopNode stop = new Parser(""" - struct S { int x; }; - S !s = new S; - if (arg) s.x=arg+1; else s.x=arg+2; - return s; - """).parse().iterate(); - assertEquals(1,countMemoryNodes(stop,StoreNode.class,new BitSet())); - assertEquals(2L,((Evaluator.Obj)Evaluator.evaluate(stop,0)).fields()[0]); - assertEquals(4L,((Evaluator.Obj)Evaluator.evaluate(stop,3)).fields()[0]); - } - - @Test public void testKeepReadsBeforeWrites() { - StopNode stop = new Parser(""" - struct S { int x; int y; }; - S !a = new S; S !b = new S; - a.x=arg; b.x=arg+1; - S !p=a; S !q=b; - if (arg<0) { p=b; q=a; } - int v; - if (arg>1) { v=p.x; p.x=41; p.y=5; } - else { v=q.x; q.x=42; } - return v; - """).parse().iterate(); - assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet())); - assertEquals(-1L,Evaluator.evaluate(stop,-1)); - assertEquals(1L,Evaluator.evaluate(stop,0)); - assertEquals(3L,Evaluator.evaluate(stop,3)); - } - - private static int countMemoryNodes(Node n, Class<?> kind, BitSet seen) { - if (n==null || seen.get(n._nid)) return 0; - seen.set(n._nid); - int cnt=kind.isInstance(n) ? 1 : 0; - for (Node def : n._inputs) cnt+=countMemoryNodes(def,kind,seen); - return cnt; - } -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter10bTest.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter10bTest.java deleted file mode 100644 index fd6fdf36..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter10bTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.evaluator.Evaluator; -import org.junit.Test; -import static org.junit.Assert.*; - -public class Chapter10bTest { - @Test public void testIndependentField() { - var stop = new Parser(""" - struct S { int x; int y; }; - S !s = new S; - s.x = 42; - while (arg > 0) { s.y = s.y + arg; arg = arg - 1; } - return s.x; - """).parse().iterate(); - assertEquals("return 42;",stop.toString()); - assertEquals(42L,Evaluator.evaluate(stop,5)); - assertNull(stop.walk(n -> n instanceof LoadNode ? n : null)); - } - - @Test public void testParallelMemoryPhis() { - var stop = new Parser(""" - struct S { int x; int y; int z; }; - S !a = new S; S !b = new S; - S !p = a; if (arg) p = b; - while (arg > 0) { - a.x = p.x + arg; - a.y = p.y + 2; - a.z = p.z + 3; - arg = arg - 1; - } - return a; - """).parse().iterate(); - int[] phis = {0}; - stop.walk(n -> { - if (n instanceof MemPhiNode) phis[0]++; - if (n instanceof MemMergeNode m && m.in(1) instanceof BulkMemPhiNode b) - for(int a=b._aliases.nextSetBit(0); a>=0; a=b._aliases.nextSetBit(a+1)) - assertTrue(a<m.nIns() && m.in(a)!=null); - assertFalse(n instanceof PhiNode && n.isMem() && - !(n instanceof MemPhiNode) && !(n instanceof BulkMemPhiNode)); - return null; - }); - assertTrue(phis[0]>=3); - assertArrayEquals(new Object[]{1L,2L,3L},((Evaluator.Obj)Evaluator.evaluate(stop,4)).fields()); - } - - @Test public void testLateSliceWorklist() throws ReflectiveOperationException { - // Seed 97 exposed a store selecting a bulk predecessor without queuing - // it. Keep both the worklist assertion and the final heap check. - for (int seed : new int[]{0,97,123,456}) { - var parser = new Parser(Chapter10Test.NESTED_MEMORY); - var field = IterPeeps.class.getDeclaredField("WORK"); - field.setAccessible(true); - Object work = field.get(null); - field = work.getClass().getDeclaredField("_R"); - field.setAccessible(true); - ((java.util.Random)field.get(work)).setSeed(seed); - var stop = parser.parse().iterate(); - assertArrayEquals(new Object[]{9L,25L,17L},((Evaluator.Obj)Evaluator.evaluate(stop,4)).fields()); - } - } -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter11Test.java deleted file mode 100644 index b363b9a9..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter11Test.java +++ /dev/null @@ -1,554 +0,0 @@ -package com.seaofnodes.simple; - - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.StopNode; -import org.junit.Test; - - -import static org.junit.Assert.assertEquals; - -public class Chapter11Test { - - // A placeholder test used to rapidly rotate through fuzzer produced issues - @Test - public void testFuzzer() { - Parser parser = new Parser( -""" -while(---- -arg) { - while(-arg) { - while(-----arg*---0) { - int N=--false==false<--arg; - while(N<-4==N) { - N=N+N; - } - } - { - int kzEl_r7EYz=3*----0!=-arg<(37*--97!=0/arg==arg)<=-(--arg!=0); - while(kzEl_r7EYz<---1) { - kzEl_r7EYz=kzEl_r7EYz+kzEl_r7EYz*arg>=24; - if(7) continue; - kzEl_r7EYz=false; - if(-kzEl_r7EYz+arg<-3) continue; - else { - while(kzEl_r7EYz) continue; - arg=----3; - kzEl_r7EYz=-(-----arg*1>-kzEl_r7EYz); - int v2uF_4ba=9; - } - } - } - int pjcp7rv=arg; - { - int m2ya3=--arg-pjcp7rv; - while(m2ya3<-----pjcp7rv>=pjcp7rv+--pjcp7rv!=---pjcp7rv!=-arg<---(-m2ya3)>m2ya3<=-----true>14!=--m2ya3) { - m2ya3=m2ya3+-(--0<=false<pjcp7rv/arg<=-true); - while((--m2ya3)<--arg<-----arg*false!=arg<pjcp7rv*arg) { - pjcp7rv=-3+-arg; - break; - } - while(-3) { - while(67+0-61) m2ya3=m2ya3; - if(----pjcp7rv==---(---arg)) pjcp7rv=0!=-23; - else pjcp7rv=----89; - while(m2ya3!=m2ya3<pjcp7rv) m2ya3=1; - while(--arg<=pjcp7rv) pjcp7rv=-32<=-m2ya3+62; - } - break; - } - } - { - int arg=-1<-9!=(-arg>=-pjcp7rv>=arg<=arg); - while(arg<-arg) { - arg=arg+--arg; - { - while((--93)--pjcp7rv*-pjcp7rv+arg) arg=0-(6>=(--(---41/(0))!=--pjcp7rv)>=---2*33); - pjcp7rv=11; - int c=pjcp7rv; - if(--107) break; - else break; - } - } - } - pjcp7rv=pjcp7rv; - int aR0Wkua=-pjcp7rv==pjcp7rv; - { - if(-0!=-0) while(-true) break; - else { - while(aR0Wkua) pjcp7rv=-0/pjcp7rv; - pjcp7rv=pjcp7rv; - } - } - } - if(-arg) { - { - int Isdlt0fr91=0!=arg; - while(Isdlt0fr91<-Isdlt0fr91==-Isdlt0fr91+true) { - Isdlt0fr91=Isdlt0fr91+-1!=true; - if(-arg) if(--arg<=Isdlt0fr91) Isdlt0fr91=-Isdlt0fr91; - int i=---arg<---arg!=--40; - { - int jVX43Ln4Dm=-(6)==-Isdlt0fr91/----0; - while(jVX43Ln4Dm<--(3-3)) { - jVX43Ln4Dm=jVX43Ln4Dm+-0<25; - i=24<--(jVX43Ln4Dm); - } - } - i=(arg!=----false>=7); - int Te=5>-arg; - break; - } - } - arg=arg!=--true/arg; - { - int UX=arg; - while(UX<-3*arg) { - UX=UX+arg/-1; - arg=-3--(-UX- -47<arg==--arg==arg)/arg- -UX>=-arg; - if(---12) while(-UX) break; - break; - } - } - continue; - } - if(arg) arg=--0; - else if(1) continue; - int DtTlhjYq=19==arg; - { - arg=--DtTlhjYq<=(DtTlhjYq/---DtTlhjYq<=-DtTlhjYq); - if(---arg) continue; - else { - int Zsl=40; - while(Zsl<--Zsl--false>((Zsl))==Zsl!=--DtTlhjYq-DtTlhjYq-DtTlhjYq) { - Zsl=Zsl+arg; - } - } - if((arg)) if(-DtTlhjYq) { - } - else while(((2)==3*arg/DtTlhjYq)<=0==---arg) return false; - else { - int lwbF=arg>=0; - while(lwbF<57) { - lwbF=lwbF+--(-arg+lwbF)>=-2>=arg!=arg; - arg=----0+0; - while(49) break; - if(true) DtTlhjYq=-----arg; - DtTlhjYq=0!=-----(0); - } - } - arg=---arg!=DtTlhjYq/--0; - DtTlhjYq=-DtTlhjYq/(--arg<arg); - if(32) { - int q=-DtTlhjYq; - while(q<arg*(0*q)/0) { - q=q+DtTlhjYq; - DtTlhjYq=arg; - } - } - else if(--arg) if(15) break; - else arg=0==arg<=-55>DtTlhjYq; - else { - } - DtTlhjYq=-0; - { - while((0)<-arg) DtTlhjYq=-DtTlhjYq; - } - if(30/--(arg)>=0) DtTlhjYq=-0>=arg; - else { - int Y2wcIQJ=--DtTlhjYq*---11; - if(arg==arg) { - Y2wcIQJ=-----Y2wcIQJ; - Y2wcIQJ=Y2wcIQJ<7; - if(DtTlhjYq) arg=arg; - else break; - while(-arg!=-31==(false<=-15>=-DtTlhjYq+--6*-----Y2wcIQJ<=---arg!=-Y2wcIQJ<=---0<-arg*--Y2wcIQJ)) DtTlhjYq=arg!=3; - } - continue; - } - arg=DtTlhjYq; - arg=--3/(---arg)>=80>=-DtTlhjYq<=DtTlhjYq; - arg=51+---(-true)!=arg!=----1==-DtTlhjYq; - if(11<=arg) if((arg)+79<32+(-arg)) continue; - else if(true==DtTlhjYq) break; - break; - } -} -if(arg!=62==arg) { - while(---arg) { - { - int bhmDacPM=arg/arg; - while(bhmDacPM<arg) { - bhmDacPM=bhmDacPM+80*((-bhmDacPM)/---bhmDacPM>1)<bhmDacPM-2!=7; - arg=-0; - arg=bhmDacPM>=-arg+-arg; - break; - } - } - int arg=true; - arg=arg<=arg; - { - int KlC=30; - while(KlC<-2) { - KlC=KlC+-1!=25<=-KlC>KlC>----0; - int Ij=arg>=-arg<=---10; - { - int LAbgurVa2R=-arg!=Ij; - while(LAbgurVa2R<-true) { - LAbgurVa2R=LAbgurVa2R+Ij; - KlC=-LAbgurVa2R; - Ij=42*(-Ij>true); - while(Ij) arg=KlC; - KlC=-----7>-(--KlC<--7)==-LAbgurVa2R; - } - } - int a=Ij<=-KlC; - if(Ij) { - int r1W=a!=-false; - } - else { - arg=56>=Ij; - while(54) break; - arg=8>=-(a<--Ij)/a>=-a-KlC; - } - int Yt_T=KlC; - arg=KlC; - } - } - if(----false/--arg/12+-----0) if(-9+-arg) while(arg!=(arg)) arg=--1>=-71-arg!=---(--arg*11+102)/--arg; - else if(----arg) continue; - if(-arg-arg) { - arg=-(-arg)*--(-3)<(-7==-arg----arg); - { - int arg=arg; - while(arg<-0>arg==-arg-(-arg>1+11)) { - arg=arg+6-arg!=--0>---30>=---arg<=arg/(3)+---arg; - } - } - arg=-arg+arg; - arg=--47+-arg<1>arg*(-arg==-arg- -0>3); - arg=(----arg)!=arg+--59; - arg=- -4+arg==-(arg)-arg; - } - else { - while(--true-----true*-25+-18>arg>=-----(-arg)---13) { - arg=-0+--true<false; - continue; - } - arg=true<=arg; - break; - } - arg=arg*22<--(0+-arg); - { - continue; - } - } - arg=1*---((3<=true)==---2); - arg=7+-(-1); - return arg+--true; -} -else { - int o3ypEsewxM=-(---arg); - int vzXB=(-(---11<=--o3ypEsewxM>=--0))<=-0/-arg; - int L=-arg; - int B4mHcrf=---vzXB; - int oZWGdzZl=-o3ypEsewxM; - int K6tQqa1=11; - while(0!=--L) { - int sdZfTM=vzXB*true+K6tQqa1; - while(sdZfTM<false) { - sdZfTM=sdZfTM+arg; - sdZfTM=oZWGdzZl; - K6tQqa1=-K6tQqa1; - arg=---K6tQqa1; - while((3+-K6tQqa1)<oZWGdzZl) { - if(-----(-1)) sdZfTM=-false; - {} - } - K6tQqa1=vzXB!=-(B4mHcrf+-7>false); - sdZfTM=vzXB; - } - } - L=false; - while(31) B4mHcrf=arg; - oZWGdzZl=-0<--oZWGdzZl<=--2; - if(K6tQqa1>=20<=46) { - if(-1+-L) if(oZWGdzZl) { - int wmhv=-----0; - } - int arg=0; - o3ypEsewxM=---(oZWGdzZl); - return -oZWGdzZl/0; - } - else if(arg) { - int yKk5=B4mHcrf>=4>K6tQqa1<-B4mHcrf/o3ypEsewxM; - return -K6tQqa1; - } - else { - if(---0) { - {} - {} - } - return o3ypEsewxM; - } -} -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("Stop[ return 9; return 0; return 0; return 0; return 0; ]", stop.toString()); - } - - - @Test - public void testPrimes() { - Parser parser = new Parser( -""" -if( arg < 2 ) return 0; -int primeCount = 1; -int prime = 3; -while( prime <= arg ) { - int isPrime = 1; - // Check for even case, so the next loop need only check odds - if( (prime/2)*2 == prime ) - continue; - // Check odds up to sqrt of prime - int j = 3; - while( j*j <= prime ) { - if( (prime/j)*j == prime ) { - isPrime = 0; - break; - } - j = j + 2; - } - if( isPrime ) - primeCount = primeCount + 1; - prime = prime + 2; -} -return primeCount; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("Stop[ return 0; return Phi(Loop,1,Phi(Region,Phi_primeCount,Phi(Region,(Phi_primeCount+1),Phi_primeCount))); ]", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 1)); // No primes 1 or below - assertEquals(1L, Evaluator.evaluate(stop, 2)); // 2 - assertEquals(2L, Evaluator.evaluate(stop, 3)); // 2, 3 - assertEquals(2L, Evaluator.evaluate(stop, 4)); // 2, 3 - assertEquals(3L, Evaluator.evaluate(stop, 5)); // 2, 3, 5 - assertEquals(4L, Evaluator.evaluate(stop, 10)); // 2, 3, 5, 7 - } - - @Test - public void testAntiDeps1() { - Parser parser = new Parser( -""" -struct S { int f; }; -S !v=new S; -v.f = 2; -int i=new S.f; -i=v.f; -if (arg) v.f=1; -return i; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 2;", stop.toString()); - assertEquals(2L, Evaluator.evaluate(stop, 0)); - assertEquals(2L, Evaluator.evaluate(stop, 1)); - } - - @Test - public void testAntiDeps2() { - Parser parser = new Parser( -""" -struct S { int f; }; -S !v = new S; -v.f = arg; -S !t = new S; -int i = 0; -if (arg) { - if (arg+1) v = t; - i = v.f; -} else { - v.f = 2; -} -return i; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,.f,0);", stop.toString()); - } - - @Test - public void testAntiDeps3() { - Parser parser = new Parser( -""" -struct S { int f; }; -S !v0 = new S; -S? v1; -if (arg) v1 = new S; -if (v1) { - v0.f = v1.f; -} else { - v0.f = 2; -} -return v0; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return S;", stop.toString()); - } - - - @Test - public void testAntiDeps4() { - Parser parser = new Parser( -""" -struct S { int f; }; -S !v = new S; -v.f = arg; -S t = new S; -int i = v.f; -if (arg+1) arg= 0; -while (arg) v.f = 2; -return i; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return arg;", stop.toString()); - } - - @Test - public void testAntiDeps5() { - Parser parser = new Parser( -""" -struct S { int f; }; -S !v = new S; -while(1) { - while(arg+1) { arg=arg-1; } - if (arg) break; - v.f = 2; -} -return v; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return S;", stop.toString()); - } - - @Test - public void testAntiDeps6() { - Parser parser = new Parser( -""" -struct s { int v; }; -s !ptr=new s; -while( -arg ) - ptr = new s; -while(1) - arg = arg+ptr.v; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testAntiDeps7() { - Parser parser = new Parser( -""" -struct S { int f; }; -S !v = new S; -S t = new S; -int i = v.f; -while (arg) { - v.f = arg; - arg = arg-1; -} -return i; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testAntiDeps8() { - Parser parser = new Parser( -""" -struct S { int f; }; -S !v = new S; -S t = new S; -while(arg) { - arg=arg-1; - int f = v.f; - v.f = 2; - if (arg) arg = f; - else v.f = 3; -} -return arg; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testAntiDeps9() { - Parser parser = new Parser( -""" -struct S { int f; }; -S !v = new S; -S t = new S; -if (arg) { - v.f=2; - int i=t.f; - v.f=i; -} -return v; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return S;", stop.toString()); - } - - @Test - public void testExample2() { - Parser parser = new Parser( -""" -struct S { int f; }; -S !v = new S; -int i = arg; -while (arg > 0) { - int j = i/3; - if (arg == 5) - v.f = j; - arg = arg - 1; -} -return v; - """); - StopNode stop = parser.parse().iterate(); - //assertEquals("return new S;", stop.toString()); - } - - @Test - public void testScheduleUse() { - Parser parser = new Parser( -""" -int v0=0; -while(0>=0) { - u1 v1=0; - v1=v0; - if(v1*0) - v0=-v1; -} -return 0; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testLoopCarriedDep() { - Parser parser = new Parser( -""" -u32 v0=0; -{ - int v1=0; - while(v1) { - v1=1>>>v0!=0; - v0=v1/0; - } -} -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter12Test.java deleted file mode 100644 index f7efde96..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter12Test.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.StopNode; -import org.junit.Ignore; -import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class Chapter12Test { - @Test public void testSubZeroFloat() { - var stop = new Parser("flt x = arg; return 0-x;").parse().iterate(); - // Compare strings so +0.0 and -0.0 remain distinct. - assertEquals("0.0", Evaluator.evaluate(stop,0).toString()); - assertEquals("-1.0", Evaluator.evaluate(stop,1).toString()); - } - - @Test public void testDeadNumericReturns() { - for( String[] test : new String[][] { - {"if(1) return 7; else return 2.5;", "7"}, - {"if(0) return 7; else return 2.5;", "2.5"}, - {"return 7; return 2.5;", "7"}, - {"return 2.5; return 7;", "2.5"} - } ) { - String src = test[0]; - var stop = new Parser(src).parse().iterate(); - assertEquals(src,test[1],com.seaofnodes.simple.evaluator.Evaluator.evaluate(stop,0).toString()); - } - } - - - @Test - public void testJig() { - Parser parser = new Parser( -""" -return 3.14; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3.14;", stop.toString()); - assertEquals(3.14, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testFloat() { - Parser parser = new Parser( -""" -return 3.14; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3.14;", stop.toString()); - assertEquals(3.14, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testSquareRoot() { - Parser parser = new Parser( -""" -flt guess = arg; -while( 1 ) { - flt next = (arg/guess + guess)/2; - if( next == guess ) break; - guess = next; -} -return guess; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)/2.0));", stop.toString()); - assertEquals(3.0, Evaluator.evaluate(stop, 9)); - assertEquals(1.414213562373095, Evaluator.evaluate(stop, 2)); - } - - @Test - public void testFPOps() { - Parser parser = new Parser( -""" -flt x = arg; -return x+1==x; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return ((flt)arg==(ToFloat+1.0));", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 1)); - } -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter13Test.java deleted file mode 100644 index a1fdeb3a..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter13Test.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.StopNode; -import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class Chapter13Test { - @Test - public void testJig() { - Parser parser = new Parser( -""" -return 3.14; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3.14;", stop.toString()); - assertEquals(3.14, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testLinkedList0() { - Parser parser = new Parser( -""" -struct LLI { LLI? next; int i; }; -LLI? !head = null; -while( arg ) { - LLI !x = new LLI; - x.next = head; - x.i = arg; - head = x; - arg = arg-1; -} -return head.next.i; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); } - } - - @Test - public void testLinkedList1() { - Parser parser = new Parser( -""" -struct LLI { LLI? next; int i; }; -LLI? !head = null; -while( arg ) { - LLI !x = new LLI; - x.next = head; - x.i = arg; - head = x; - arg = arg-1; -} -if( !head ) return 0; -LLI? next = head.next; -if( next==null ) return 1; -return next.i; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("Stop[ return 0; return 1; return .i; ]", stop.toString()); - assertEquals(2L, Evaluator.evaluate(stop, 3)); - } - - @Test - public void testCoRecur() { - Parser parser = new Parser( -""" -struct int0 { int i; flt0? f; }; -struct flt0 { flt f; int0? i; }; -int0 !i0 = new int0; -i0.i = 17; -flt0 !f0 = new flt0; -f0.f = 3.14; -i0.f = f0; -f0.i = i0; -return f0.i.f.i.i; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 17;", stop.toString()); - } - - @Test - public void testNullRef0() { - Parser parser = new Parser( -""" -struct N { N? next; int i; }; -N n = new N; -return n.next; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return null;", stop.toString()); - } - - @Test - public void testNullRef1() { - Parser parser = new Parser( -""" -struct M { int m; }; -struct N { M next; int i; }; -N n = new N { next = new M; }; -return n.next; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return (const)M;", stop.toString()); - } - - @Test - public void testNullRef2() { - Parser parser = new Parser( -""" -struct M { int m; }; -struct N { M next; int i; }; -N n = new N { next = null; } -return n.next; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Type null is not of declared type *M",e.getMessage()); } - } - - @Test - public void testNullRef3() { - Parser parser = new Parser( -""" -struct N { N? next; int i; }; -N !n = new N; -n.i = 3.14; -return n.i; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Type 3.14 is not of declared type int",e.getMessage()); } - } - - @Test - public void testNullRef4() { - Parser parser = new Parser("-null-5/null-5;"); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); } - } - - @Test public void testNullRef5() { - Parser parser = new Parser("return null+42;"); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); } - } - - @Test - public void testEmpty() { - Parser parser = new Parser( -""" -struct S{}; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testForwardRef0() { - Parser parser = new Parser( -""" -struct S1 { S2? s; }; -return new S2; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); } - } - - @Test - public void testForwardRef1() { - Parser parser = new Parser( -""" -struct S1 { S2? s; }; -struct S2 { int x; }; -return new S1.s=new S2; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return S2;", stop.toString()); - } - - @Test - public void testcheckNull() { - Parser parser = new Parser( -""" -struct I {int i;}; -struct P { I? pi; }; -P !p1 = new P; -P !p2 = new P; -p2.pi = new I; -p2.pi.i = 2; -if (arg) p1 = new P; -return p1.pi.i + 1; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); } - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter14Test.java deleted file mode 100644 index 6846e7af..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter14Test.java +++ /dev/null @@ -1,227 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.StopNode; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class Chapter14Test { - - @Test - public void testJig() { - Parser parser = new Parser( -""" -return 3.14; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3.14;", stop.toString()); - assertEquals(3.14, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testRange() { - Parser parser = new Parser( -""" -int b; -if( arg ) b=1; else b=0; -int c = 99; -if( b < 0 ) c = -1; -if( b > 2 ) c = 1; -return c; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 99;", stop.toString()); - assertEquals(99L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testU8() { - Parser parser = new Parser( -""" -u8 b = 123; -b = b + 456;// Truncate -return b; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 67;", stop.toString()); - assertEquals(67L, Evaluator.evaluate(stop, 0)); - } - - - @Test - public void testU8While() { - Parser parser = new Parser( -""" -u8 b = 123; -while( b ) b = b + 456;// Truncate -return b; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testU1() { - Parser parser = new Parser( -""" -bool b = 123; -b = b + 456;// Truncate -u1 c = b; // No more truncate needed -return c; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 1;", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testAnd() { - Parser parser = new Parser( -""" -int b = 123; -b = b+456 & 31; // Precedence -return b; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3;", stop.toString()); - assertEquals(3L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testRefLoad() { - Parser parser = new Parser( -""" -struct Foo { u1 b; }; -Foo !f = new Foo; -f.b = 123; -return f.b; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 1;", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testSigned() { - Parser parser = new Parser( -""" -i8 b = 255; // Chopped -return b; // Sign extend -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return -1;", stop.toString()); - assertEquals(-1L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testI8() { - Parser parser = new Parser( -""" -i8 b = arg; -b = b + 1;// Truncate -return b; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return (((((arg<<56)>>56)+1)<<56)>>56);", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 0)); - assertEquals(-128L, Evaluator.evaluate(stop, 127)); - } - - @Test - public void testMask() { - Parser parser = new Parser( -""" -u16 mask = (1<<16)-1; // AND mask -int c = 123456789 & mask; -return c; // -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 52501;", stop.toString()); - assertEquals(52501L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testOr() { - Parser parser = new Parser( -""" -return (arg | 123 ^ 456) >>> 1; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return (((arg|123)^456)>>>1);", stop.toString()); - assertEquals(217L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testMaskFloat() { - Parser parser = new Parser( -""" -flt f = arg; -arg = f & 0; -return arg; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot '&' FltBot",e.getMessage()); } - } - - @Test - public void testCloneAnd() { - Parser parser = new Parser(""" -int v0=0; -u32 v1 = 1&(1<<arg)&(1<<arg); -while(arg) v1=-v0; -while(v1) break; -return v1; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,(((1<<arg)&1)&Shl),0);", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testAndHigh() { - Parser parser = new Parser(""" -int v0=0; -if(0&0>>>0) { - while(0) { - u8 v1=0; - v0=0>>>0; - v1=arg; - while(v1+0) {} - } -} -return v0; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testTypes() { - Parser parser = new Parser( -""" -i8 xi8 = 123456789; if( xi8 != 21 ) return -8; -i16 xi16 = 123456789; if( xi16 != -13035 ) return -16; -i32 xi32 = 123456789; if( xi32 != 123456789 ) return -32; -i64 xi64 = 123456789; if( xi64 != 123456789 ) return -64; -int xint = 123456789; if( xint != 123456789 ) return -64; - -u1 ui1 = 123456789; if( ui1 != 1 ) return 1; -u8 ui8 = 123456789; if( ui8 != 21 ) return 8; -u16 ui16 = 123456789; if( ui16 != 52501 ) return 16; -u32 ui32 = 123456789; if( ui32 != 123456789 ) return 32; - -flt fflt = 3.141592653589793; if( fflt != 3.141592653589793 ) return 3; -f64 ff64 = 3.141592653589793; if( ff64 != 3.141592653589793 ) return 3; -f32 ff32 = 3.141592653589793; if( ff32 != 3.1415927410125732) return 5; - -return 0; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 0)); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter15Test.java deleted file mode 100644 index e66a009b..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter15Test.java +++ /dev/null @@ -1,387 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.StopNode; -import org.junit.Test; -import org.junit.Ignore; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class Chapter15Test { - - @Test - public void testJig() { - Parser parser = new Parser( -""" -return 3.14; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3.14;", stop.toString()); - assertEquals(3.14, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testCyclic() { - Parser parser = new Parser( -""" -struct C { C? l; }; -C !c = new C; -c.l = c; -return c; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return C;", stop.toString()); - assertEquals("Obj<C>@1{l=obj@1}", Evaluator.evaluate(stop, 0).toString()); - } - - @Test - public void testSafetyCheck() { - Parser parser = new Parser( -""" -u8[] old = new u8[0]; -u8[] !output = new u8[1]; -int i = 0; -while (i < old#) { - output[i] = old[i]; - i = i + 1; -} -output[i] = 1; -return output; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return [u8];", stop.toString()); - assertEquals("\u0001", Evaluator.evaluate(stop, 0).toString()); - } - - @Test - public void testBasic1() { - Parser parser = new Parser( -""" -int[] is = new int[2]; -return is[1]; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testBasic2() { - Parser parser = new Parser( -""" -int[] is = new int[2]; -int[] is2 = new int[2]; -return is[1]; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testBasic3() { - Parser parser = new Parser( -""" -int[] !a = new int[2]; -a[0] = 1; -a[1] = 2; -return a[0]; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 1;", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testBasic4() { - Parser parser = new Parser( -""" -struct A { int i; }; -A?[] !a = new A?[2]; -return a; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return [*A?];", stop.toString()); - assertEquals("Obj<[*A?]>{#=2,[]=[null,null]}", Evaluator.evaluate(stop, 0).toString()); - } - - @Test - public void testBasic5() { - Parser parser = new Parser( -""" -struct S { int x; flt y; }; -// A new S -S !s = new S; s.x=99; s.y = 3.14; - -// Double-d array of Ss. Fill in one row. -S?[]?[] !iss = new S?[]?[2]; -iss[0] = new S?[7]; -iss[0][2] = s; - -// Now pull out the filled-in value, with null checks -flt rez; -S?[]? is = iss[arg]; -if( !is ) rez = 1.2; -else { - S? i = is[2]; - if( !i ) rez = 2.3; - else rez = i.y; -} -return rez; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,1.2,Phi(Region,2.3,.y));", stop.toString()); - assertEquals(3.14, Evaluator.evaluate(stop, 0)); - assertEquals(1.2 , Evaluator.evaluate(stop, 1)); - } - - @Test - public void testBasic6() { - Parser parser = new Parser( -""" -struct S { int x; flt y; }; -// A new S -S !s = new S; s.x=99; s.y = 3.14; - -// Double-d array of Ss. Fill in one row. -S?[]?[] iss = new S?[]?[2]; -iss[0] = new S?[7]; -iss[0][2] = s; - -// Now pull out the filled-in value, with null checks -flt rez = 1.2; -if( iss[arg] ) - if( iss[arg][2] ) - rez = iss[arg][2].y; -return rez; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Might be null accessing '[]'",e.getMessage()); } - } - - @Test - public void testTree() { - Parser parser = new Parser( -""" -// Can we define a forward-reference array? -struct Tree { Tree?[]? _kids; }; -Tree !root = new Tree; -root._kids = new Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES???? -root._kids[0] = new Tree; -return root; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Tree;", stop.toString()); - assertEquals("Obj<Tree>{_kids=Obj<[*Tree?]>{#=2,[]=[Obj<Tree>{_kids=null},null]}}", Evaluator.evaluate(stop, 0).toString()); - } - - @Test - public void testNestedStructAddMemProj() { - Parser parser = new Parser( -""" -struct S { int a; int[] b; }; -return 0; -"""); - StopNode stop = parser.parse(); - assertEquals("return 0;", stop.toString()); - } - - @Test - public void testRollingSum() { - Parser parser = new Parser( -""" -int[] !ary = new int[arg]; -// Fill [0,1,2,3,4,...] -int i=0; -while( i < ary# ) { - ary[i] = i; - i = i+1; -} -// Fill [0,1,3,6,10,...] -i=0; -while( i < ary# - 1 ) { - ary[i+1] = ary[i+1] + ary[i]; - i = i+1; -} -return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return (.[]+(.[]*1000));", stop.toString()); - assertEquals(1006L, Evaluator.evaluate(stop, 4)); - } - - @Test - public void sieveOEratosthenes() { - Parser parser = new Parser( -""" -int[] !ary = new int[arg], !primes = new int[arg]; -int nprimes = 0, p=2; -// Find primes while p^2 < arg -while( p*p < arg ) { - // skip marked non-primes - while( ary[p]==1 ) p = p + 1; - // p is now a prime - primes[nprimes] = p; nprimes = nprimes + 1; - // Mark out the rest non-primes - int i = p + p; - while( i < ary# ) { - ary[i] = 1; - i = i + p; - } - p = p + 1; -} -// Now just collect the remaining primes, no more marking -while( p < arg ) { - if( ary[p] == 0 ) { - primes[nprimes] = p; nprimes = nprimes + 1; - } - p = p + 1; -} -// Copy/shrink the result array -int[] !rez = new int[nprimes]; -int j = 0; -while( j < nprimes ) { - rez[j] = primes[j]; - j = j + 1; -} -return rez; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return [int];", stop.toString()); - Evaluator.Obj obj = (Evaluator.Obj)Evaluator.evaluate(stop, 20); - assertEquals("[int] {int #; int ![]; }",obj.struct().toString()); - long nprimes = (Long)obj.fields()[0]; - long[] primes = new long[]{2,3,5,7,11,13,17,19}; - for( int i=0; i<nprimes; i++ ) - assertEquals(primes[i],(long)(Long)obj.fields()[i+1]); - } - - @Test - public void testNewNodeInit() { - Parser parser = new Parser( -""" -struct S {int i; flt f;}; -S !s1 = new S; -S !s2 = new S; -s2.i = 3; -s2.f = 2.0; -if (arg) s1 = new S; -return s1.i + s1.f; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return ((flt).i+.f);", stop.toString()); - assertEquals(0.0, Evaluator.evaluate(stop, 0)); - } - - - @Test - public void testBad0() { - Parser parser = new Parser( -""" -return new flt; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); } - } - - @Test - public void testBad1() { - Parser parser = new Parser( -""" -int is = new int[2]; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Type *[int] is not of declared type int",e.getMessage()); } - } - - @Test - public void testBad2() { - Parser parser = new Parser( -""" -int[] is = new int[3.14]; -return is[1]; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot allocate an array with length 3.14",e.getMessage()); } - } - - @Test - public void testBad3() { - Parser parser = new Parser( -""" -int[] is = new int[arg]; -return is[1]; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 4)); - try { Evaluator.evaluate(stop,0); } - catch( ArrayIndexOutOfBoundsException e ) { assertEquals("Array index 1 out of bounds for array length 0",e.getMessage()); } - try { Evaluator.evaluate(stop,-1); } - catch( NegativeArraySizeException e ) { assertEquals("-1",e.getMessage()); } - } - - @Test - public void testProgress() { - Parser parser = new Parser(""" -i8 v1=0&0; -u8 v2=0; -byte v4=0; -if(0) {} -while(v2<0) { - v4=0-v1; - break; -} -int v5=0&0; -while(v5+(0&0)) { - int v7=0&0; - while(v7) - v4=0>>>v5; - while(v1) - return 0; -} -return v1; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testSharpNot() { - Parser parser = new Parser( -""" -if(0>>0) {} -while(0) {} -u32 v7=0; -int v8=0; -while(0<--1>>>---(v7*0==v8)) {} -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } - - - @Test - public void testProgress2() { - Parser parser = new Parser( -""" -if(1) {} -else { - while(arg>>>0&0>>>0) {} - byte v3=0>>>0; - while(0) {} - int v7=0>>>0; - while(v7<0>>>0) { - while(0+v7<=0) if(1) arg=-12; - if(arg) { - v3=arg+v3+0; - arg=0; - } - } -} -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - } -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter16Test.java deleted file mode 100644 index d9edf0e6..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter16Test.java +++ /dev/null @@ -1,280 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.StopNode; -import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class Chapter16Test { - - @Test - public void testJig() { - Parser parser = new Parser( -""" -return 3.14; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3.14;", stop.toString()); - assertEquals(3.14, Evaluator.evaluate(stop, 0)); - } - - - @Test - public void testMulti0() { - Parser parser = new Parser( -""" -int x, y; -return x+y; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 0)); - } - @Test - public void testMulti1() { - Parser parser = new Parser( -""" -int x=2, y=x+1; -return x+y; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 5;", stop.toString()); - assertEquals(5L, Evaluator.evaluate(stop, 0)); - } - -// @Test -// public void testFinal0() { -// Parser parser = new Parser( -//""" -//int !x=2; -//x=3; -//return x; -//"""); -// try { parser.parse().iterate(); fail(); } -// catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } -// } - - @Test - public void testFinal1() { - Parser parser = new Parser( -""" -int x=2, y=3; -if( arg ) { int x = y; x = x*x; y=x; } // Shadow final x -return y; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,9,3);", stop.toString()); - assertEquals(3L, Evaluator.evaluate(stop, 0)); - assertEquals(9L, Evaluator.evaluate(stop, 1)); - } - - @Test - public void testConstruct0() { - Parser parser = new Parser(""" -struct X { int x=3; }; -X z = new X; -return z.x; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3;", stop.toString()); - assertEquals(3L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testConstruct1() { - Parser parser = new Parser(""" -struct X { int !x; }; -X z = new X { x=3; }; -return z.x; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 3;", stop.toString()); - assertEquals(3L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testConstruct2() { - Parser parser = new Parser(""" -struct X { int x=3; }; -X z = new X { x = 4; }; -return z.x; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 4;", stop.toString()); - assertEquals(4L, Evaluator.evaluate(stop, 0)); - } - - - @Test - public void testStructFinal0() { - Parser parser = new Parser(""" -struct Point { int !x, !y; }; -Point p = new Point { x=3; y=4; }; -return p; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return (const)Point;", stop.toString()); - assertEquals("Obj<Point>{x=3,y=4}", Evaluator.evaluate(stop, 0).toString()); - } - - @Test - public void testStructFinal1() { - Parser parser = new Parser(""" -struct Point { int x=3, y=4; }; -Point p = new Point { x=5; y=6; }; -p.x++; -return p; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } - } - - @Test - public void testStructFinal2() { - Parser parser = new Parser(""" -struct Point { int x=3, y=4; }; -Point p = new Point; -p.x++; -return p; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } - } - - @Test - 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 Parser(src).parse().iterate(); fail(fields); } - catch( Exception e ) { assertEquals("'Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); } - } - } - - @Test - public void testStructFinal4() { - Parser parser = new Parser(""" -struct Point { val x=3; val y=4; }; -Point p = new Point; -p.x++; -return p; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } - } - - @Test - public void testStructFinal5() { - Parser parser = new Parser(""" -struct Point { var x=3; var y=4; }; -Point !p = new Point; -p.x++; -return p; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Point;", stop.toString()); - assertEquals("Obj<Point>{x=4,y=4}", Evaluator.evaluate(stop, 0).toString()); - } - - // Same as the Chapter13 test with the same name, but using the new - // constructor syntax - @Test - public void testLinkedList1() { - Parser parser = new Parser( -""" -struct LLI { LLI? next; int i; }; -LLI? !head = null; -while( arg ) { - head = new LLI { next=head; i=arg; }; - arg = arg-1; -} -if( !head ) return 0; -LLI? next = head.next; -if( !next ) return 1; -return next.i; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("Stop[ return 0; return 1; return .i; ]", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 0)); - assertEquals(1L, Evaluator.evaluate(stop, 1)); - assertEquals(2L, Evaluator.evaluate(stop, 3)); - } - - @Test - public void testLinkedList2() { - Parser parser = new Parser( -""" -struct LLI { LLI? next; int i; }; -LLI? !head = null; -while( arg ) { - head = new LLI { - next=head; - // Any old code in the constructor - int !tmp=arg; - while( arg > 10 ) { - tmp = tmp + arg; - arg = arg - 1; - } - i=tmp; - }; - arg = arg-1; -} -if( !head ) return 0; -LLI? next = head.next; -if( !next ) return 1; -return next.i; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("Stop[ return 0; return 1; return .i; ]", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 0)); - assertEquals(1L, Evaluator.evaluate(stop, 1)); - assertEquals(2L, Evaluator.evaluate(stop, 11)); - } - - @Test - public void testSquare() { - Parser parser = new Parser( -""" -struct Square { - flt !side = arg; - // Newtons approximation to the square root, computed in a constructor. - // The actual allocation will copy in this result as the initial - // value for 'diag'. - flt !diag = arg*arg/2; - while( 1 ) { - flt next = (side/diag + diag)/2; - if( next == diag ) break; - diag = next; - } -}; -return new Square; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Square;", stop.toString()); - assertEquals("Obj<Square>{side=3.0,diag=1.7320508075688772}", Evaluator.evaluate(stop, 3).toString()); - assertEquals("Obj<Square>{side=4.0,diag=2.0}", Evaluator.evaluate(stop, 4).toString()); - } - static final String CONSTRUCTOR_MEMORY = """ - struct S { int x; int y; }; - struct T { int z=arg+40; }; - T !t = new T; - S !a = new S { x=11; y=7; }; - S !b = new S { x=22; y=9; }; - S !p=a; - if (arg) p=b; - int before=p.x; - S !c = new S { - x=p.x+1; - { int i=0; while (i<2) { p.y=p.y+1; i=i+1; } } - y=p.y; - }; - p.x=33; - return before*10000+c.x*100+c.y+t.z; - """; - - @Test public void testConstructorMemory() { - StopNode stop = new Parser(CONSTRUCTOR_MEMORY).parse().iterate(); - assertEquals(111249L,Evaluator.evaluate(stop,0)); - assertEquals(222352L,Evaluator.evaluate(stop,1)); - } -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java deleted file mode 100644 index 1ca9a515..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java +++ /dev/null @@ -1,500 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.StopNode; -import org.junit.Test; -import org.junit.Ignore; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -public class Chapter17Test { - - @Test - public void testJig() { - Parser parser = new Parser("return 1;"); - StopNode stop = parser.parse().iterate(); - assertEquals("return 1;", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 0)); - } - - - // --------------------------------------------------------------- - @Test - public void testInc0() { - Parser parser = new Parser(""" -return arg++; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return arg;", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testInc1() { - Parser parser = new Parser(""" -return arg+++arg++; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return ((arg*2)+1);", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testInc2() { - Parser parser = new Parser(""" -// -(arg--)-(arg--) -return -arg---arg--; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return (-((arg*2)+-1));", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testInc3() { - Parser parser = new Parser(""" -int[] !xs = new int[arg]; -xs[0]++; -xs[1]++; -return xs[0]; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 1;", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 2)); - } - - @Test - public void testInc4() { - Parser parser = new Parser(""" -u8[] !xs = new u8[1]; -xs[0]--; -return xs[0]; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 255;", stop.toString()); - assertEquals(255L, Evaluator.evaluate(stop, 2)); - } - - @Test - public void testInc5() { - Parser parser = new Parser(""" -struct S { u16 x; }; -S !s = new S; -s.x--; -return s.x; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 65535;", stop.toString()); - assertEquals(65535L, Evaluator.evaluate(stop, 2)); - } - - @Test - public void testInc6() { - Parser parser = new Parser("return --arg;"); - StopNode stop = parser.parse().iterate(); - assertEquals("return (arg+-1);", stop.toString()); - assertEquals(-1L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testInc7() { - Parser parser = new Parser("u8 x=0; return --x;"); - StopNode stop = parser.parse().iterate(); - assertEquals("return 255;", stop.toString()); - assertEquals(255L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testInc8() { - Parser parser = new Parser("int x; x+=2; return x+=3;"); - StopNode stop = parser.parse().iterate(); - assertEquals("return 5;", stop.toString()); - assertEquals(5L, Evaluator.evaluate(stop, 0)); - } - - // --------------------------------------------------------------- - @Test public void testVar0() { - Parser parser = new Parser("var d; return d;"); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); } - } - - @Test public void testVar1() { - Parser parser = new Parser("val d; return d;"); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); } - } - - @Test public void testVar2() { - Parser parser = new Parser("int x; x=3; x++; return x; // Ok, no initializer so x is mutable "); - StopNode stop = parser.parse().iterate(); - assertEquals("return 4;", stop.toString()); - assertEquals(4L, Evaluator.evaluate(stop, 0)); - } - - @Test public void testVar3() { - Parser parser = new Parser("int x=3; x++; return x; // Ok, primitive so x is mutable despite initializer "); - StopNode stop = parser.parse().iterate(); - assertEquals("return 4;", stop.toString()); - assertEquals(4L, Evaluator.evaluate(stop, 0)); - } - - @Test public void testVar4() { - Parser parser = new Parser("struct S{int x;}; S s; s=new S; s.x++; return s.x; // Ok, no initializer so x is mutable "); - StopNode stop = parser.parse().iterate(); - assertEquals("return 1;", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 0)); - } - - @Test public void testVar5() { - Parser parser = new Parser("struct S{int x;}; S s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable "); - StopNode stop = parser.parse().iterate(); - assertEquals("return 4;", stop.toString()); - assertEquals(4L, Evaluator.evaluate(stop, 0)); - } - - @Test public void testVar6() { - Parser parser = new Parser("struct S{int x;}; S s=new S; s.x++; return s.x; // Error initializer so x is immutable "); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } - } - - @Test public void testVar7() { - Parser parser = new Parser("struct S{int x;}; S s=new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable "); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } - } - - @Test public void testVar8() { - Parser parser = new Parser("struct S{int x;}; S !s=new S; s.x++; return s.x; // Ok, has '!' so s.x is mutable "); - StopNode stop = parser.parse().iterate(); - assertEquals("return 1;", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 0)); - } - - @Test public void testVar9() { - Parser parser = new Parser("struct S{int x;}; var s=new S; s.x++; return s.x; // Ok, has var so s.x is mutable "); - StopNode stop = parser.parse().iterate(); - assertEquals("return 1;", stop.toString()); - assertEquals(1L, Evaluator.evaluate(stop, 0)); - } - - @Test public void testVar10() { - Parser parser = new Parser("struct S{int x;}; val s=new S; s.x++; return s.x; // Error, has val so x is immutable "); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } - } - - @Test public void testVar11() { - Parser parser = new Parser(""" -struct Bar { int x; }; -Bar !bar = new Bar; -bar.x = 3; // Ok, bar is mutable - -struct Foo { Bar? !bar; int y; }; -Foo !foo = new Foo { bar = bar; }; -foo.bar = bar; // Ok foo is mutable -foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable - -val xfoo = foo; // Throw away mutability -xfoo.bar.x++; // Error, cannot mutate through xfoo - """); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } - } - - @Test public void testVar12() { - Parser parser = new Parser(""" -struct Bar { int x; }; -Bar !bar = new Bar; -bar.x = 3; // Ok, bar is mutable - -struct Foo { Bar? !bar; int y; }; -Foo !foo = new Foo; -foo.bar = bar; // Ok bar is mutable -foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable - -val xfoo = foo; // Throw away mutability -int x4 = xfoo.bar.x; // Ok to read through xfoo, gets 4 -foo.bar.x++; // Bumps to 5 -int x5 = xfoo.bar.x; // Ok to read through xfoo, gets 5 -return x4*10+x5; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 45;", stop.toString()); - assertEquals(45L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testVar13() { - Parser parser = new Parser(""" -int i,i++; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); } - } - - - @Test - public void testVar14() { - Parser parser = new Parser(""" -struct B {}; -struct A { B b; }; -A x = new A { - return b; // read before init - b = new B; -}; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Cannot read uninitialized field 'b'",e.getMessage()); } - } - - @Test - public void testVar15() { - Parser parser = new Parser(""" -struct B {}; -struct A { B b; }; -return new A { - if (arg) b = new B; // Constructor ends with partial init of b -}.b; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("'A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); } - } - - // --------------------------------------------------------------- - @Test - public void testTrinary0() { - Parser parser = new Parser(""" -return arg ? 1 : 2; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,1,2);", stop.toString()); - assertEquals(2L, Evaluator.evaluate(stop, 0)); - assertEquals(1L, Evaluator.evaluate(stop, 1)); - } - - @Test - public void testTrinary1() { - Parser parser = new Parser(""" -return arg ? 0 : arg; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return 0;", stop.toString()); - assertEquals(0L, Evaluator.evaluate(stop, 0)); - } - - @Test - public void testTrinary2() { - Parser parser = new Parser(""" -struct Bar { int x; }; -var b = arg ? new Bar : null; -return b ? b.x++ + b.x++ : -1; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,1,-1);", stop.toString()); - assertEquals(-1L, Evaluator.evaluate(stop, 0)); - assertEquals( 1L, Evaluator.evaluate(stop, 1)); - } - - @Test - public void testTrinary3() { - Parser parser = new Parser(""" -struct Bar { int x; }; -var b = arg ? new Bar; -return b ? b.x++ + b.x++ : -1; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,1,-1);", stop.toString()); - assertEquals(-1L, Evaluator.evaluate(stop, 0)); - assertEquals( 1L, Evaluator.evaluate(stop, 1)); - } - - @Test - public void testTrinary4() { - // This test case will benefit from an unzipping transformation - Parser parser = new Parser(""" -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" -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,Phi(Region,.x,.x),0);", stop.toString()); - assertEquals( 0L, Evaluator.evaluate(stop, 0)); - assertEquals( 1L, Evaluator.evaluate(stop, 1)); - assertEquals( 2L, Evaluator.evaluate(stop, 2)); - } - - @Test - public void testTrinary5() { - Parser parser = new Parser(""" -flt f=arg?1:1.2; -return f; // missing widening -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Region,1.0,1.2);", stop.toString()); - assertEquals(1.2, Evaluator.evaluate(stop, 0)); - } - - // --------------------------------------------------------------- - @Test - public void testFor0() { - Parser parser = new Parser(""" -int sum=0; -for( int i=0; i<arg; i++ ) - sum += i; -return sum; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", stop.toString()); - assertEquals(3L, Evaluator.evaluate(stop, 3)); - assertEquals(45L, Evaluator.evaluate(stop, 10)); - } - @Test - public void testFor1() { - Parser parser = new Parser(""" -int sum=0, i=0; -for( ; i<arg; i++ ) - sum += i; -return sum; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", stop.toString()); - assertEquals(3L, Evaluator.evaluate(stop, 3)); - assertEquals(45L, Evaluator.evaluate(stop, 10)); - } - @Test - public void testFor2() { - Parser parser = new Parser(""" -int sum=0; -for( int i=0; ; i++ ) { - if( i>=arg ) break; - sum += i; -} -return sum; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", stop.toString()); - assertEquals(3L, Evaluator.evaluate(stop, 3)); - assertEquals(45L, Evaluator.evaluate(stop, 10)); - } - @Test - public void testFor3() { - Parser parser = new Parser(""" -int sum=0; -for( int i=0; i<arg; ) - sum += i++; -return sum; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", stop.toString()); - assertEquals(3L, Evaluator.evaluate(stop, 3)); - assertEquals(45L, Evaluator.evaluate(stop, 10)); - } - @Test - public void testFor4() { - Parser parser = new Parser(""" -int sum=0; -for( int i=0; i<arg; i++ ) - sum += i; -return i; -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); } - } - @Test - public void testFor5() { - Parser parser = new Parser(""" -for(;;arg++;) {} -"""); - try { parser.parse().iterate(); fail(); } - catch( Exception e ) { assertEquals("Syntax error, expected Unexpected code after expression: ;",e.getMessage()); } - } - - - - // --------------------------------------------------------------- - @Test - public void testForward0() { - Parser parser = new Parser(""" -struct A{ - B? f1; - B? f2; -}; -return new A; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return A;", stop.toString()); - assertEquals("Obj<A>{f1=null,f2=null}", Evaluator.evaluate(stop).toString()); - } - - @Test - public void testForward1() { - Parser parser = new Parser(""" -struct A{ - B?[]? nil_array_of_b; - B?[] not_array_of_b = new B?[0]; -}; -return new A.not_array_of_b; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return (const)[*B?];", stop.toString()); - assertEquals("Obj<[*B?]>{#=0,[]=[]}", Evaluator.evaluate(stop).toString()); - } - - // --------------------------------------------------------------- - @Test - public void testLinkedList2() { - Parser parser = new Parser(""" -struct LLI { LLI? next; int i; }; -LLI? !head = null; -while( arg-- ) - head = new LLI { next=head; i=arg; }; -int sum=0; -var ptr = head; // A read-only ptr, to be assigned from read-only next fields -for( ; ptr; ptr = ptr.next ) - sum += ptr.i; -return sum; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return Phi(Loop,0,(Phi_sum+.i));", stop.toString()); - assertEquals(45L, Evaluator.evaluate(stop, 10)); - } - - @Test - public void sieveOfEratosthenes() { - Parser parser = new Parser( -""" -var ary = new bool[arg], primes = new int[arg]; -var nprimes=0, p=0; -// Find primes while p^2 < arg -for( p=2; p*p < arg; p++ ) { - // skip marked non-primes - while( ary[p] ) p++; - // p is now a prime - primes[nprimes++] = p; - // Mark out the rest non-primes - for( int i = p + p; i < ary#; i += p ) - ary[i] = true; -} -// Now just collect the remaining primes, no more marking -for( ; p < arg; p++ ) - if( !ary[p] ) - primes[nprimes++] = p; -// Copy/shrink the result array -var !rez = new int[nprimes]; -for( int j=0; j<nprimes; j++ ) - rez[j] = primes[j]; -return rez; -"""); - StopNode stop = parser.parse().iterate(); - assertEquals("return [int];", stop.toString()); - Evaluator.Obj obj = (Evaluator.Obj)Evaluator.evaluate(stop, 20); - assertEquals("[int] {int #; int ![]; }",obj.struct().toString()); - long nprimes = (Long)obj.fields()[0]; - long[] primes = new long[]{2,3,5,7,11,13,17,19}; - for( int i=0; i<nprimes; i++ ) - assertEquals(primes[i],(long)(Long)obj.fields()[i+1]); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter17/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java deleted file mode 100644 index 0fa4283b..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import org.junit.Test; -import static org.junit.Assert.*; - -public class CompoundAssignmentTest { - private static final String[] OPS = {"&=", "|=", "^=", "<<=", ">>=", ">>>="}; - - private static long apply(String op, long lhs, long rhs) { - return switch (op) { - case "&=" -> lhs & rhs; - case "|=" -> lhs | rhs; - case "^=" -> lhs ^ rhs; - case "<<=" -> lhs << rhs; - case ">>=" -> lhs >> rhs; - case ">>>=" -> lhs >>> rhs; - default -> throw new AssertionError(op); - }; - } - - @Test public void testLocals() { - for (String op : OPS) - for (long arg : new long[]{-8, 0, 5, 64}) { - long expected = apply(op, arg, 3); - check("int x=arg; int r=(x " + op + " 3); return x*10+r;", - arg, expected*11); - } - } - - @Test public void testRhsOnce() { - for (String op : OPS) { - long expected = apply(op, 5, 1); - check("int x=arg; int count=0; int r=(x " + op + - " (count=count+1)); return count*1000+x*10+r;", 5, 1000+expected*11); - } - } - - @Test public void testNarrowingAndChaining() { - check("u8 x=255; x <<= 1; return x;", 0, 254); - check("u8 x=255; x ^= 256; return x;", 0, 255); - check("int x=1; int y=3; x |= y <<= 2; return x*100+y;", 0, 1312); - check("int x=-8; x >>= 65; return x;", 0, -4); - check("int x=-8; x >>>= 65; return x;", 0, Long.MAX_VALUE-3); - } - - @Test public void testExistingOperators() { - check("int x=8; x+=2; x-=1; x*=3; x/=9; return x;", 0, 3); - check("int x=arg; int y=x++; int z=x--; return x*100+y*10+z;", 5, 556); - check("return (arg << 2) + (arg >> 1) + (arg >>> 1);", 8, 40); - check("return arg <= 8;", 8, 1); - check("return arg >= 8;", 8, 1); - } - - @Test public void testRejectedAssignments() { - for (String op : OPS) { - rejected("val x=3; x " + op + " 1; return x;"); - rejected("int x=3; x " + op + " 1.5; return x;"); - } - } - - private static void rejected(String src) { - try { - check(src, 0, 0); - fail("Accepted invalid assignment: " + src); - } catch (RuntimeException e) { - assertEquals("Must report a language error", RuntimeException.class, e.getClass()); - assertNotNull(e.getMessage()); - assertFalse("Must report a language error: " + e, e.getMessage().contains("Not yet implemented")); - } - } - - private static void check(String src, long arg, long expected) { - var stop = new Parser(src).parse().iterate(); - assertEquals(src, Long.valueOf(expected), Evaluator.evaluate(stop, arg)); - } -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java deleted file mode 100644 index 2c98f405..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.fuzzer.Fuzzer; -import java.util.Random; -import org.junit.Ignore; -import org.junit.Test; -import static org.junit.Assert.assertTrue; - -/** - * Normal tests run chapter-local regression seeds. Open failures and exploratory - * fuzzing are opt-in (run directly in the IDE or temporarily remove Ignore). - * Seeds depend on this chapter's generator; do not copy them between chapters. - */ -public class FuzzerWrap { - - private static final long[] REGRESSION_SEEDS = { - }; - - private static final long[] OPEN_FAILING_SEEDS = { - }; - - @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds(REGRESSION_SEEDS); } - @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); } - - private static void fuzzPeepsSeeds(long... seeds) { - var fuzzer = new Fuzzer(); - for (long seed : seeds) - fuzzer.fuzzPeepsRegression(seed); - } - - @Ignore - @Test - public void fuzzPeepsLarge() { - var fuzzer = new Fuzzer(); - for (int i=0; i<1000000; i++) - fuzzer.fuzzPeeps(i); - assertTrue(fuzzer.noExceptions()); - } - - - @Test @Ignore - public void fuzzPeepsRandom() { - Random R = new Random(System.currentTimeMillis()); - var fuzzer = new Fuzzer(); - for (int i=0; i<100; i++) - fuzzer.fuzzPeeps(R.nextLong()); - assertTrue(fuzzer.noExceptions()); - } - - @Test - @Ignore - public void fuzzPeepTiming() { - var fuzzer = new Fuzzer(); - int max_nid=0; - for (int i=0; i<1000000; i++) - max_nid = fuzzer.fuzzPeepTiming(i, max_nid); - } -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter17/src/test/java/com/seaofnodes/simple/SchedulerTest.java deleted file mode 100644 index 6e7e2c80..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/SchedulerTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.StopNode; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class SchedulerTest { - - private static void assertObj(Object obj, String name, Object... fields) { - assertEquals(Evaluator.Obj.class, obj.getClass()); - var o = (Evaluator.Obj)obj; - assertEquals(name, o.struct()._name); - Assert.assertArrayEquals(fields, o.fields()); - } - - @Test - @Ignore - public void testJig() { - Parser parser = new Parser(""" -struct S { - int f; -} -S !v=new S; -v.f = 2; -int i=v.f; -if (arg) v.f=1; -return i; -"""); - StopNode stop = parser.parse();//.iterate(); - var eval = new Evaluator(stop); - assertEquals(2L, eval.evaluate(1, 10)); - } - - @Test - public void testStoreInIf() { - Parser parser = new Parser(""" -struct S { - int f; -}; -S !v0=new S; -if(arg) v0.f=1; -return v0; -"""); - StopNode stop = parser.parse().iterate(); - var eval = new Evaluator(stop); - assertObj(eval.evaluate(0, 10), "S", 0L); - assertObj(eval.evaluate(1, 10), "S", 1L); - } - - @Test - public void testStoreInIf2() { - Parser parser = new Parser(""" -struct S { - int f; -}; -S !v=new S; -v.f = 2; -int i=new S.f; -i=v.f; -if (arg) v.f=1; -return i; -"""); - StopNode stop = parser.parse().iterate(); - var eval = new Evaluator(stop); - assertEquals(2L, eval.evaluate(0, 10)); - assertEquals(2L, eval.evaluate(1, 10)); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/TypeTest.java b/chapter17/src/test/java/com/seaofnodes/simple/TypeTest.java deleted file mode 100644 index 1cb97cf6..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/TypeTest.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.type.*; -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertSame; - -public class TypeTest { - - // Test basic properties and GLB - @Test - public void testTypeAdHoc() { - TypeStruct s1 = TypeStruct.make("s1", new Field[]{ - Field.make("a", TypeInteger.BOT,-1, false), - Field.make("b", TypeInteger.BOT,-2, false) }); - TypeStruct s2 = TypeStruct.make("s2", new Field[]{ - Field.make("a", TypeInteger.BOT,-3, false), - Field.make("b", TypeInteger.BOT,-4, false) }); - Assert.assertEquals(s1, s1.glb()); - Assert.assertNotEquals(s1, s1.dual()); - Assert.assertEquals(s1.makeRO(), s1.dual().glb()); - - TypeMem m1 = TypeMem.make(1,TypeInteger.ZERO); - TypeMem m2 = TypeMem.make(2,TypeInteger.U16); - TypeMem m3 = TypeMem.make(3,TypeFloat.BOT); - TypeMem m4 = TypeMem.make(4,TypeInteger.BOT); - - Assert.assertNotEquals(m1, m2); - Assert.assertNotEquals(m2, m3); - Assert.assertNotEquals(m3, m4); - - Assert.assertEquals(TypeStruct.BOT, s1.meet(s2)); - Assert.assertEquals(TypeMem.make(-1,TypeInteger.U16), m1.meet(m2)); - Assert.assertEquals(TypeMem .BOT, m2.meet(m3)); - Assert.assertEquals(TypeMem .BOT, m3.meet(m4)); - - Assert.assertEquals(TypeMem.make(1,TypeInteger.BOT), m1.glb()); - Assert.assertEquals(TypeMem.make(1,TypeInteger.ZERO), m1.dual()); - Assert.assertEquals(m4.dual(), m4.glb().dual()); - - TypeMemPtr ptr1 = TypeMemPtr.make(s1); - Assert.assertEquals(s1, ptr1._obj); - TypeMemPtr ptr2 = TypeMemPtr.make(s2); - Assert.assertEquals(s2, ptr2._obj); - - TypeMemPtr ptr1nil = TypeMemPtr.make(s1, true); - Assert.assertEquals(s1, ptr1nil._obj); - Assert.assertTrue(ptr1nil._nil); - Assert.assertFalse(ptr1nil._obj==null); - TypeMemPtr ptr2nil = TypeMemPtr.make(s2, true); - Assert.assertEquals(s2, ptr2nil._obj); - - Assert.assertNotEquals(ptr1, ptr2); - Assert.assertNotEquals(ptr1, ptr1.glb()); - Assert.assertEquals(ptr1nil, ptr1.glb()); - - Assert.assertEquals(ptr1, ptr1.dual().dual()); - Assert.assertEquals(ptr1.glb().makeRO(), ptr1.dual().glb()); - Assert.assertEquals(TypeMemPtr.make(TypeStruct.BOT,true), ptr1.meet(ptr2nil)); - Assert.assertEquals(ptr1.glb(), ptr1.meet(TypeMemPtr.NULLPTR)); - - TypeMemPtr TOP = TypeMemPtr.TOP; - TypeMemPtr BOT = TypeMemPtr.make(TypeStruct.BOT,true ); - TypeMemPtr PTR = TypeMemPtr.make(TypeStruct.BOT,false); - TypeMemPtr NULL = TypeMemPtr.NULLPTR; - Type PTR_meet_NULL = NULL.meet(PTR); - Assert.assertEquals(BOT, PTR_meet_NULL); - Type ptr1_meet_ptr2 = ptr1.meet(ptr2); - Assert.assertEquals(PTR, ptr1_meet_ptr2); - Type NULL_join_ptr1 = NULL.join(ptr1); - Assert.assertEquals(TOP, NULL_join_ptr1); - Type NULL_join_PTR = PTR.join(NULL); - Assert.assertEquals(TOP, NULL_join_PTR); - Type ptr1_dual = ptr1.dual(); - Type nullableptr1_dual = ptr1nil.dual(); - } - - // Test theoretical properties. - // This is a symmetric complete bounded (ranked) lattice. - // Also, the meet is commutative and associative. - // The lattice has a dual (symmetric), and join is ~(~x meet ~y). - // See https://en.wikipedia.org/wiki/Lattice_(order). - @Test - public void testLatticeTheory() { - Type[] ts = Type.gather(); - - // Confirm commutative & complete - for( Type t0 : ts ) - for( Type t1 : ts ) { - check_commute (t0,t1); - check_symmetric(t0,t1); - } - - // Confirm associative - for( Type t0 : ts ) - for( Type t1 : ts ) - for( Type t2 : ts ) - assoc(t0,t1,t2); - - // Confirm symmetry. If A isa B, then A.join(C) isa B.join(C) - for( Type t0 : ts ) - for( Type t1 : ts ) - if( t0.isa(t1) ) - for( Type t2 : ts ) { - Type t02 = t0.join(t2); - Type t12 = t1.join(t2); - Type mt = t02.meet(t12); - assertSame(mt,t12); - } - } - - // By design in meet, args are already flipped to order _type, which forces - // symmetry for things with badly ordered _type fields. The question is - // still interesting for other orders. - private static void check_commute( Type t0, Type t1 ) { - if( t0==t1 ) return; - if( t0.is_simple() && !t1.is_simple() ) return; // By design, flipped the only allowed order - Type mta = t0.meet(t1); - Type mtb = t1.meet(t0); // Reverse args and try again - assertSame(mta,mtb); - } - - // A & B = MT - // Expect: ~A & ~MT == ~A - // Expect: ~B & ~MT == ~B - private static void check_symmetric( Type t0, Type t1 ) { - if( t1==t0 ) return; - Type mt = t0.meet(t1); - Type dm = mt.dual(); - Type d0 = t0.dual(); - Type d1 = t1.dual(); - Type ta = dm.meet(d1); - Type tb = dm.meet(d0); - assertSame(ta,d1); - assertSame(tb,d0); - } - - private static void assoc( Type t0, Type t1, Type t2 ) { - Type t01 = t0 .meet(t1 ); - Type t12 = t1 .meet(t2 ); - Type t01_2 = t01.meet(t2 ); - Type t0_12 = t0 .meet(t12); - assertSame(t01_2,t0_12); - } - - // Test cyclic types and meets - @Test - public void testCyclic0() { - Type d0 = TypeStruct.S1.dual(); - Type d1 = d0.dual(); - assertSame(TypeStruct.S1,d1); - } - - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter17/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java deleted file mode 100644 index 30af9895..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java +++ /dev/null @@ -1,374 +0,0 @@ -package com.seaofnodes.simple.evaluator; - -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.*; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.util.*; - -public class Evaluator { - - private static final Object MEMORY = new Object(); - - public record Obj(TypeStruct struct, Object[] fields) { - private void init(IdentityHashMap<Obj, Integer> objs) { - var oid = objs.get(this); - if (oid != null) { - if (oid != 0) objs.put(this, 0); - return; - } - objs.put(this, -1); - for (var obj : fields) { - if (obj instanceof Obj o) o.init(objs); - } - } - private static int p(StringBuilder sb, IdentityHashMap<Obj, Integer> objs, int id, String indentation, String step, String sep, Object obj) { - if (obj instanceof Obj o) return o.p(sb, objs, id, indentation, step, sep); - sb.append(obj); - return id; - } - private int p(StringBuilder sb, IdentityHashMap<Obj, Integer> objs, int id, String indentation, String step, String sep) { - var cid = objs.get(this); - assert cid != null; - if (cid > 0) { - sb.append("obj@").append(cid); - return id; - } - if (struct._name.equals("[u8]")) { - sb.append('"'); - for (int i=struct._fields.length-1; i<fields.length; i++) { - var v = fields[i]; - if (v == null) v = 0; - assert v instanceof Number; - var n = ((Number)v).byteValue() & 0xFF; - if (n >= 0x20 && n < 0x80) { - if (n == 0x22 || n == 0x5C) sb.append("\\"); - sb.append((char)n); - } else { - sb.append("\\x").append("0123456789abcdef".charAt(n >> 4)).append("0123456789abcdef".charAt(n & 0xF)); - } - } - sb.append('"'); - return id; - } - sb.append("Obj<").append(struct._name).append(">"); - if (cid == 0) { - cid = id++; - objs.put(this, cid); - sb.append("@").append(cid); - } - sb.append("{"); - if (struct._fields.length == 0) { - assert !struct.isAry(); - sb.append("}"); - return id; - } - String nextIndent = indentation + step; - int e = struct._fields.length - 1; - for(int i=0; i<e; i++) { - sb.append(nextIndent).append(struct._fields[i]._fname).append("="); - id = p(sb, objs, id, nextIndent, step, sep, fields[i]); - sb.append(sep); - } - sb.append(nextIndent).append(struct._fields[e]._fname).append("="); - if (struct.isAry()) { - sb.append("["); - if (fields.length > e) { - String innerIndent = nextIndent + step; - sb.append(innerIndent); - id = p(sb, objs, id, nextIndent, step, sep, fields[e]); - for (int i = e+1; i < fields.length; i++) { - sb.append(sep).append(innerIndent); - id = p(sb, objs, id, nextIndent, step, sep, fields[i]); - } - sb.append(nextIndent); - } - sb.append("]"); - } else { - id = p(sb, objs, id, nextIndent, step, sep, fields[e]); - } - sb.append(indentation).append("}"); - return id; - } - private StringBuilder p(StringBuilder sb, String indentation, String step, String sep) { - var objs = new IdentityHashMap<Obj, Integer>(); - init(objs); - p(sb, objs, 1, indentation, step, sep); - return sb; - } - @Override - public String toString() { - if (struct._name.equals("[u8]")) { - var sb = new StringBuilder(); - for (int i=struct._fields.length-1; i<fields.length; i++) { - var v = fields[i]; - if (v == null) v = 0; - assert v instanceof Number; - var n = ((Number)v).byteValue() & 0xFF; - sb.append((char)n); - } - return sb.toString(); - } - return p(new StringBuilder(), "", "", ",").toString(); - } - public String pretty() { - return p(new StringBuilder(), "\n", " ", "").toString(); - } - } - - private static int getFieldIndex(TypeStruct struct, MemOpNode memop, long off) { - int idx = struct.find(memop._name); - if( idx >= 0 ) return idx; - throw new AssertionError("Field "+memop._name+" not found in struct " + struct._name); - } - - public enum Status { - TIMEOUT, - FALLTHROUGH - } - - /** - * Find the start node from some node in the graph or null if there is no start node - */ - private static StartNode findStart(BitSet visit, Node node) { - if (node == null) return null; - if( visit.get(node._nid) ) return null; - visit.set(node._nid); - StartNode start = node instanceof StartNode ? (StartNode) node : null; - for( Node def : node._inputs ) { - var res = findStart(visit,def); - if( res != null ) start = res; - } - for( Node use : node._outputs ) { - var res = findStart(visit,use); - if( res != null ) start = res; - } - return start; - } - - private final Object[] values; - private final ArrayList<Object> phiCache = new ArrayList<>(); - private final StartNode start; - private final Scheduler.Block startBlock; - - public Evaluator(Node graph) { - var visited = new BitSet(); - this.start = findStart(visited, graph); - if (this.start == null) { - this.values = null; - this.startBlock = null; - return; - } - this.startBlock = Scheduler.schedule(this.start); - this.values = new Object[visited.length()]; - } - - private long div(DivNode div) { - long in2 = vall(div.in(2)); - return in2 == 0 ? 0 : vall(div.in(1)) / in2; - } - private double div(DivFNode div) { - double in2 = vald(div.in(2)); - return in2 == 0 ? 0 : vald(div.in(1)) / in2; - } - - private static long offToIdx(long off, TypeStruct t) { - off -= t.aryBase(); - int scale = t.aryScale(); - long mask = (1L << scale)-1; - assert (off & mask) == 0; - return off>>scale; - } - - private Object alloc(NewNode alloc) { - TypeStruct type = alloc._ptr._obj; - Object[] body=null; - int num; - if( type.isAry() ) { - long sz = (Long)val(alloc.size()); - long n = offToIdx(sz, type); - if( n < 0 ) - throw new NegativeArraySizeException(""+n); - body = new Object[(int)n+1]; // Array body - var elem = type._fields[1]._type; - if (elem instanceof TypeInteger) { - for (int i=0; i<n; i++) body[i+1] = 0L; - } else if (elem instanceof TypeFloat) { - for (int i = 0; i < n; i++) body[i+1] = 0D; - } else { - assert elem instanceof TypeMemPtr; - } - // Length value - body[0] = vall(alloc.in(3)); - } else { - body = new Object[num = type._fields.length]; - for (int i=0; i<num; i++) - body[i] = val(alloc.in(3+i)); - } - return new Object[]{new Obj(type,body),MEMORY}; - } - - private Object load(LoadNode load) { - var from = (Obj)val(load.ptr()); - var off = vall(load.off()); - var idx = getFieldIndex(from.struct, load, (Long)off); - if( idx==from.struct._fields.length-1 && from.struct.isAry() ) { - long len = from.fields.length - from.struct._fields.length + 1; - long i = offToIdx(off, from.struct); - if( i < 0 || i >= len ) - throw new ArrayIndexOutOfBoundsException("Array index out of bounds " + i + " < " + len); - return from.fields[(int)i+from.struct._fields.length-1]; - - } else - return from.fields[idx]; - } - - private Object store(StoreNode store) { - var to = (Obj)val(store.ptr()); - var off = vall(store.off()); - var val = val(store.val()); - var idx = getFieldIndex(to.struct, store, (Long)off); - if( idx==to.struct._fields.length-1 && to.struct.isAry() ) { - long len = to.fields.length - to.struct._fields.length + 1; - long i = offToIdx(off, to.struct); - if( i < 0 || i >= len ) - throw new RuntimeException("Array index out of bounds " + off + " <= " + len); - to.fields[(int)i+to.struct._fields.length-1] = val; - } else - to.fields[idx] = val; - return null; - } - - private static boolean isTrue(Object obj) { - if (obj == null) return false; - if (obj instanceof Number n) return n.longValue() != 0; - return true; - } - - private Object val(Node node) { - return values[node._nid]; - } - - private long vall(Node node) { - var v = val(node); - if (v instanceof Number n) return n.longValue(); - throw new AssertionError("Not a long " + v); - } - private double vald(Node node) { - var v = val(node); - if (v instanceof Number n) return n.doubleValue(); - throw new AssertionError("Not a double " + v); - } - - private Object cons(ConstantNode cons) { - var type = cons.compute(); - if (type instanceof TypeInteger i) return i.value(); - if (type instanceof TypeFloat i) return i.value(); - assert type instanceof TypeMemPtr; - return null; - } - - private Object exec(Node node) { - return switch (node) { - case ConstantNode cons -> cons(cons); - case AddNode add -> vall(add.in(1)) + vall(add.in(2)); - case AddFNode add -> vald(add.in(1)) + vald(add.in(2)); - case BoolNode.EQF eq -> vald(eq.in(1)) == vald(eq.in(2)) ? 1L : 0L; - case BoolNode.LEF le -> vald(le.in(1)) <= vald(le.in(2)) ? 1L : 0L; - case BoolNode.LTF lt -> vald(lt.in(1)) < vald(lt.in(2)) ? 1L : 0L; - case BoolNode.EQ eq -> Objects.equals(val(eq.in(1)), val(eq.in(2))) ? 1L : 0L; - case BoolNode.LE le -> vall(le.in(1)) <= vall(le.in(2)) ? 1L : 0L; - case BoolNode.LT lt -> vall(lt.in(1)) < vall(lt.in(2)) ? 1L : 0L; - case DivNode div -> div(div); - case DivFNode div -> div(div); - case MinusNode minus -> -vall(minus.in(1)); - case MinusFNode minus -> -vald(minus.in(1)); - case MulNode mul -> vall(mul.in(1)) * vall(mul.in(2)); - case MulFNode mul -> vald(mul.in(1)) * vald(mul.in(2)); - case NotNode not -> isTrue(val(not.in(1))) ? 0L : 1L; - case SubNode sub -> vall(sub.in(1)) - vall(sub.in(2)); - case SubFNode sub -> vald(sub.in(1)) - vald(sub.in(2)); - case ShlNode shl -> vall(shl.in(1)) << vall(shl.in(2)); - case ShrNode shr -> vall(shr.in(1)) >>> vall(shr.in(2)); - case SarNode sar -> vall(sar.in(1)) >> vall(sar.in(2)); - case AndNode and -> vall(and.in(1)) & vall(and.in(2)); - case OrNode or -> vall(or .in(1)) | vall(or .in(2)); - case XorNode xor -> vall(xor.in(1)) ^ vall(xor.in(2)); - case CastNode cast -> val(cast.in(1)); - case ToFloatNode cast -> (double)vall(cast.in(1)); - case LoadNode load -> load(load); - case StoreNode store -> store(store); - case MemMergeNode merge -> MEMORY; - 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 ReadOnlyNode ro -> val(ro.in(1)); - default -> throw new AssertionError("Unexpected node " + node); - }; - } - - /** - * Run the graph until either a return is found or the number of loop iterations are done. - */ - public Object evaluate(long parameter, int loops) { - if (start == null) return Status.TIMEOUT; - var s = new Object[start.compute()._types.length]; - s[1] = MEMORY; - s[2] = parameter; - values[start._nid] = s; - int i=0; - Scheduler.Block block = this.startBlock; - for(;;) { - for(; i<block.nodes().length;i++) values[block.nodes()[i]._nid] = exec(block.nodes()[i]); - i=0; - switch (block.exit()) { - case null: - return Status.FALLTHROUGH; - case ReturnNode ret: - return val(ret.expr()); - case IfNode ifn: - block = block.next()[isTrue(val(ifn.in(1))) ? 0 : 1]; - if (block == null) return Status.FALLTHROUGH; - break; - case RegionNode region: - if (loops-- <= 0) return Status.TIMEOUT; - int exit = block.exitId(); - assert exit > 0 && region.nIns() > exit; - block = block.next()[0]; - assert block != null; - for (; i < block.nodes().length; i++) { - if (!(block.nodes()[i] instanceof PhiNode)) break; - phiCache.add(val(block.nodes()[i].in(exit))); - } - for (i=0; i<phiCache.size(); i++) { - values[block.nodes()[i]._nid] = phiCache.get(i); - } - phiCache.clear(); - break; - default: - throw Utils.TODO("Unexpected control node " + block.exit()); - } - } - } - - public static Object evaluate(StopNode graph) { - return evaluate(graph, 0); - } - - public static Object evaluate(StopNode graph, long parameter) { - return evaluate(graph, parameter, 1000); - } - - public static Object evaluate(StopNode graph, long parameter, int loops) { - var res = evaluateWithResult(graph, parameter, loops); - if (res == Status.TIMEOUT) throw new RuntimeException("Timeout"); - return res; - } - - public static Object evaluateWithResult(StopNode graph, long parameter, int loops) { - return new Evaluator(graph).evaluate(parameter, loops); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java b/chapter17/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java deleted file mode 100644 index afcc700f..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java +++ /dev/null @@ -1,596 +0,0 @@ -package com.seaofnodes.simple.evaluator; - -import com.seaofnodes.simple.Utils; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.Type; - -import java.util.*; - -/** - * A late scheduler for test cases. - * <br> - * <br> - * This scheduler is used for tests and is not the final scheduler. - * <br> - * <br> - * The scheduler schedules not as late as possible. Since it is just for test cases this is fine. - * A better scheduler will try to place nodes outside of loops. Due to the schedule late strategy - * this scheduler might place initialization code in loops. - * <br> - * We schedule late here since memory edges have some implicit information. There can only be one - * memory edge (for a field) at a time. However, before <code>if</code> statements this might split. - * When scheduling early a store might be moved out of the loop but then there are two edges alive. - * The one through the store node and the adjacent one. This is an invalid schedule and instead of - * handling this case a late scheduler does not have this problem as memory edges are combined - * through phi nodes. - * <br> - * <br> - * A bit about this scheduler. It first discovers all alive nodes and allocates side data for them. - * Then the control flow graph is build. Then, all phis are placed and finally all the remaining nodes - * in an order where a node is placed when all alive uses are placed. Finally, the output is produced. - */ -public class Scheduler { - - private final HashMap<Node,ArrayList<NodeData>> readers = new HashMap<>(); - - /** - * A basic block with a schedule of the containing nodes. - * - * @param nodes The nodes in the final schedule of this block - * @param exit The exit node of this block. - * @param exitId In case the next block is a region this gives the entry id into the region. - * @param next The next blocks. - */ - public record Block(Node[] nodes, Node exit, int exitId, Block[] next) {} - - /** - * Definition of a block in the building process. - */ - private static class BasicBlock { - - /** - * Dominator of this block - */ - final BasicBlock dom; - /** - * Id of this block where <code>a.depth < b.depth</code> means that a is before b or - * both are in different branches. - */ - final int depth; - /** - * Previous blocks - */ - final BasicBlock[] prev; - /** - * Entry to this block - */ - final Node entry; - /** - * Schedules node in reverse order. - */ - final ArrayList<Node> reverseSchedule = new ArrayList<>(); - - /** - * Initialize the block. - * @param entry The node that started this block - * @param depth The depth of this node. All previous nodes need to have a lower value. - * @param prev The previous blocks. - */ - BasicBlock(Node entry, int depth, BasicBlock... prev) { - BasicBlock dom = null; - int max = -1; - for(var bb: prev) { - max = Math.max(max, bb.depth); - dom = dom==null?bb:dom(dom, bb); - } - assert depth > max; - this.dom = dom; - this.depth = depth; - this.entry = entry; - this.prev = prev; - } - - } - - /** - * Ancillary data for nodes used during the scheduling process. - */ - private static class NodeData { - - /** - * The node this ancillary data is used for. - */ - final Node node; - - /** - * Number of alive users not yet scheduled. - */ - int users = 1; - - /** - * Block into which the node should be placed so far. - * This will be updated with every placed use. - */ - BasicBlock block; - - /** - * Initialize ancillary data for a node - * @param node The node this ancillary data is associated with. - */ - NodeData(Node node) { - this.node = node; - } - - } - - /** - * Ancillary data for nodes. - */ - private final IdentityHashMap<Node, NodeData> data = new IdentityHashMap<>(); - /** - * List of nodes which can be placed since all users are placed. - */ - private final Stack<NodeData> scheduleQueue = new Stack<>(); - - /** - * Get the ancillary data for an alive node. - * @param node The node for which the ancillary data is requested. - * @return The ancillary data for the node. - */ - NodeData d(Node node) { - return od(node).orElseThrow(AssertionError::new); - } - - /** - * Get the ancillary data for a potentially dead node. - * @param node The node for which the ancillary data is requested. - * @return The ancillary data for the node an alive node or - * <code>Optional.empty()</code> for <code>null</code> or dead nodes. - */ - Optional<NodeData> od(Node node) { - return Optional.ofNullable(data.get(node)); - } - - /** - * Checks a node for validity - * @param data The node to check. - * @return true if all placed inputs are before this node. - */ - private boolean isValid(NodeData data) { - return data.node._inputs.stream() - .map(i->i==null?null:d(i)) - .map(d->d==null||d.users>0?null:d.block) - .allMatch(d->d==null||dom(d, data.block)==d); - } - - /** - * Checks that node is not XCtrl. - * @param node The node to check. - * @return true if the node is not XCtrl. - */ - private static boolean isNotXCtrl(Node node) { - return !(node instanceof ConstantNode c && c.compute() == Type.XCONTROL) && !(node instanceof XCtrlNode); - } - - /** - * Return the dominator of <code>a</code> and <code>b</code> - * @param a Block a - * @param b Block b - * @return The dominator of <code>a</code> and <code>b</code> - */ - private static BasicBlock dom(BasicBlock a, BasicBlock b) { - while (a!=b) { - if (a.depth >= b.depth) a=a.dom; - if (b.depth > a.depth) b=b.dom; - } - return a; - } - - /** - * Return if a is before b. - * @param a Block a - * @param b Block b - * @return true is a is before b. - */ - private static boolean isBefore(BasicBlock a, BasicBlock b) { - while (b.depth > a.depth) { - while (b.dom.depth > a.depth) b = b.dom; - for (int i=0; i<b.prev.length-1; i++) { - if (isBefore(a, b.prev[i])) return true; - } - b = b.prev[b.prev.length-1]; - } - return a == b; - } - - /** - * Is node placed during the control flow graph build. - * @param data Node to check. - * @return true if node is placed during the control flow graph build. - */ - private static boolean isPinnedNode(NodeData data) { - return data.node instanceof CFGNode || data.node instanceof PhiNode; - } - - /** - * Refine placement of a node. - * @param data The node to refine. - * @param block The block before the node should be scheduled. - */ - private void refinePlacement(NodeData data, BasicBlock block) { - assert !isPinnedNode(data); - data.block = data.block == null ? block : dom(data.block, block); - assert isValid(data); - } - - /** - * Refine placement if before is happening before data - * @param data The node for which the placement should be refined. - * @param before The node before which the data node should be placed. - */ - private void optionalRefinePlacement(NodeData data, Node before) { - od(before).ifPresent(d->{ - // before might be in a different branch. So check this case with isBefore - // and only refine the placement if it is not in a different branch. - if (isBefore(d.block, data.block)) refinePlacement(data, d.block); - }); - } - - /** - * Schedule all nodes not yet scheduled. - */ - private void doSchedule() { - while (!scheduleQueue.isEmpty()) { - var data = scheduleQueue.pop(); - assert !isPinnedNode(data); - - assert data.block != null; - assert data.users == 0; - - if (data.node instanceof LoadNode l) { - assert isValid(data); - // Handle anti-deps of load nodes. - // At this point all anti-dep nodes are scheduled, - // but they did not refine the placement - // so do that now. - var mem = l.in(1); - for(var out : l.antiDeps()) { - if (out instanceof PhiNode p) { - var r = p.in(0); - for (int i = 1; i < p.nIns(); i++) { - if (p.in(i) == mem) optionalRefinePlacement(data, r.in(i)); - } - } else if (!(out instanceof LoadNode)) { - optionalRefinePlacement(data, out); - } - } - } - - assert isValid(data); - data.block.reverseSchedule.add(data.node); - - for(var in:data.node._inputs) { - if (in!=null) update(d(in), data.block); - } - var loads = readers.get(data.node); - if (loads != null) - for (var load : loads) decUsers(load); - } - - // Now all nodes should be placed and have a block assigned - assert this.data.values().stream().noneMatch(d->d.block==null); - } - - /** - * Decrement the not-yet-placed users of a block. - * @param data The node for which the users should be decremented. - */ - private void decUsers(NodeData data) { - assert data.users > 0; - if (--data.users == 0) { - assert data.block != null; - assert isValid(data); - - // When all users are gone this node can be scheduled. - scheduleQueue.push(data); - } - } - - /** - * Update the placement of a node - * @param data The node to update - * @param block The block before which the node should happen. - */ - private void update(NodeData data, BasicBlock block) { - assert block != null; - if (data.users == 0) { - assert isPinnedNode(data); - return; - } - refinePlacement(data, block); - decUsers(data); - } - - /** - * Checks if a CFG node is ready to be placed. This is used for regions joining branches. - * @param node The CFG node to check - * @return true if all parents of a CFG node are placed. - */ - private boolean isCFGNodeReady(Node node) { - if (node instanceof LoopNode l) { - assert d(l.in(1)).block != null; - } else if (node instanceof RegionNode r) { - for(int i=1; i<r.nIns(); i++) { - if (od(r.in(i)).map(d->d.block==null).orElse(false)) return false; - } - } else if (node instanceof XCtrlNode) { - return false; - } else { - assert d(node.in(0)).block != null; - } - return true; - } - - /** - * Schedule all phi nodes. - * @param phiQueue List of all the phi nodes to schedule. - */ - private void schedulePhis(Stack<NodeData> phiQueue) { - while(!phiQueue.empty()) { - var data = phiQueue.pop(); - var phi = (PhiNode) data.node; - var r = phi.in(0); - data.block = d(r).block; - for(int i=1; i<phi.nIns(); i++) { - var in = phi.in(i); - od(r.in(i)).ifPresent(d->update(d(in), d.block)); - } - } - } - - /** - * Build the control flow graph. - * @param start The start node. - */ - private void doBuildCTF(StartNode start) { - var queue = new Stack<NodeData>(); - var phiQueue = new Stack<NodeData>(); - queue.push(d(start)); - int depth = 0; - while (!queue.isEmpty()) { - var data = queue.pop(); - var node = data.node; - assert node instanceof CFGNode; - BasicBlock block; - switch (node) { - case StartNode s: - block = new BasicBlock(s, depth++); - break; - case LoopNode l: - block = new BasicBlock(l, depth++, d(l.in(1)).block); - break; - case RegionNode r: - var prev = new ArrayList<BasicBlock>(); - for(int i=1; i<r.nIns(); i++) { - od(r.in(i)).ifPresent(d->prev.add(d.block)); - } - block = new BasicBlock(r, depth++, prev.toArray(BasicBlock[]::new)); - break; - case IfNode i: - block = d(i.in(0)).block; - break; - case ReturnNode r: - block = d(r.in(0)).block; - break; - default: - block = new BasicBlock(node, depth++, d(node.in(0)).block); - } - data.block = block; - if (node instanceof RegionNode r) { - // Regions might have phis which need to be scheduled. - // Put them on a list for later scheduling. - for (var out:r._outputs) { - if (out instanceof PhiNode p) { - var d = this.data.get(p); - if (d!=null) { - d.users=0; - phiQueue.push(d); - } - } - } - } - if (!(node instanceof ReturnNode)) - for (Node n : node._outputs) - if(n instanceof CFGNode && isCFGNodeReady(n) && d(n).block == null) - queue.push(d(n)); - var b = block; - for(var in:data.node._inputs) od(in).ifPresent(d->update(d, b)); - } - - // Schedule all phi nodes. - schedulePhis(phiQueue); - } - - /** - * Mark a node alive and create ancillary data for it. - * @param queue List into which this node should be placed if it needs to be visited. - * @param node Node which should be marked alive. - * @param cfg If this node is a CFG node. - */ - private void markAlive(Stack<NodeData> queue, Node node, boolean cfg) { - var nd = od(node).orElse(null); - if (nd != null) { - // Node was already visited, just increase the users. - if (nd.users>0) nd.users++; - return; - } - assert node instanceof CFGNode == cfg; - assert isNotXCtrl(node); - nd = new NodeData(node); - if (cfg) nd.users=0; - data.put(node, nd); - queue.push(nd); - } - - /** - * Mark all alive nodes. - * First all CFG nodes are marked, then all other nodes. - * @param node THe start node. - */ - private void doMarkAlive(Node node) { - var cfgQueue = new Stack<NodeData>(); - var dataQueue = new Stack<NodeData>(); - markAlive(cfgQueue, node, true); - // Mark all CFG nodes. - while (!cfgQueue.isEmpty()) { - var data = cfgQueue.pop(); - node = data.node; - assert node instanceof CFGNode; - if (!(node instanceof ReturnNode)) { - for (var out : node._outputs) if (out!=null && out instanceof CFGNode && isNotXCtrl(out)) markAlive(cfgQueue, out, true); - } - for (var in : node._inputs) if(in!=null && !(in instanceof CFGNode) && isNotXCtrl(in)) markAlive(dataQueue, in, false); - } - // Mark all other nodes. - while (!dataQueue.isEmpty()) { - var data = dataQueue.pop(); - node = data.node; - assert !(node instanceof CFGNode); - if (node instanceof PhiNode phi) { - var r = phi.in(0); - for (int i=1; i<phi.nIns(); i++) { - if (od(r.in(i)).isPresent()) markAlive(dataQueue, phi.in(i), false); - } - } else { - for (var in : node._inputs) if (in != null && !(in instanceof CFGNode)) markAlive(dataQueue, in, false); - } - } - // Writes, including New behind an aggregate, schedule before their - // anti-dependent reads in this reverse scheduling pass. - for (var load : data.values()) - if (load.node instanceof LoadNode l) - for (var use : l.antiDeps()) - if ((use instanceof StoreNode || use instanceof NewNode) && data.containsKey(use)) { - load.users++; - readers.computeIfAbsent(use,k->new ArrayList<>()).add(load); - } - } - - /** - * Helper function to append the nodes of a block to an array of nodes - * @param arr The array the nodes of a block should be appended to. - * @param node A potentially separator node - * @param block The block from which the nodes should be appended. - * @return The combined new array. - */ - private static Node[] appendNodes(Node[] arr, Node node, BasicBlock block) { - var idx = arr.length; - arr = Arrays.copyOf(arr, arr.length+block.reverseSchedule.size()+(node==null?0:1)); - if (node != null) arr[idx++] = node; - for(int i=block.reverseSchedule.size()-1;i>=0;i--) { - arr[idx++] = block.reverseSchedule.get(i); - } - return arr; - } - - /** - * Find the CFG output of a node - * @param node The node - * @return THe CFG output of the node. - */ - private static Node findSingleCFGOut(Node node) { - if (node instanceof StartNode) { - for(var n:node._outputs) if (n instanceof CProjNode p && p._idx==0) return p; - return null; - } - assert node._outputs.stream().filter(x -> x instanceof CFGNode).limit(2).count()<=1; - for(var n:node._outputs) if(n instanceof CFGNode) return n; - return null; - } - - /** - * Build the final data structure after all the scheduling happened. - * @param start The start node - * @return The final schedule. - */ - private Block build(Node start) { - var blocks = new IdentityHashMap<Node, Block>(); - var queue = new Stack<NodeData>(); - queue.push(d(start)); - - // Visit all CFG nodes and create blocks for them. - // This can combine blocks. - while(!queue.isEmpty()) { - var data = queue.pop(); - var first = data.node; - Node prev; - var last = first; - Node[] arr = new Node[0]; - if (first instanceof RegionNode) { - var nodes = new ArrayList<Node>(); - for(var out: first._outputs) { - if (out instanceof PhiNode) { - if(od(out).isPresent()) nodes.add(out); - } - } - arr = nodes.toArray(arr); - } - arr = appendNodes(arr, null, data.block); - assert !(first instanceof IfNode); - for(;;) { - prev = last; - last = findSingleCFGOut(last); - if (last == null) break; - if (last instanceof RegionNode) { - if (blocks.get(last) == null && last != first) - queue.push(d(last)); - break; - } - if (last instanceof ReturnNode) break; - if (last instanceof IfNode if_) { - for(var out:if_._outputs) if (out instanceof CFGNode && blocks.get(out) == null) queue.push(d(out)); - break; - } - data = d(last); - arr = appendNodes(arr, data.node, data.block); - } - blocks.put(first, switch(last) { - case null -> new Block(arr, null, -1, new Block[0]); - case IfNode i -> new Block(arr, i, -1, new Block[2]); - case RegionNode r -> new Block(arr, r, r._inputs.find(prev), new Block[1]); - case ReturnNode r -> new Block(arr, r, -1, new Block[0]); - default -> throw new AssertionError("Unexpected block exit node"); - }); - } - // Update the next pointer of all blocks. - for (var block: blocks.values()) { - switch(block.exit) { - case null: break; - case IfNode i: - for(var out:i._outputs) if (out instanceof CProjNode p) block.next[p._idx] = blocks.get(p); - break; - case RegionNode r: - block.next[0] = blocks.get(r); - break; - default: - assert block.exit instanceof ReturnNode; - } - } - // And return the block for the start node. - return blocks.get(start); - } - - /** - * Create a schedule for the program reachable from start. - * @param start The start node - * @return The final schedule. - */ - public static Scheduler.Block schedule(StartNode start) { - var scheduler = new Scheduler(); - scheduler.doMarkAlive(start); - scheduler.doBuildCTF(start); - scheduler.doSchedule(); - return scheduler.build(start); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java deleted file mode 100644 index 7dcb88e5..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ /dev/null @@ -1,199 +0,0 @@ -package com.seaofnodes.simple.fuzzer; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.evaluator.Evaluator; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.node.StopNode; - -import java.util.ArrayList; -import java.util.Objects; -import java.util.Random; -import java.util.function.Consumer; - -/** - * Implementation of the fuzzer. - * Random testcases are generated using the ScriptGenerator. - * This will generate a script similar to how the parser parses it but instead of parsing it will - * randomly choose a variation the parser would parse and generate it. - * These scripts are the parsed by the parser and all exceptions are caught and filtered - * to only show one occurrence of one problem. This includes script which generate different - * results when graphs optimized with different worklist seeds are executed. - * To aid debugging scripts that cause errors are then reduced by applying rules and checking - * that the same issue persists. - */ -public class Fuzzer { - - private static final int EVAL_TIMEOUT = 1000; - - /** - * List of exceptions already encountered. - * This is used to filter new exceptions and discard them if they are already found. - */ - private final ArrayList<Throwable> exceptions = new ArrayList<>(); - - /** - * Filter and record an exception in a script. - * @param e The exception caused by the script - * @param script The script causing the exception - * @param reproducer Reproducer which should be used to reproduce the test case. - */ - private void recordException(Throwable e, String script, Consumer<String> reproducer, long seed) { - for (var ex : exceptions) { - if (FuzzerUtils.isExceptionFromSameCause(ex, e)) return; - } - exceptions.add(e); - System.out.println("========== Stack =========="); - e.printStackTrace(System.out); - System.out.println("========== Seed =========="); - System.out.println(seed); - System.out.println("========== Code ==========="); - System.out.println(script); - System.out.println("========= Reduced ========="); - System.out.println(Reducer.reduce(script, e, reproducer)); - System.out.println("==========================="); - System.out.flush(); - } - - private static boolean neq(Object a, Object b) { - if (Objects.equals(a, b)) return false; - if (a instanceof Evaluator.Obj ea && b instanceof Evaluator.Obj eb) { - if (ea.struct() != eb.struct()) return true; - for(int i=0;i<ea.fields().length;i++) { - if (neq(ea.fields()[i], eb.fields()[i])) return true; - } - return false; - } - return true; - } - - /** - * Check that two graphs result in the same output when supplied with a value - * @param e1 Evaluator for the first graph - * @param e2 Evaluator for the second graph - * @param in The input value to both graphs to test for an equal output - */ - private static void checkGraphs(Evaluator e1, Evaluator e2, long in) { - var r1 = e1.evaluate(in, EVAL_TIMEOUT); - var r2 = e2.evaluate(in, EVAL_TIMEOUT); - if (r1 == Evaluator.Status.TIMEOUT || r2 == Evaluator.Status.TIMEOUT) return; - if (neq(r1, r2)) - throw new RuntimeException("Different calculations values " + r1 + " vs " + r2); - } - - /** - * Run checks for script. Compile the script with two different optimizer worklist seeds. - * Check that exceptions raised in the parser by both methods are the same and only happens if the script may be invalid. - * If the script was successfully parsed check that both version behave the same. - * @param script The script to test - * @param valid If the script is definitely valid. If not some exceptions may be suppressed. - */ - private static void runCheck(String script, boolean valid) { - StopNode stop1; - try { - stop1 = FuzzerUtils.parse(script, 123); - } catch (RuntimeException e1) { - try { - FuzzerUtils.parse(script, 456); - } catch (RuntimeException e2) { - if (FuzzerUtils.isExceptionFromSameCause(e1, e2)) { - if (!valid || e1.getClass() == RuntimeException.class) return; - } else { - e1.addSuppressed(e2); - } - } - throw e1; - } - var stop2 = FuzzerUtils.parse(script, 456); - var e1 = new Evaluator(stop1); - var e2 = new Evaluator(stop2); - checkGraphs(e1, e2, 0); - checkGraphs(e1, e2, 1); - checkGraphs(e1, e2, 10); - } - - /** - * Check that the script does not result in exceptions. - * @param script The script to test. - * @param valid If the script is valid or if it might contain syntax errors. - */ - private void check(String script, boolean valid, long seed) { - try { - runCheck(script, valid); - } catch (Throwable e) { - recordException(e, script, s->runCheck(s, valid), seed); - } - } - - /** - * Run one test with the given seed, and check for peephole counts. - * @param seed The seed to use for generating this test case - */ - public int fuzzPeepTiming(long seed, int max_nids) { - var rand = new Random(seed); - var sb = new StringBuilder(); - new ScriptGenerator(rand, sb, true).genProgram(); - try { - var parser = new Parser(sb.toString()); - - var stop = parser.parse(); - int parse_peeps= Node.ITER_CNT; - int parse_nops = Node.ITER_NOP_CNT; - double parse_nop_ratio = (double)parse_nops/parse_peeps; - int nids = Node.UID(); - if( nids <= max_nids ) return max_nids; - - double parse_peeps_per_node = (double)parse_peeps/nids; - - stop.iterate(); - int iter_peeps= Node.ITER_CNT; - int iter_nops = Node.ITER_NOP_CNT; - double iter_nop_ratio = (double)iter_nops/iter_peeps; - double iter_peeps_per_node = (double)iter_peeps/nids; - - System.out.printf("%6d | Parsing: peeps: %5d, nops: %5d, ratio: %5.3f, last UID: %5d ps/node: %5.3f | Iter: peeps: %6d, nops: %6d, ratio: %5.3f, ps/node: %5.3f\n", - seed, - parse_peeps,parse_nops,parse_nop_ratio,nids,parse_peeps_per_node, - iter_peeps , iter_nops, iter_nop_ratio, iter_peeps_per_node); - return nids; - } catch (Throwable e) { - // ignore exceptions, let the normal fullPeeps catch and reduce, - // we're interested in valid program optimizations - } - return max_nids; - } - - - /** - * Run one test with the given seed. - * @param seed The seed to use for generating this test case - */ - public void fuzzPeeps(long seed) { - var rand = new Random(seed); - var sb = new StringBuilder(); - var valid = new ScriptGenerator(rand, sb, true).genProgram(); - check(sb.toString(), valid, seed); - } - - - /** - * Check that no exceptions happened. - * @return true if no exception happened - */ - public boolean noExceptions() { - return exceptions.isEmpty(); - } - - /** Run a fixed chapter-local seed without reducing or swallowing failures. */ - public void fuzzPeepsRegression(long seed) { - var rand = new Random(seed); - var sb = new StringBuilder(); - var valid = new ScriptGenerator(rand, sb, true).genProgram(); - try { - runCheck(sb.toString(), valid); - } catch (Throwable e) { - AssertionError ae = new AssertionError("Fuzzer regression seed failed: " + seed); - ae.initCause(e); - throw ae; - } - } -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java deleted file mode 100644 index f947a234..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java +++ /dev/null @@ -1,142 +0,0 @@ -package com.seaofnodes.simple.fuzzer; - -import com.seaofnodes.simple.IterPeeps; -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.node.Node; -import com.seaofnodes.simple.node.StopNode; - -import java.io.OutputStream; -import java.io.PrintStream; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Field; -import java.util.BitSet; - -/** - * Some utilities for the fuzzer. - */ -class FuzzerUtils { - - /** - * Get the val of a private field - */ - @SuppressWarnings("unchecked") - public static <T> T getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException { - return (T) (obj instanceof Class<?> clz ? getField(clz, name).get(null) : getField(obj.getClass(), name).get(obj)); - } - - /** - * Get access to a private field - */ - public static Field getField(Class<?> clz, String name) throws NoSuchFieldException { - var field = clz.getDeclaredField(name); - try { - field.setAccessible(true); - } catch (Throwable e) { - // Intentionally left empty - } - return field; - } - - /** - * Try to rethrow an exception or create a runtime exception and throw it. - */ - public static RuntimeException rethrow(Throwable e) { - if (e instanceof RuntimeException re) return re; - if (e instanceof Error er) throw er; - return new RuntimeException(e); - } - - /** - * Copy of Node.WVISIST - * Used to clear as exceptions might happen in the walk and left this bitset not cleared - */ - private static final BitSet NodeWalkVisit; - private static final java.util.Random WorkRandom; - - /** - * Write access to Iterate.MID_ASSERT - * Exception could happen mid assert and left this at true. Used to reset to false prior to parsing. - */ - private static final MethodHandle set_MID_ASSERT; - - /** - * Some problems might trigger debug output. Suppress this output with a null print stream during fuzzing. - */ - public static final PrintStream NULL_PRINT_STREAM = new PrintStream(OutputStream.nullOutputStream()); - - static { - try { - NodeWalkVisit = getFieldValue(Node.class, "WVISIT"); - WorkRandom = getFieldValue(getFieldValue(IterPeeps.class, "WORK"), "_R"); - set_MID_ASSERT = MethodHandles.lookup().unreflectSetter(getField( IterPeeps.class, "MID_ASSERT")); - } catch (Exception e) { - throw new ExceptionInInitializerError(e); - } - } - - /** - * Check that the messages are similar. - */ - private static boolean similarMessages(String a, String b) { - if (a == null || b == null) return true; - a = a.replaceAll("'\\w+'", "'normalized'").replaceAll("variable \\w+", "variable normalized"); - b = b.replaceAll("'\\w+'", "'normalized'").replaceAll("variable \\w+", "variable normalized"); - return a.equals(b); - } - - /** - * Check if both stack traces have the same first entry. Empty stack traces can happen with NPE optimization - * and allow the other to be any stack trace. - */ - private static boolean sameStackTraceOrigin(StackTraceElement[] a, StackTraceElement[] b) { - if (a == null || a.length == 0 || b == null || b.length == 0) return true; // One or both stack traces are missing. - return a[0].equals(b[0]); - } - - /** - * Checks if two exceptions are caused by the same problem. - */ - public static boolean isExceptionFromSameCause(Throwable a, Throwable b) { - if (a == null || b == null) return a == b; // Handle the null case - if (a.getClass() != b.getClass()) return false; // Different classes - if (!similarMessages(a.getMessage(), b.getMessage())) return false; // Different messages. Null is allowed as wildcard - var s1 = a.getStackTrace(); - var s2 = b.getStackTrace(); - if (!sameStackTraceOrigin(s1, s2)) return false; // Different stack trace origins - if (!isExceptionFromSameCause(a.getCause(), b.getCause())) return false; // Different causes - var o1 = a.getSuppressed(); - var o2 = b.getSuppressed(); - if (o1 == null || o2 == null || o1.length != o2.length) return o1 == o2; // Different suppression - for (int i=0; i<o1.length; i++) { - if (!isExceptionFromSameCause(o1[i], o2[i])) return false; // Different suppression - } - return true; - } - - /** - * Parse and optimize using the given worklist seed - */ - public static StopNode parse(String script, long workListSeed) { - var err = System.err; - var out = System.out; - try { - System.setErr(NULL_PRINT_STREAM); - System.setOut(NULL_PRINT_STREAM); - try { - set_MID_ASSERT.invokeExact(false); - } catch (Throwable e) { - throw rethrow(e); - } - var parser = new Parser(script); - WorkRandom.setSeed(workListSeed); - var stop = parser.parse(); - return stop.iterate(); - } finally { - NodeWalkVisit.clear(); - System.setErr(err); - System.setOut(out); - } - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java b/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java deleted file mode 100644 index 3731b40c..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.seaofnodes.simple.fuzzer; - -import java.util.Arrays; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * This class is used to reduce test cases created by the fuzzer. - * It applies rewrite rules to the test case and checks that the - * test still produces the same exception. - * This is done in a fixpoint iteration until every rewrite produces - * no or a different exception. - */ -public class Reducer { - - private interface Replacer extends BiConsumer<StringBuilder, Matcher> {} - - /** - * Holder for the rewrite rules - */ - private record Replace(Pattern pattern, boolean iterate, Replacer... replacements) { - Replace(Pattern p, Object... replacements) { - this(p, replacements[0] == Boolean.TRUE, Arrays.stream(replacements).filter(o->!(o instanceof Boolean)).map(Replace::from).toArray(Replacer[]::new)); - } - private static Replacer from(Object o) { - if (o instanceof Replacer r) return r; - if (o instanceof Integer i) return (s, m)->s.append(m.group(i)); - if (o instanceof String r) return (s, m)->s.append(r); - throw new IllegalArgumentException(); - } - } - - /** - * List of rewrite rules. - */ - private static final Replace[] REPLACEMENTS = { - new Replace(Pattern.compile("\\b(?:[^\\W0]|\\w\\w+)\\b"), "0", "1"), - new Replace(Pattern.compile("(?<=\\n|^)[^\\n]+(?=\\n|$)"), true, ""), - new Replace(Pattern.compile("(?<=[+\\-*/<>=(])--"), ""), - new Replace(Pattern.compile("\\b--"), "+"), - new Replace(Pattern.compile("-"), ""), - new Replace(Pattern.compile("\\b\\+-"), "-"), - new Replace(Pattern.compile("(?<![+\\-*/<>=])(?:-|\\*|/|>=|<=|>|<|!=|==)(?![+\\-*/<>=])"), "+"), - new Replace(Pattern.compile("\\{((?:[^{}]|\\{})+)(?:\\n[ \\t]*)?}"), "", "{}", 1), - new Replace(Pattern.compile("(?<=\\n|^)([ \\t]*)\\{\\n(.*?)\\n\\1}", Pattern.DOTALL), 2), - new Replace(Pattern.compile("\\(([^()]+)\\)"), 1), - new Replace(Pattern.compile("\\d+\\+"), ""), - new Replace(Pattern.compile("\\+\\d+"), ""), - new Replace(Pattern.compile("\\n\\n+"), "\n"), - new Replace(Pattern.compile("^\\n+|\\n+$"), ""), - new Replace(Pattern.compile("else *"), ""), - new Replace(Pattern.compile("\\bif\\([^()]+\\) *"), ""), - new Replace(Pattern.compile("\\bwhile\\([^()]+\\) *"), ""), - new Replace(Pattern.compile("\\bstruct \\w+ \\{[^{}]*}"), "") - }; - - /** - * Predicate to test if the reduced script generates the same error. - */ - private final Predicate<String> tester; - - /** - * Create a reducer with a test function. - * @param tester Test function which returns true when the same issue is found in the script. - */ - private Reducer(Predicate<String> tester) { - this.tester = tester; - } - - /** - * Rewrites the script with one rewrite rule. - */ - private String doReplacement(String script, Pattern pattern, Replacer[] replacements) { - var matcher = pattern.matcher(script); - var sb = new StringBuilder(); - var last = 0; - var skip = 0; - outer: - while (matcher.find(last + skip)) { - sb.append(script, last, matcher.start()); - last = matcher.end(); - skip = last == matcher.start() ? 1 : 0; - var pos = sb.length(); - for (var replacement : replacements) { - replacement.accept(sb, matcher); - var tail = sb.length(); - sb.append(script, last, script.length()); - if (tester.test(sb.toString())) { - sb.setLength(tail); - continue outer; - } - sb.setLength(pos); - } - last = matcher.start(); - skip = 1; - } - sb.append(script, last, script.length()); - return sb.toString(); - } - - /** - * Clean up variable names from the random ones generated by the fuzzer. - */ - private String cleanVariables(String script) { - var matcher = Pattern.compile("\\b([0-9a-zA-Z_]+) ([0-9a-zA-Z_]+)").matcher(script); - int[] num = {0, 0}; - while (matcher.find()) { - var type = matcher.group(1); - var var = matcher.group(2); - if (type.equals("new")) continue; - String prefix = "v"; - int t = 0; - if (type.equals("struct")) { - t = 1; - prefix = "s"; - } - if (var.matches(prefix+"\\d+")) continue; - while (script.contains(prefix+num[t])) num[t]++; - var n = script.replaceAll("\\b"+var+"\\b", prefix + num[t]); - if (tester.test(n)) { - script = n; - } - } - return script; - } - - /** - * Run all rewrite rules once. - */ - private String doAllReplacements(String script) { - for (var replace: REPLACEMENTS) { - String old; - do { - old = script; - script = doReplacement(script, replace.pattern, replace.replacements); - } while (replace.iterate && !old.equals(script)); - } - return script; - } - - /** - * Reduce the script to a minimal version which produces the same problem with a test function. - * @param tester Test function which returns true when the same issue is found in the script. - */ - public static String reduce(String script, Predicate<String> tester) { - var reducer = new Reducer(tester); - String old; - do { - old = script; - script = reducer.doAllReplacements(script); - script = reducer.cleanVariables(script); - } while (!script.equals(old)); - return script; - } - - /** - * Reduce the script to a minimal version which produces the same problem with a reproducer. - * @param ex The exception originally caused by the script - * @param reproducer Test function which throws an exception which will be checked against ex. - */ - public static String reduce(String script, Throwable ex, Consumer<String> reproducer) { - return reduce(script, s->{ - Throwable nex = null; - try { - reproducer.accept(s); - } catch (Throwable e) { - nex = e; - } - return FuzzerUtils.isExceptionFromSameCause(nex, ex); - }); - } - -} diff --git a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java deleted file mode 100644 index d3cbb0ee..00000000 --- a/chapter17/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java +++ /dev/null @@ -1,880 +0,0 @@ -package com.seaofnodes.simple.fuzzer; - -import com.seaofnodes.simple.Parser; -import com.seaofnodes.simple.node.ScopeNode; -import com.seaofnodes.simple.type.TypeInteger; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.function.Predicate; - -/** - * Generate a pseudo random script. - * This generator will generate the same script for two random number generators initialized with the same seed. - * To generate valid code it implements the parser but instead of parsing it emits the statements and expressions. - * It is guaranteed to terminate. - */ -public class ScriptGenerator { - - private static final int MAX_STATEMENTS_PER_BLOCK = 5; - private static final int MAX_BINARY_EXPRESSIONS_PER_EXPRESSION = 10; - private static final int MAX_UNARY_EXPRESSIONS_PER_EXPRESSION = 6; - private static final int MAX_NAME_LENGTH = 10; - private static final int MAX_BLOCK_DEPTH = 4; - private static final int MAX_EXPRESSION_DEPTH = 10; - private static final int MAX_SUFFIX_RECURSIVE_DEPTH = 10; - - /** - * Number of spaces per indentation - */ - private static final int INDENTATION = 4; - /** - * Valid characters for identifiers. The last 10 are not valid for the first character. - */ - private static final String VAR_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"; - /** - * List of keywords not valid for identifiers. - */ - private static final HashSet<String> KEYWORDS; - - static { - try { - KEYWORDS = FuzzerUtils.getFieldValue(new Parser(""), "KEYWORDS"); - } catch (Throwable e) { - throw new ExceptionInInitializerError(e); - } - } - - /** - * Flag for a statement that it does not pass on control flow to the next statement. - */ - private static final int FLAG_STOP = 0x01; - /** - * Flag for a statement that it contains a final if statement without an else branch. - */ - private static final int FLAG_IF_WITHOUT_ELSE = 0x02; - /** - * Binary operators. - */ - private static final String[] BINARY_OP = {"==", "!=", "<", "<=", ">", ">=", "+", "-", "*", "/", "&", ">>", ">>>", "<<"}; - /** - * Unary operators. - */ - private static final String[] UNARY_OP = {"-"}; - - private static class Type { - final String name; - Type(String name) {this.name=name;} - boolean isa(Type other) { return this == other; } - } - - private static class TypeStruct extends Type { - record Field (String name, Type type, TypeStruct struct) {} - Field[] fields; - final TypeNullable nullable = new TypeNullable(this); - TypeStruct(String name) { - super(name); - } - boolean isa(Type other) { return this == other || other == nullable; } - } - - private static class TypeNullable extends Type { - - final Type base; - - TypeNullable(Type base) { - super(base.name+"?"); - this.base = base; - } - - } - - private static final Type TYPE_FLT = new Type("flt"); - - private static class TypeInt extends Type { - - TypeInt(String name) { - super(name); - } - - @Override - boolean isa(Type other) { - return super.isa(other) || other == TYPE_FLT || other instanceof TypeInt; - } - } - - private static final List<TypeInt> INTTYPES = new ArrayList<>(); - - static { - new Parser("", TypeInteger.BOT); - for( var e : Parser.defaultTypes().entrySet() ) { - if( e.getValue() instanceof TypeInteger ) { - INTTYPES.add(new TypeInt(e.getKey())); - } - } - } - - private static final Type TYPE_INT = INTTYPES.stream().filter(t->t.name.equals("int")).findAny().get(); - private static final Type TYPE_BOOL = INTTYPES.stream().filter(t->t.name.equals("bool")).findAny().get(); - - private static class Variable { - final String name; - final Type declared; - final Variable shadowing; - Type type; - boolean shadowed = false; - - Variable(String name, Type type, Variable var) { - this.name = name; - this.declared = type; - this.type = type; - this.shadowing = var; - } - } - - /** - * The random number generator used for random decisions. - */ - private final Random random; - /** - * Indentation depth. - */ - private int indentation = 0; - /** - * Output string builder - */ - private final StringBuilder sb; - /** - * Number of nested loops - */ - private int loopDepth = 0; - /** - * Current scope start in the variables list. - */ - private int currScopeStart = 0; - /** - * The depth of blocks allowed. - */ - private int depth = MAX_BLOCK_DEPTH; - /** - * The depth of expressions allowed. - */ - private int exprDepth = MAX_EXPRESSION_DEPTH; - /** - * Current variables in scope. - */ - private final ArrayList<Variable> variables = new ArrayList<>(); - /** - * All defined structs. - */ - private final ArrayList<TypeStruct> structs = new ArrayList<>(); - /** - * All forward declared structs - */ - private final ArrayList<TypeStruct> forwardStructs = new ArrayList<>(); - /** - * Allow to declare structs. - */ - private boolean allowStructs = true; - /** - * If this needs to generate valid scripts or is also allowed to generate invalid ones. - */ - private boolean generateValid; - /** - * If this script might be invalid. - */ - private boolean maybeInvalid = false; - - - /** - * - * @param random the random number generator. - * @param sb the script will be written to this string builder. - * @param generateValid if the script should be guaranteed valid or if it is allowed to contain bugs. - */ - public ScriptGenerator(Random random, StringBuilder sb, boolean generateValid) { - this.random = random; - this.sb = sb; - this.generateValid = generateValid; - } - - /** - * Random number with log distribution - * @param max The maximum value - * @return A random number in the range [0, max) with higher numbers being more uncommon. - */ - private int randLog(int max) { - max--; - int n = 1<<max; - var num = random.nextInt(n); - for (int i=max-1; i>=0; i--) { - if ((num & (1<<i))!=0) return max-i-1; - } - return max; - } - - /** - * Helper function to print indentation. - * @return sb for chaining - */ - private StringBuilder printIndentation() { - return sb.repeat(' ', indentation); - } - - /** - * Generate a random name for variables. This might be invalid when it is a keyword. - * @return The random name generated - */ - private StringBuilder getRandomName() { - int len = random.nextInt(MAX_NAME_LENGTH) + 1; - StringBuilder sb = new StringBuilder(len); - sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()-10))); - for (int i=1; i<len; i++) - sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()))); - return sb; - } - - /** - * Should invalid code be generated? - * @return True if invalid code should be generated - */ - private boolean generateInvalid() { - if (generateValid || random.nextInt(1000)>2) return false; - maybeInvalid = true; - return true; - } - - /** - * Lookup a variable by name - * @param name name of the variable - * @return The index of the variable in variables or -1 - */ - private int lookup(String name) { - for (int i=variables.size()-1; i>=0; i--) { - if (variables.get(i).name.equals(name)) return i; - } - return -1; - } - - /** - * Lookup a variable by name - * @param name name of the variable - * @return THe variable of null if not found - */ - private Variable lookupVar(String name) { - int idx = lookup(name); - return idx == -1 ? null : variables.get(idx); - } - - /** - * Add a variable to the current scope - * @param name The name of the variable - * @param type The declared type of the variable - */ - private void addVariable(String name, Type type) { - var old = lookupVar(name); - variables.add(new Variable(name, type, old)); - if (old != null) old.shadowed = true; - } - - /** - * Pop variables to level to - * @param to Level until which variables should be popped - */ - private void popVariables(int to) { - while (variables.size() > to) { - var v = variables.removeLast(); - if (v.shadowing != null) v.shadowing.shadowed = false; - } - } - - /** - * Get a random visible variable patching pred - * @param pred The predicate the variable needs to match - * @return A random visible variable matching pred or null if no variable mached pred. - */ - private Variable findVisibleVariablesMatching(Predicate<Variable> pred) { - int num=0; - for (var v:variables) { - if (!v.shadowed && pred.test(v)) num++; - } - if (num == 0) return null; - num = random.nextInt(num); - for (var v:variables) { - if (!v.shadowed && pred.test(v) && num-- == 0) return v; - } - throw new AssertionError(); - } - - /** - * Get a structure by name - * @param name Name of the structure - * @return The structure of null - */ - private TypeStruct getStruct(String name) { - for (var s:structs) { - if (s.name.equals(name)) return s; - } - for (var s:forwardStructs) if (s.name.equals(name)) return s; - return null; - } - - /** - * Generate a variable name. This might be new random name or a name from an outer scope. - * If the program is allowed to be invalid this can return keywords and already used variable names in the current scope. - * @return The variable name. - */ - private String getVarName() { - if (currScopeStart > 0 && random.nextInt(10) > 7) { - // Use a variable outside the current scope. - var v = variables.get(random.nextInt(currScopeStart)); - if (lookup(v.name) < currScopeStart) return v.name; - if (!generateValid) { - maybeInvalid = true; - return v.name; - } - } - // Generate a new random variable name - StringBuilder sb = getRandomName(); - var v = sb.toString(); - if (!generateValid && (KEYWORDS.contains(v) || lookup(v) >= currScopeStart)) { - maybeInvalid = true; - return v; - } - while (KEYWORDS.contains(v) || lookup(v) >= currScopeStart) { - sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()))); - v = sb.toString(); - } - return v; - } - - private <T> T randFromList(List<T> list) { - return list.get(random.nextInt(list.size())); - } - - /** - * Get a random structure name - * @return A random structure name - */ - private String getStructName() { - if (!structs.isEmpty() && generateInvalid()) return randFromList(structs).name; - if (!forwardStructs.isEmpty() && generateInvalid()) return randFromList(forwardStructs).name; - - // Generate a new random struct name - StringBuilder sb = getRandomName(); - var v = sb.toString(); - if (!generateValid && (KEYWORDS.contains(v) || getStruct(v) != null)) { - maybeInvalid = true; - return v; - } - while (KEYWORDS.contains(v) || getStruct(v) != null) { - sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()))); - v = sb.toString(); - } - return v; - } - - public TypeInt getIntType() { - return randFromList(INTTYPES); - } - - /** - * Get a random type - * @return A random type - */ - private Type getType(boolean allowForward) { - int t = random.nextInt(10); - if( t<5 || structs.isEmpty() ) return getIntType(); - if( t<7 ) return TYPE_FLT; - if (allowForward && random.nextInt(10)==0) { - int i=random.nextInt(forwardStructs.size()+1); - if (i>0) return forwardStructs.get(i-1).nullable; - var name = getStructName(); - var struct = new TypeStruct(name); - forwardStructs.add(struct); - return struct.nullable; - } - TypeStruct struct = randFromList(structs); - if (random.nextBoolean()) return struct.nullable; - return struct; - } - - private Type getType() { - return getType(false); - } - - /** - * Generates a program and writes it into the string builder supplied in the constructor. - * @return If the program is allowed to be invalid returns if something potentially invalid was generated. - */ - public boolean genProgram() { - var oldGenerateValid = generateValid; - if (!generateValid && random.nextBoolean()) generateValid = true; - this.maybeInvalid = false; - addVariable(ScopeNode.ARG0, TYPE_INT); - currScopeStart = variables.size(); - if ((genStatements() & FLAG_STOP) == 0) { - if (generateValid || random.nextInt(10)<7) { - genReturn(); - } else { - this.maybeInvalid = true; - } - } - generateValid = oldGenerateValid; - return !this.maybeInvalid; - } - - /** - * Generate a list of statements. - * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the last statement generated - */ - public int genStatements() { - var num = random.nextInt(MAX_STATEMENTS_PER_BLOCK * (depth+1)); - for (int i=0; i<num; i++) { - printIndentation(); - var stop = genStatement(); - sb.append("\n"); - if ((stop & FLAG_STOP) != 0) return stop; - } - return 0; - } - - /** - * Generate a single statement - * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement - */ - public int genStatement() { - return switch (random.nextInt(12)) { - case 0 -> allowStructs || generateInvalid() ? genStruct() : genAssignment(); - case 1 -> genBlock(); - case 2 -> genIf(); - case 3 -> genCountedLoop(); - case 4 -> genWhile(); - case 5, 6 -> genDecl(); - case 7, 8, 9 -> genAssignment(); - case 10 -> genNullCheck(); - default -> genExit(); - }; - } - - /** - * Generate a new structure - * @return 0 - */ - public int genStruct() { - int idx = random.nextInt(forwardStructs.size()+10); - TypeStruct struct; - if (idx < 10) { - var name = getStructName(); - struct = new TypeStruct(name); - forwardStructs.add(struct); - } else { - struct = forwardStructs.get(idx-10); - } - var fields = new TypeStruct.Field[generateInvalid() ? 0 : random.nextInt(10)]; - sb.append("struct ").append(struct.name).append(" {\n"); - indentation += INDENTATION; - for (int i=0; i<fields.length; i++) { - var fieldName = getRandomName(); - var type = getType(true); - printIndentation().append(type.name).append(" ").append(fieldName).append(";\n"); - fields[i] = new TypeStruct.Field(fieldName.toString(), type, struct); - } - indentation -= INDENTATION; - printIndentation().append("}"); - struct.fields = fields; - forwardStructs.remove(struct); - structs.add(struct); - return 0; - } - - /** - * Generate a statement for if, else and while blocks. - * This is special since it disallows declarations and prefers to generate blocks. - * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement - */ - public int genStatementBlock() { - var oldAllowStructs = allowStructs; - allowStructs = false; - indentation += INDENTATION; - int res; - if (depth == 0) { - res = random.nextBoolean() ? genAssignment() : genExit(); - } else { - depth--; - res = switch (random.nextInt(12)) { - case 1, 2, 3, 4, 5 -> { - depth++; - indentation -= INDENTATION; - var ret = genBlock(); - indentation += INDENTATION; - depth--; - yield ret; - } - case 6 -> genIf(); - case 7 -> genCountedLoop(); - case 8 -> genWhile(); - case 9 -> genAssignment(); - case 10 -> genNullCheck(); - default -> genExit(); - }; - depth++; - } - indentation -= INDENTATION; - allowStructs = oldAllowStructs; - return res; - } - - /** - * Generate an exit. This can be return or break and continue when in a loop. - * @return FLAG_STOP - */ - public int genExit() { - if (loopDepth == 0 && generateValid) return genReturn(); - return switch (random.nextInt(7)) { - case 0,1,2 -> { - if (loopDepth == 0) maybeInvalid = true; - sb.append("continue;"); - yield FLAG_STOP; - } - case 3,4,5 -> { - if (loopDepth == 0) maybeInvalid = true; - sb.append("break;"); - yield FLAG_STOP; - } - default -> genReturn(); - }; - } - - /** - * Generate a block statement. After a certain depth only empty blocks are generated to ensure termination of the generator. - * @return flag FLAG_STOP for the generated statement - */ - public int genBlock() { - if (depth == 0) { - sb.append("{}"); - return 0; - } - var oldAllowStructs = allowStructs; - var oldCSS = currScopeStart; - allowStructs = false; - currScopeStart = variables.size(); - depth--; - sb.append("{\n"); - indentation += INDENTATION; - var stop = genStatements(); - indentation -= INDENTATION; - printIndentation().append("}"); - popVariables(currScopeStart); - depth++; - currScopeStart = oldCSS; - allowStructs = oldAllowStructs; - return stop & ~FLAG_IF_WITHOUT_ELSE; - } - - /** - * Generate an if statement with an optional else block. - * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement - */ - public int genIf() { - sb.append("if("); - genExpression(TYPE_BOOL, true); - sb.append(") "); - var stop = genStatementBlock(); - if ((stop & FLAG_IF_WITHOUT_ELSE) == 0 && random.nextInt(10) > 3) { - sb.append("\n"); - printIndentation().append("else "); - stop &= genStatementBlock(); - } else { - stop = FLAG_IF_WITHOUT_ELSE; - } - return stop; - } - - /** - * Generate a block with a null check - * if (nullable) { handle_nullable_as_non_nullable; } - */ - public int genNullCheck() { - var v = findVisibleVariablesMatching(va->va.declared instanceof TypeNullable); - if (v == null) return genIf(); - var n = (TypeNullable)v.declared; - boolean negate = random.nextBoolean(); - sb.append("if("); - if (negate) sb.append("!"); - sb.append(v.name).append(")"); - if (!negate) v.type = n.base; - var stop = genStatementBlock(); - v.type = n; - if ((stop & FLAG_IF_WITHOUT_ELSE) == 0 && random.nextInt(10) > 3) { - sb.append("\n"); - printIndentation().append("else "); - if (negate) v.type = n.base; - stop &= genStatementBlock(); - v.type = n; - } else { - stop = FLAG_IF_WITHOUT_ELSE; - } - return stop; - } - - /** - * Generate a while loop. - * @return 0 - */ - public int genWhile() { - sb.append("while("); - genExpression(TYPE_BOOL, true); - sb.append(") "); - loopDepth++; - genStatementBlock(); - loopDepth--; - return 0; - } - - /** - * Generate a counted loop of the form - * <code> - * { - * int varname = start_expr; - * while (varname < end_expr) loop_body; - * } - * </code> - * @return 0 - */ - public int genCountedLoop() { - if (depth == 0) return genWhile(); - var oldCSS = currScopeStart; - currScopeStart = variables.size(); - sb.append("{\n"); - indentation += INDENTATION; - String name = getVarName(); - printIndentation().append("int ").append(name).append("="); - genExpression(TYPE_INT, true); - sb.append(";\n"); - addVariable(name, TYPE_INT); - printIndentation().append("while(").append(name).append("<"); - genExpression(TYPE_INT, true); - sb.append(") {\n"); - indentation += INDENTATION; - depth--; - loopDepth++; - printIndentation().append(name).append("=").append(name).append("+"); - genExpression(TYPE_INT, true); - sb.append(";\n"); - genStatements(); - loopDepth--; - depth++; - indentation -= INDENTATION; - printIndentation().append("}\n"); - indentation -= INDENTATION; - printIndentation().append("}"); - popVariables(currScopeStart); - currScopeStart = oldCSS; - return 0; - } - - /** - * Generate a declaration statement. - * @return 0 - */ - public int genDecl() { - var type = getType(); - var name = getVarName(); - sb.append(generateInvalid() ? getRandomName() : type.name).append(" ").append(name); - if (!(type instanceof TypeNullable) || random.nextBoolean()) { - sb.append("="); - genExpression(type, true); - } - sb.append(";"); - addVariable(name, type); - return 0; - } - - /** - * Generate an assignment statement. - * @return 0 - */ - public int genAssignment() { - Type type; - Type declared; - if (generateInvalid()) { - sb.append(getRandomName()); - type = getType(); - declared = type; - } else { - var v = findVisibleVariablesMatching(va->true); - if (v == null) return genDecl(); - sb.append(v.name); - type = v.type; - declared = v.declared; - } - if (generateInvalid()) { - var name = getRandomName(); - sb.append(".").append(name); - declared = getType(); - } else if (type instanceof TypeStruct s && s.fields.length > 0 && random.nextBoolean()) { - var field = s.fields[random.nextInt(s.fields.length)]; - sb.append(".").append(field.name); - declared = field.type; - } - sb.append("="); - genExpression(declared, true); - sb.append(";"); - return 0; - } - - /** - * Generate a return statement. - * @return FLAG_STOP - */ - public int genReturn() { - var type = getType(); - sb.append("return "); - genExpression(type, true); - sb.append(";"); - return FLAG_STOP; - } - - /** - * Generate a binary expression. - * This method does not care about operator precedence. - */ - public void genExpression(Type type, boolean change) { - if (change && generateInvalid()) type = getType(); - if (!(type instanceof TypeInt) && type != TYPE_FLT) { - genUnary(type, false); - return; - } - var num = randLog(MAX_BINARY_EXPRESSIONS_PER_EXPRESSION); - while(num-->0) { - genUnary(type, false); - sb.append(BINARY_OP[random.nextInt(BINARY_OP.length)]); - } - genUnary(type, false); - } - - /** - * Generate a unary expression. - */ - public void genUnary(Type type, boolean change) { - if (change && generateInvalid()) type = getType(); - if (!(type instanceof TypeInt) && type != TYPE_FLT) { - genSuffix(type, false); - return; - } - var num = randLog(MAX_UNARY_EXPRESSIONS_PER_EXPRESSION); - while(num-->0) { - sb.append(UNARY_OP[random.nextInt(UNARY_OP.length)]); - } - genSuffix(type, false); - } - - public TypeStruct.Field getField(Type type) { - int n=0; - for (var s: structs) { - for (var f: s.fields) { - if (f.type.isa(type)) n++; - } - } - if (n==0) return null; - n = random.nextInt(n); - for (var s: structs) { - for (var f: s.fields) { - if (f.type.isa(type)) if (n--==0) return f; - } - } - throw new AssertionError(); - } - - /** - * Generate - */ - public void genSuffix(Type type, boolean change) { - genSuffixRecursive(type, change, MAX_SUFFIX_RECURSIVE_DEPTH); - } - - public void genSuffixRecursive(Type type, boolean change, int depth) { - if (change && generateInvalid()) type = getType(); - if (depth > 0 && random.nextBoolean()) { - if (generateInvalid()) { - var field = getRandomName(); - var t = getType(); - genSuffixRecursive(t, true, depth-1); - sb.append(".").append(field); - return; - } else { - var field = getField(type); - if (field != null) { - genSuffixRecursive(field.struct, true, depth-1); - sb.append(".").append(field.name); - return; - } - } - } - genPrimary(type, false); - } - - /** - * Generate a primary expression. - */ - public void genPrimary(Type type, boolean change) { - if (change && generateInvalid()) type = getType(); - var rand = random.nextInt(10); - if (rand == 0 && exprDepth != 0) { - sb.append("("); - exprDepth--; - genExpression(type, false); - exprDepth++; - sb.append(")"); - } else if (rand < 6) { - genVariable(type); - } else { - genConst(type); - } - } - - /** - * Generate a constant. - */ - public void genConst(Type type) { - if (generateInvalid()) type = getType(); - if (type instanceof TypeInt || (type == TYPE_FLT && random.nextBoolean())) { - var rand = random.nextInt(10); - switch (rand) { - case 0 -> sb.append("true"); - case 1 -> sb.append("false"); - default -> sb.append(random.nextInt(1<<(rand-2))); - } - } else if (type == TYPE_FLT) { - sb.append(random.nextFloat()); - } else if (type instanceof TypeNullable n) { - if ((n.base instanceof TypeStruct s && s.fields == null) || random.nextBoolean()) { - sb.append("null"); - } else { - sb.append("new ").append(generateInvalid() ? getRandomName() : n.base.name); - } - } else { - sb.append("new ").append(generateInvalid() ? getRandomName() : type.name); - } - } - - /** - * Generate a variable. If there are none generate a number instead. - * If the program is allowed to be invalid a random name can be generated in some cases. - */ - public void genVariable(Type type) { - if (generateInvalid()) { - sb.append(getRandomName()); - return; - } - if (generateInvalid()) type = getType(); - var t = type; - var v = findVisibleVariablesMatching(va->va.type.isa(t)); - if (v == null) genConst(type); - else sb.append(v.name); - } - -} |
