From 18fd28edce813d36c491e42d9e64c53c7251d26e Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Fri, 7 Feb 2025 08:58:36 -0800 Subject: Chapter 19: Instruction Selection and Portable Compilation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: Hsiang-Ying Fu <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: hadha Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com> Co-authored-by: fuhsnn <66062782+fuhsnn@users.noreply.github.com> Co-authored-by: Symious <14933944+symious@users.noreply.github.com> Co-authored-by: RobertObkircher Co-authored-by: Jun Kawasaki Co-authored-by: Claude Opus 5 --- .../java/com/seaofnodes/simple/BrainFuckTest.java | 2 +- .../java/com/seaofnodes/simple/Chapter03Test.java | 2 +- .../java/com/seaofnodes/simple/Chapter04Test.java | 21 +- .../java/com/seaofnodes/simple/Chapter05Test.java | 64 +-- .../java/com/seaofnodes/simple/Chapter06Test.java | 30 +- .../java/com/seaofnodes/simple/Chapter08Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter09Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter11Test.java | 10 +- .../java/com/seaofnodes/simple/Chapter12Test.java | 2 +- .../java/com/seaofnodes/simple/Chapter15Test.java | 12 +- .../java/com/seaofnodes/simple/Chapter17bTest.java | 8 +- .../java/com/seaofnodes/simple/Chapter18Test.java | 58 +-- .../java/com/seaofnodes/simple/Chapter19Test.java | 529 +++++++++++++++++++++ src/test/java/com/seaofnodes/simple/Eval2.java | 6 +- .../java/com/seaofnodes/simple/FuzzerWrap.java | 7 +- src/test/java/com/seaofnodes/simple/Main.java | 17 + .../com/seaofnodes/simple/evaluator/Evaluator.java | 13 +- .../com/seaofnodes/simple/evaluator/Scheduler.java | 2 +- .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 15 +- .../com/seaofnodes/simple/fuzzer/FuzzerUtils.java | 36 +- .../seaofnodes/simple/fuzzer/ScriptGenerator.java | 16 +- src/test/java/com/seaofnodes/simple/progs/fcn.smp | 15 + src/test/java/com/seaofnodes/simple/progs/fcn2.smp | 5 + src/test/java/com/seaofnodes/simple/progs/fcn3.smp | 42 ++ .../java/com/seaofnodes/simple/progs/sieve.smp | 32 ++ 25 files changed, 758 insertions(+), 194 deletions(-) create mode 100644 src/test/java/com/seaofnodes/simple/Chapter19Test.java create mode 100644 src/test/java/com/seaofnodes/simple/Main.java create mode 100644 src/test/java/com/seaofnodes/simple/progs/fcn.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/fcn2.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/fcn3.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/sieve.smp (limited to 'src/test/java/com/seaofnodes/simple') diff --git a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java index 37ecde1c..8f09cb3d 100644 --- a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java +++ b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java @@ -63,7 +63,7 @@ for( int pc = 0; pc < program#; pc++ ) { } return output; """); - code.parse().opto().typeCheck().GCM(); + code.parse().opto().typeCheck()/*.instSelect("x86_64_v2", "SystemV")*/.GCM().localSched(); assertEquals("Hello World!\n", Eval2.eval(code, 0, 10000)); } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter03Test.java b/src/test/java/com/seaofnodes/simple/Chapter03Test.java index 409e23ed..8212d246 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter03Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter03Test.java @@ -46,7 +46,7 @@ public class Chapter03Test { @Test public void testRedeclareVar() { try { - new Parser("int a=1; int b=2; int c=0; int b=3; c=a+b;").parse(); + new CodeGen("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()); diff --git a/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/src/test/java/com/seaofnodes/simple/Chapter04Test.java index d2260e1f..934e794f 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter04Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter04Test.java @@ -34,14 +34,14 @@ public class Chapter04Test { public void testAddAddMul() { CodeGen code = new CodeGen("return arg+0+arg;"); code.parse(); - assertEquals("return (arg*2);", code.print()); + assertEquals("return (arg<<1);", code.print()); } @Test public void testPeephole3() { CodeGen code = new CodeGen("return 1+arg+2+arg+3; "); code.parse(); - assertEquals("return ((arg*2)+6);", code.print()); + assertEquals("return ((arg<<1)+6);", code.print()); } @Test @@ -90,8 +90,7 @@ public class Chapter04Test { // Adding functions means `main` does not constant fold the incoming arg value @Ignore @Test public void testConstantArg() { - CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2)); - code.parse(); + CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2), 123L).parse(); assertEquals("return 2;", code.expr().in(1).toString()); } @@ -139,12 +138,8 @@ public class Chapter04Test { @Test public void testBug3() { - try { - new Parser("inta=1; return a;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Undefined name 'inta'",e.getMessage()); - } + try { new CodeGen("inta=1; return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Undefined name 'inta'",e.getMessage()); } } @Test @@ -156,8 +151,8 @@ public class Chapter04Test { @Test public void testBug5() { - Parser parser = new Parser("return arg--2;"); - StopNode ret = parser.parse(); - assertEquals("return (arg--2);", ret.print()); + CodeGen code = new CodeGen("return arg--2;"); + code.parse(); + assertEquals("return (arg--2);", code.print()); } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/src/test/java/com/seaofnodes/simple/Chapter05Test.java index ff60c695..7a9414a2 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter05Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter05Test.java @@ -64,7 +64,7 @@ else b=a+1; return a+b;"""); code.parse().opto(); - assertEquals("return ((arg*2)+Phi(Region,2,3));", code.print()); + assertEquals("return ((arg<<1)+Phi(Region,2,3));", code.print()); } @Test @@ -79,7 +79,7 @@ else a=b+1; return a+b;"""); code.parse().opto(); - assertEquals("return ((Phi(Region,(arg*2),arg)+arg)+Phi(Region,4,5));", code.print()); + assertEquals("return ((Phi(Region,(arg<<1),arg)+arg)+Phi(Region,4,5));", code.print()); } @Test @@ -150,79 +150,51 @@ return a; @Test public void testTrue() { - StopNode stop = new Parser("return true;").parse(); - assertEquals("return 1;",stop.ret().print()); + CodeGen code = new CodeGen("return true;").parse(); + assertEquals("return 1;",code.print()); } @Test public void testHalfDef() { - try { - new CodeGen("if( arg==1 ) int b=2; return b;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Cannot define a 'b' on one arm of an if",e.getMessage()); - } + try { new CodeGen("if( arg==1 ) int b=2; return b;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Cannot define a 'b' 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()); - } + try { new CodeGen("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 CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Cannot define a 'a' on one arm of an if",e.getMessage()); - } + try { new CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Cannot define a 'a' 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()); - } + try { new CodeGen("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()); - } + try { new CodeGen("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()); - } + try { new CodeGen("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 CodeGen("int a=1; ififif(arg);a=2;return a;").parse(); - fail(); - } catch( RuntimeException e ) { - assertEquals("Undefined name 'ififif'",e.getMessage()); - } + try { new CodeGen("int a=1; ififif(arg);a=2;return a;").parse(); fail(); } + catch( RuntimeException e ) { assertEquals("Undefined name 'ififif'",e.getMessage()); } } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/src/test/java/com/seaofnodes/simple/Chapter06Test.java index 2107cdb0..117531f0 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter06Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter06Test.java @@ -103,8 +103,7 @@ else if( arg==3 ) else a=5; return a; -""", TypeInteger.constant(2)); - code.parse().opto(); +""", TypeInteger.constant(2), 123L).parse().opto(); assertEquals("return 5;", code.print()); } @@ -124,8 +123,7 @@ else if( arg==3 ) else a=5; return a; -""", TypeInteger.constant(1)); - code.parse().opto(); +""", TypeInteger.constant(1), 123L).parse().opto(); assertEquals("return 3;", code.print()); } @@ -165,7 +163,7 @@ else if( arg==3 ) else a=5; return a; -""", TypeInteger.constant(1)); +""", TypeInteger.constant(1), 123L); code.parse().opto(); assertEquals("return 3;", code.print()); } @@ -186,7 +184,7 @@ else if( arg==3 ) else a=5; return a; -""", TypeInteger.constant(3)); +""", TypeInteger.constant(3), 123L); code.parse().opto(); assertEquals("return 4;", code.print()); } @@ -210,7 +208,8 @@ return a+b; @Test public void testDemo1True() { - CodeGen code = new CodeGen(""" + CodeGen code = new CodeGen( +""" arg=1; int a = 0; int b = 1; @@ -220,14 +219,15 @@ if( arg ) { else b = 3; } return a+b; -""", TypeInteger.constant(1)); +""", TypeInteger.constant(1), 123L); code.parse().opto(); assertEquals("return 4;", code.print()); } @Test public void testDemo1False() { - CodeGen code = new CodeGen(""" + CodeGen code = new CodeGen( +""" arg=0; int a = 0; int b = 1; @@ -237,7 +237,7 @@ if( arg ) { else b = 3; } return a+b; -""", TypeInteger.constant(0)); +""", TypeInteger.constant(0), 123L); code.parse().opto(); assertEquals("return 1;", code.print()); } @@ -263,7 +263,8 @@ return a+b+c; @Test public void testDemo2True() { - CodeGen code = new CodeGen(""" + CodeGen code = new CodeGen( +""" arg=1; int a = 0; int b = 1; @@ -275,14 +276,15 @@ if( arg ) { else b = 3; } return a+b+c; -""", TypeInteger.constant(1)); +""", TypeInteger.constant(1), 123L); code.parse().opto(); assertEquals("return 6;", code.print()); } @Test public void testDemo2arg2() { - CodeGen code = new CodeGen(""" + CodeGen code = new CodeGen( +""" arg=2; int a = 0; int b = 1; @@ -294,7 +296,7 @@ if( arg ) { else b = 3; } return a+b+c; -""", TypeInteger.constant(2)); +""", TypeInteger.constant(2), 123L); code.parse().opto(); assertEquals("return 5;", code.print()); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/src/test/java/com/seaofnodes/simple/Chapter08Test.java index 03a2c50a..350df18b 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter08Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter08Test.java @@ -1,8 +1,6 @@ package com.seaofnodes.simple; -import com.seaofnodes.simple.evaluator.Evaluator; import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeInteger; import org.junit.Assert; import org.junit.Test; @@ -147,7 +145,7 @@ return arg; @Test public void testBreakOutsideLoop() { try { - new Parser(""" + new CodeGen(""" if(arg <= 10) { break; arg = arg + 1; diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java index b210f7e9..10f4a78b 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java @@ -41,7 +41,7 @@ else { return x; """); code.parse().opto(); - assertEquals("return Phi(Region,(arg*2),(Mul+1));", code.print()); + assertEquals("return Phi(Region,(arg<<1),(Shl+1));", code.print()); Assert.assertEquals( "2", Eval2.eval(code, 1)); Assert.assertEquals("23", Eval2.eval(code, 11)); } @@ -255,7 +255,7 @@ while(0==1) while(v0) v0=1+v0; """); code.parse().opto(); - assertEquals("return ~int;", code.print()); + assertEquals("return 0;", code.print()); } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/src/test/java/com/seaofnodes/simple/Chapter11Test.java index 9fd392d2..14819869 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter11Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter11Test.java @@ -48,17 +48,11 @@ public class Chapter11Test { assertEquals("S{x=1,y=2,z=3}",Eval2.eval(code,4)); } - @Test public void testLateSliceWorklist() throws ReflectiveOperationException { + @Test public void testLateSliceWorklist() { // 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 code = new CodeGen(Chapter10Test.NESTED_MEMORY).parse(); - 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 code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse(); code.opto(); assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/src/test/java/com/seaofnodes/simple/Chapter12Test.java index a15132d4..90aaf631 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter12Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter12Test.java @@ -183,7 +183,7 @@ return new S1.s=new S2; struct I {int i;}; struct P { !I? !pi; }; !P !p1 = new P; -!P p2 = new P; +!P !p2 = new P; p2.pi = new I; p2.pi.i = 2; if (arg) p1 = new P; diff --git a/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/src/test/java/com/seaofnodes/simple/Chapter15Test.java index 97f695bf..c38dfb00 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter15Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter15Test.java @@ -142,7 +142,7 @@ struct S { int x; flt y; }; !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]; +!S?[]?[] !iss = new !S?[]?[2]; iss[0] = new !S?[7]; iss[0][2] = s; @@ -151,10 +151,12 @@ flt rez = 1.2; if( iss[arg] ) if( iss[arg][2] ) rez = iss[arg][2].y; -return rez; -"""); - try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Might be null accessing '[]'",e.getMessage()); } +return rez;"""); + code.parse().opto().typeCheck(); + assertEquals("return Phi(Region,Phi(Region,.y,1.2),1.2);", code.print()); + assertEquals("3.14", Eval2.eval(code, 0)); + try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks + catch( Exception e ) { assertEquals("Index 10 out of bounds for length 2",e.getMessage()); } } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java index e83b43af..19cc25d9 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter17bTest.java +++ b/src/test/java/com/seaofnodes/simple/Chapter17bTest.java @@ -39,7 +39,7 @@ return arg++; return arg+++arg++; """); code.parse().opto(); - assertEquals("return ((arg*2)+1);", code.print()); + assertEquals("return ((arg<<1)+1);", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @@ -50,7 +50,7 @@ return arg+++arg++; return -arg---arg--; """); code.parse().opto(); - assertEquals("return (-((arg*2)+-1));", code.print()); + assertEquals("return (-((arg<<1)+-1));", code.print()); assertEquals("1", Eval2.eval(code, 0)); } @@ -344,7 +344,7 @@ var b = arg ? new Bar : null; return b ? b.x++ + b.x++ : -1; """); code.parse().opto(); - assertEquals("return Phi(Region,((.x*2)+1),-1);", code.print()); + assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); assertEquals("-1", Eval2.eval(code, 0)); assertEquals("1", Eval2.eval(code, 1)); } @@ -357,7 +357,7 @@ var b = arg ? new Bar; return b ? b.x++ + b.x++ : -1; """); code.parse().opto(); - assertEquals("return Phi(Region,((.x*2)+1),-1);", code.print()); + assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print()); assertEquals("-1", Eval2.eval(code, 0)); assertEquals("1", Eval2.eval(code, 1)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java index 7f2b92ac..f965edd4 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -9,30 +9,6 @@ import static org.junit.Assert.fail; import org.junit.Ignore; public class Chapter18Test { - @Test public void testFloatArrays() { - for( String type : new String[]{"flt","f64","f32"} ) { - CodeGen zero = new CodeGen("var a = new "+type+"[arg]; return a[arg-1];") - .parse().opto().typeCheck(); - assertEquals("0.0",Eval2.eval(zero,3)); - new CodeGen(type+" x = arg; var a = new "+type+"[arg]; a[0] = x; return a[0];") - .parse().opto().typeCheck(); - CodeGen loop = new CodeGen("var a = new "+type+"[arg]; "+ - "for(int i=0; i int #1}", Eval2.eval(code, 3)); } @Test @@ -216,7 +192,7 @@ var sq = { int x -> }; return sq(arg)+sq(3); """); - code.parse().opto(); + code.parse().opto().typeCheck().GCM().localSched(); assertEquals("Stop[ return (sq( 3)+sq( arg)); return (Parm_x(sq,int,3,arg)*x); ]", code._stop.toString()); assertEquals("13", Eval2.eval(code, 2)); } @@ -255,7 +231,7 @@ var fcn = arg ? { int x -> x*x; } : { int x -> x+x; }; return fcn(3); """); code.parse().opto(); - assertEquals("Stop[ return Phi(Region,{ int -> int #1},{ int -> int #2})( 3); return (Parm_x($fun,int,3)*x); return (Parm_x($fun,int,3)*2); ]", code._stop.toString()); + assertEquals("Stop[ return Phi(Region,{ int -> int #1},{ int -> int #2})( 3); return (Parm_x($fun,int,3)*x); return (Parm_x($fun,int,3)<<1); ]", code._stop.toString()); assertEquals("6", Eval2.eval(code, 0)); assertEquals("9", Eval2.eval(code, 1)); } @@ -264,7 +240,7 @@ return fcn(3); @Test public void testFcn5() { CodeGen code = new CodeGen("val fact = { int x -> x <= 1 ? 1 : x*fact(x-1); }; return fact(arg);"); - code.parse().opto(); + code.parse().opto().typeCheck(); assertEquals("Stop[ return fact( arg); return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*fact( Sub))); ]", code._stop.toString()); assertEquals( "1", Eval2.eval(code, 0)); assertEquals( "1", Eval2.eval(code, 1)); @@ -319,7 +295,6 @@ for(;;) { assertEquals("3", Eval2.eval(code, 0)); } - @Test public void testFcn9() { CodeGen code = new CodeGen( @@ -336,6 +311,31 @@ for(;;) { assertEquals(null, Eval2.eval(code, 0)); } + + @Test + public void testFcn10() { + CodeGen code = new CodeGen( +""" +struct Person { + int age; +}; + +val fcn = { !Person?[] !ps, int x -> + val tmp = ps[x]; + if( ps[x] ) + ps[x].age++; +}; + +var ps = new !Person?[2]; +ps[0] = new Person; +ps[1] = new Person; +fcn(ps,1); +"""); + code.parse().opto().typeCheck().GCM().localSched(); + assertEquals("return 0;", code._stop.toString()); + assertEquals("0", Eval2.eval(code, 0)); + } + // Function break @Test public void testErr1() { diff --git a/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/src/test/java/com/seaofnodes/simple/Chapter19Test.java new file mode 100644 index 00000000..1abda81f --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java @@ -0,0 +1,529 @@ +package com.seaofnodes.simple; +import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.*; + + +import org.junit.Test; +import static org.junit.Assert.*; +import static org.junit.Assert.fail; +import org.junit.Ignore; + +public class Chapter19Test { + @Test public void testPrintingFunctionLookup() { + var code = new CodeGen("return 0;").parse(); + var tfp = TypeFunPtr.make((byte)2,TypeTuple.make(TypeInteger.constant(987654)),TypeInteger.constant(123456),1L<<30); + var con = new ConstantNode(tfp); + var call = new CallNode(null,code._start,con,con); + // Diagnostic lookup may construct/intern the return-erased linker key. + con.toString(); + org.junit.Assert.assertNull(call.name()); + + var fun = new FunNode(null,tfp); + fun._name = "printerTarget"; + fun.addDef(code._start); + code.link(fun); + var otherReturn = tfp.makeFrom(TypeInteger.constant(654321)); + org.junit.Assert.assertSame(fun,code.link(otherReturn)); + org.junit.Assert.assertTrue(con.toString().contains("printerTarget")); + assertEquals("printerTarget",call.name()); + } + + + @Ignore + @Test + public void testJig() { + CodeGen code = new CodeGen(""" +return 0; +"""); + code.parse().opto().typeCheck(); + assertEquals("return 0;", code._stop.toString()); + assertEquals("0", Eval2.eval(code, 2)); + } + + + @Test + public void testString() { + CodeGen code = new CodeGen( +""" +struct String { + u8[] !cs; + int _hashCode; +}; + +val equals = { !String !self, !String s -> + if( self == s ) return true; + if( self.cs# != s.cs# ) return false; + for( int i=0; i< self.cs#; i++ ) + if( self.cs[i] != s.cs[i] ) + return false; + return true; +}; + +val hashCode = { !String self -> + self._hashCode + ? self._hashCode + : (self._hashCode = _hashCodeString(self)); +}; + +val _hashCodeString = { !String self -> + int hash=0; + if( self.cs ) { + for( int i=0; i< self.cs#; i++ ) + hash = hash*31 + self.cs[i]; + } + if( !hash ) hash = 123456789; + return hash; +}; + +!String !s = new String { cs = new u8[17]; }; +s.cs[0] = 67; // C +s.cs[1] = 108; // l +hashCode(s);"""); + code.parse().opto().typeCheck().GCM().localSched(); + assertEquals("Stop[ return Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash)))); return Phi(Region,1,0,0,1); ]", code._stop.toString()); + assertEquals("-2449306563677080489", Eval2.eval(code, 2)); + } + + @Test + public void testBasic0() { + CodeGen code = new CodeGen("return 0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return 0;", code._stop.toString()); + } + + @Test + public void testBasic1() { + CodeGen code = new CodeGen("return arg+1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (inc,arg);", code._stop.toString()); + } + + @Test + public void testBasic2() { + CodeGen code = new CodeGen("return -17;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return -17;", code._stop.toString()); + } + + + @Test + public void testBasic3() { + CodeGen code = new CodeGen("return arg==1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (set==,(cmp,arg));", code._stop.toString()); + } + + @Test + public void testBasic4() { + CodeGen code = new CodeGen("return arg<<1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (shli,arg);", code._stop.toString()); + } + + @Test + public void testBasic5() { + CodeGen code = new CodeGen("return arg >> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (sari,arg);", code._stop.toString()); + } + + @Test + public void testBasic6() { + CodeGen code = new CodeGen("return arg >>> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (shri,arg);", code._stop.toString()); + } + + @Test + public void testBasic7() { + CodeGen code = new CodeGen("return arg / 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (div,arg,2);", code._stop.toString()); + } + + @Test + public void testBasic8() { + CodeGen code = new CodeGen("return arg * 6;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (muli,arg);", code._stop.toString()); + } + + @Test + public void testBasic9() { + CodeGen code = new CodeGen("return arg & 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (andi,arg);", code._stop.toString()); + } + + @Test + public void testBasic10() { + CodeGen code = new CodeGen("return arg | 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (ori,arg);", code._stop.toString()); + } + + @Test + public void testBasic11() { + CodeGen code = new CodeGen("return arg ^ 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (xori,arg);", code._stop.toString()); + } + + @Test + public void testBasic12() { + CodeGen code = new CodeGen("return arg + 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testBasic13() { + CodeGen code = new CodeGen("return arg - 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (subf,(i2f8,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testBasic14() { + CodeGen code = new CodeGen("return arg * 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (mulf,(i2f8,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testBasic15() { + CodeGen code = new CodeGen("return arg / 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (divf,(i2f8,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testBasic16() { + CodeGen code = new CodeGen( +""" +int arg1 = arg + 1; +return arg1 / arg;"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (div,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testBasic17() { + CodeGen code = new CodeGen( +""" +int arg1 = arg + 1; +return arg1 * arg; +"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (mul,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testBasic18() { + CodeGen code = new CodeGen( + """ + int arg1 = arg + 1; + return arg1 << arg; + """); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (shl,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testBasic19() { + CodeGen code = new CodeGen( + """ + int arg1 = arg + 1; + return arg1 >> arg; + """); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (sar,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testBasic20() { + CodeGen code = new CodeGen( + """ + int arg1 = arg + 1; + return arg1 >>> arg; + """); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (shr,(inc,arg),arg);", code._stop.toString()); + } + + @Test + public void testToFloat() { + CodeGen code = new CodeGen(""" + int a = arg; + return a + 2.0; + """ + ).parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString()); + } + + @Test + public void testIfStmt() { + CodeGen code = new CodeGen( +""" +int a = 1; +if (arg == 1) + a = arg+2; +else { + a = arg-3; +} +return a;"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print()); + } + + @Test + public void testIfMerge2() { + CodeGen code = new CodeGen( +""" +int a=arg+1; +int b=arg+2; +if( arg==1 ) + b=b+a; +else + a=b+1; +return a+b;"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print()); + } + + @Test + public void testLoop() { + CodeGen code = new CodeGen( +""" +int sum=0; +for( int i=0; i + flt guess = x; + while( 1 ) { + flt next = (x/guess + guess)/2; + if( next == guess ) return guess; + guess = next; + } +}; +flt farg = arg; +return sqrt(farg); +"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("return Phi(Loop,(i2f8,arg),(divf,(addf,(divf,i2f8,Phi_guess),Phi_guess),2.0f));", code.print()); + }; + + + @Test + public void sieveOfEratosthenes() { + CodeGen code = new CodeGen( +""" +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 x*x; } : { int x -> x+x; }; +return fcn(2)*10 + fcn(3); +"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("Stop[ return (add,Phi(Region,{ int -> int #1},{ int -> int #2})( 3),(muli,Phi_( 2))); return (mul,Parm_x($fun,int,3,2),x); return (shli,Parm_x($fun,int,3,2)); ]", code.print()); + } + + @Test + public void testFcn2() { + CodeGen code = new CodeGen( +""" +val sq = { int x -> x*x; }; +return sq(arg) + sq(3); +"""); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + assertEquals("Stop[ return (add,sq(),sq()); return (mul,Parm_x(sq,int,3,arg),x); ]", code.print()); + } + + @Test public void testSelectedMemory() { + for( String cpu : new String[]{"x86_64_v2","riscv"} ) + for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY, + Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY} ) { + var code = new CodeGen(src).parse().opto().typeCheck(); + var aliases = memoryPhis(code._stop); + code.instSelect(cpu,"SystemV"); + assertEquals(aliases,memoryPhis(code._stop)); + code.GCM().localSched(); + code._stop.walk(n -> { + if( !(n instanceof CFGNode) && !(n instanceof ProjNode) ) + assertTrue(n.in(0) instanceof CFGNode); + if( n instanceof NewNode nn ) { + assertEquals(2,((TypeTuple)nn._type)._types.length); + assertTrue(nn.mem() instanceof MemMergeNode); + assertNull(nn.mem().in(1)); // Partial allocation input. + MachNode mach = (MachNode)nn; + assertNull(mach.regmap(1)); + assertNotNull(mach.regmap(2)); + assertNotNull(mach.outregmap(0)); + assertNull(mach.outregmap(1)); + } + if( n instanceof CallNode call ) { + assertEquals(4,call.nIns()); // ctrl, memory, two args; direct target is embedded. + } + return null; + }); + } + } + + private static java.util.ArrayList memoryPhis(Node stop) { + var phis = new java.util.ArrayList(); + // Count only definitions reachable from Stop, as instruction selection does. + // A dead Phi cycle can still appear through the reverse use edges. + var seen = new java.util.BitSet(); + var work = new java.util.ArrayList(); + work.add(stop); + for( int i=0; i + n instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.AddMemX86 ? n : null); + assertNotNull(read); + int writes = 0; + for( Node use : read.antiDeps() ) + if( use instanceof MemOpNode st && st.isMem() ) { + assertSame(read.cfg0(),st.cfg0()); + assertTrue(st._inputs.find(read)>=0); + assertTrue(read.cfg0()._outputs.find(read) 0L; + case TypeFloat tf -> 0D; + default -> null; + }; } return new Object[]{new Obj(type,body),null}; - } private Object load(LoadNode load) { @@ -341,7 +344,7 @@ public class Evaluator { assert block != null; for (; i < block.nodes().length; i++) { if (!(block.nodes()[i] instanceof PhiNode phi)) break; - var val = region instanceof FunNode fun && "main".equals(fun.sig()._name) && ((ParmNode)phi)._idx==2 && exit==1 + var val = region instanceof FunNode fun && "main".equals(fun._name) && ((ParmNode)phi)._idx==2 && exit==1 ? parameter : val(phi.in(exit)); phiCache.add(val); diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java index 52d8f3e3..4bfa7628 100644 --- a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java +++ b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java @@ -508,7 +508,7 @@ public class Scheduler { private static CFGNode findSingleCFGOut(Node node) { if (node instanceof StartNode) { for(var n:node._outputs) - if (n instanceof FunNode fun && "main".equals(fun.sig()._name) ) + if (n instanceof FunNode fun && "main".equals(fun._name) ) return fun; return null; } diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index a5634e82..851380cf 100644 --- a/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -126,17 +126,16 @@ public class Fuzzer { var code = new CodeGen(sb.toString()); code.parse(); - int parse_peeps= Node.ITER_CNT; - int parse_nops = Node.ITER_NOP_CNT; + int nids = code.UID(); + if( nids <= max_nids ) return max_nids; // Smaller, looking for trends as we grow + int parse_peeps= code._iter_cnt; + int parse_nops = code._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; code.opto(); - int iter_peeps= Node.ITER_CNT; - int iter_nops = Node.ITER_NOP_CNT; + int iter_peeps= code._iter_cnt; + int iter_nops = code._iter_nop_cnt; double iter_nop_ratio = (double)iter_nops/iter_peeps; double iter_peeps_per_node = (double)iter_peeps/nids; @@ -173,7 +172,7 @@ public class Fuzzer { return exceptions.isEmpty(); } - /** Run a fixed seed without reducing or swallowing failures. */ + /** 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(); diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java index 4509c6fc..5c89a485 100644 --- a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java +++ b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java @@ -4,6 +4,7 @@ import com.seaofnodes.simple.CodeGen; import com.seaofnodes.simple.IterPeeps; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.StopNode; +import com.seaofnodes.simple.type.TypeInteger; import java.io.OutputStream; import java.io.PrintStream; import java.lang.invoke.MethodHandle; @@ -46,34 +47,11 @@ class FuzzerUtils { 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. */ @@ -122,17 +100,9 @@ class FuzzerUtils { try { System.setErr(NULL_PRINT_STREAM); System.setOut(NULL_PRINT_STREAM); - try { - set_MID_ASSERT.invokeExact(false); - } catch (Throwable e) { - throw rethrow(e); - } - var code = new CodeGen(script); - code.parse(); - WorkRandom.setSeed(workListSeed); - return code.opto().typeCheck().GCM().localSched(); + var code = new CodeGen(script, TypeInteger.BOT, workListSeed); + return code.parse().opto().typeCheck().GCM().localSched(); } finally { - NodeWalkVisit.clear(); System.setErr(err); System.setOut(out); } diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java index d061a656..167a5b24 100644 --- a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java +++ b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java @@ -3,12 +3,13 @@ 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; +import static com.seaofnodes.simple.Parser.KEYWORDS; + /** * Generate a pseudo random script. @@ -34,18 +35,6 @@ public class ScriptGenerator { * 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 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. @@ -108,7 +97,6 @@ public class ScriptGenerator { private static final List 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())); diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn.smp b/src/test/java/com/seaofnodes/simple/progs/fcn.smp new file mode 100644 index 00000000..43d398b3 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/fcn.smp @@ -0,0 +1,15 @@ + +struct Person { + int age; +}; + +val fcn = { !Person?[] ps, int x -> + val tmp = ps[x]; + if( ps[x] ) + ps[x].age++; +}; + +!Person?[] !ps = new !Person?[2]; +ps[0] = new Person; +ps[1] = new Person; +fcn(ps,1); diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn2.smp b/src/test/java/com/seaofnodes/simple/progs/fcn2.smp new file mode 100644 index 00000000..049132d9 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/fcn2.smp @@ -0,0 +1,5 @@ + +val fcn = (arg&1) + ? { int x -> x*x; } + : { int x -> x+x; }; +fcn(arg); diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn3.smp b/src/test/java/com/seaofnodes/simple/progs/fcn3.smp new file mode 100644 index 00000000..55b74455 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/fcn3.smp @@ -0,0 +1,42 @@ + +//val fact = { int n -> n <= 1 ? 1 : fact(n-1)*n; } +struct LL { !LL? !next; int payload; }; + + +struct String { + + val _hashCodeString = { -> + int hash=0; + for( int i=0; i< cs#; i++ ) + hash = hash*31 + cs[i]; + if( !hash ) hash = 123456789; + return hash; + }; + + val equals = { !String s -> + if( self == s ) return true; + if( cs# != s.cs# ) return false; + for( int i=0; i< #; i++ ) + if( [i] != s.cs[i] ) + return false; + return true; + }; + + int _hashCode = hash(); + u8[] cs; +}; + + + +val hashCode= { !String self -> + int hash = self._hashCode + ? self._hashCode + : self._hashCode = _hashCodeString(self); + return hash; +}; + + +String s = new String { cs = new u8[17]; }; +s.cs[0] = 67; // C +s.cs[1] = 108; // l +hashCode(s); diff --git a/src/test/java/com/seaofnodes/simple/progs/sieve.smp b/src/test/java/com/seaofnodes/simple/progs/sieve.smp new file mode 100644 index 00000000..0788e6c5 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sieve.smp @@ -0,0 +1,32 @@ + +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; -- cgit v1.2.3