From 018379b0cf4e286ceacb1e39f34dd80f770e381e Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 21 Apr 2025 14:18:23 -0700 Subject: Chapter 22: A Hello, World! MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> --- .../java/com/seaofnodes/simple/BrainFuckTest.java | 2 +- .../java/com/seaofnodes/simple/Chapter01Test.java | 6 +- .../java/com/seaofnodes/simple/Chapter09Test.java | 9 +- .../java/com/seaofnodes/simple/Chapter10Test.java | 2 + .../java/com/seaofnodes/simple/Chapter13Test.java | 7 +- .../java/com/seaofnodes/simple/Chapter15Test.java | 6 +- .../java/com/seaofnodes/simple/Chapter16Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter17Test.java | 8 +- .../java/com/seaofnodes/simple/Chapter18Test.java | 17 +- .../java/com/seaofnodes/simple/Chapter19Test.java | 7 +- .../java/com/seaofnodes/simple/Chapter20Test.java | 7 +- .../java/com/seaofnodes/simple/Chapter21Test.java | 8 +- .../java/com/seaofnodes/simple/Chapter22Test.java | 240 ++++ src/test/java/com/seaofnodes/simple/Eval2.java | 22 +- src/test/java/com/seaofnodes/simple/EvalArm64.java | 1156 ++++++++++---------- src/test/java/com/seaofnodes/simple/EvalRisc5.java | 28 +- src/test/java/com/seaofnodes/simple/Simple.java | 246 ++--- src/test/java/com/seaofnodes/simple/TestC.java | 97 +- src/test/java/com/seaofnodes/simple/TypeTest.java | 16 +- .../seaofnodes/simple/fuzzer/ScriptGenerator.java | 2 +- src/test/java/com/seaofnodes/simple/progs/echo.smp | 3 + .../com/seaofnodes/simple/progs/helloWorld.smp | 3 + src/test/java/com/seaofnodes/simple/progs/jig.smp | 4 + .../java/com/seaofnodes/simple/progs/person.smp | 2 +- .../com/seaofnodes/simple/progs/riscv_load.smp | 4 + .../com/seaofnodes/simple/progs/sext_str_1.smp | 4 + .../seaofnodes/simple/progs/sext_str_fold_away.smp | 5 + .../simple/progs/sext_str_not_fold_away.smp | 4 + .../simple/progs/sext_str_not_fold_away_2.smp | 4 + .../com/seaofnodes/simple/progs/store_peep.smp | 3 + 30 files changed, 1116 insertions(+), 810 deletions(-) create mode 100644 src/test/java/com/seaofnodes/simple/Chapter22Test.java create mode 100644 src/test/java/com/seaofnodes/simple/progs/echo.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/helloWorld.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/riscv_load.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp create mode 100644 src/test/java/com/seaofnodes/simple/progs/store_peep.smp (limited to 'src/test') diff --git a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java index 2733620e..1e1a1dc3 100644 --- a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java +++ b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java @@ -16,7 +16,7 @@ public class BrainFuckTest { String brain_fuck = "Hello World!\n"; TestC.run("brain_fuck", brain_fuck, 40); - EvalRisc5 R5 = TestRisc5.build("brain_fuck", 0, 44, false); + EvalRisc5 R5 = TestRisc5.build("brain_fuck", 0, 28, false); int trap = R5.step(100000); assertEquals(0,trap); int ptr = (int)R5.regs[riscv.A0]; diff --git a/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/src/test/java/com/seaofnodes/simple/Chapter01Test.java index cddbdfcf..ac9adacc 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter01Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter01Test.java @@ -25,9 +25,9 @@ public class Chapter01Test { public void testZero() { CodeGen code = new CodeGen("return 0;"); code.parse(); - for( Node use : code._start._outputs ) - if( use instanceof ConstantNode con && con._type instanceof TypeInteger ) - assertEquals(TypeInteger.constant(0),con._type); + Node use = code.expr(); + if( use instanceof ConstantNode con && con._type instanceof TypeInteger ) + assertEquals(TypeInteger.constant(0),con._type); } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java index 401f346f..d4d02aa9 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java @@ -128,14 +128,14 @@ while(v1+arg) { } """); code.parse().opto(); - assertEquals("return 0;", code.print()); + assertEquals("return Top;", code.print()); } @Test public void testWhile0() { - CodeGen code = new CodeGen("while(0) continue; if(0) arg=0;"); + CodeGen code = new CodeGen("while(0) continue; if(0) arg=0; return arg;"); code.parse().opto(); - assertEquals("return 0;", code.print()); + assertEquals("return arg;", code.print()); } @Test @@ -145,6 +145,7 @@ if(0) while(0) { int arg=arg; while(0) {} } +return 0; """); code.parse().opto(); assertEquals("return 0;", code.print()); @@ -255,7 +256,7 @@ while(0==1) while(v0) v0=1+v0; """); code.parse().opto(); - assertEquals("return 0;", code.print()); + assertEquals("Stop[ ]", code.print()); } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/src/test/java/com/seaofnodes/simple/Chapter10Test.java index 1253e4a6..712704db 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter10Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter10Test.java @@ -304,6 +304,7 @@ if(0) { v0=v2; } } +return 0; """); code.parse().opto(); assertEquals("return 0;", code.print()); @@ -378,6 +379,7 @@ while(0) {} } } } } +return 0; """); code.parse().opto(); assertEquals("return 0;", code.print()); diff --git a/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/src/test/java/com/seaofnodes/simple/Chapter13Test.java index 6bf45a22..a19f85e3 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter13Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter13Test.java @@ -130,15 +130,15 @@ return n.i; @Test public void testNullRef4() { - CodeGen code = new CodeGen("-null-5/null-5;"); + CodeGen code = new CodeGen("return -null-5/null-5;"); try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); } + catch( Exception e ) { assertEquals("Cannot '+' null",e.getMessage()); } } @Test public void testNullRef5() { CodeGen code = new CodeGen("return null+42;"); try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); } + catch( Exception e ) { assertEquals("Cannot '+' null",e.getMessage()); } } @Test @@ -146,6 +146,7 @@ return n.i; CodeGen code = new CodeGen( """ struct S{}; +return 0; """); code.parse().opto(); assertEquals("return 0;", code.print()); diff --git a/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/src/test/java/com/seaofnodes/simple/Chapter15Test.java index 8b138b46..a98e62d8 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter15Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter15Test.java @@ -253,10 +253,10 @@ return new flt; public void testBad1() { CodeGen code = new CodeGen( """ -int is = new int[2]; +flt is = new int[2]; """); try { code.parse().opto(); fail(); } - catch( Exception e ) { assertEquals("Type *[int] is not of declared type int",e.getMessage()); } + catch( Exception e ) { assertEquals("Type *[int] is not of declared type flt",e.getMessage()); } } @Test @@ -323,6 +323,7 @@ while(0) {} u32 v7=0; int v8=0; while(0<--1>>>---(v7*0==v8)) {} +return 0; """); code.parse().opto(); assertEquals("return 0;", code.print()); @@ -347,6 +348,7 @@ else { } } } +return 0; """); code.parse().opto(); assertEquals("return 0;", code.print()); diff --git a/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/src/test/java/com/seaofnodes/simple/Chapter16Test.java index 930624e5..58bb4148 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter16Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter16Test.java @@ -128,7 +128,7 @@ p.x++; return p; """); try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } } @Test @@ -140,7 +140,7 @@ p.x++; return p; """); try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/src/test/java/com/seaofnodes/simple/Chapter17Test.java index 78515083..bdeb645c 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter17Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter17Test.java @@ -172,13 +172,13 @@ return s.x; @Test public void testVar6() { CodeGen code = new CodeGen("struct S{int x;}; S s=new S; s.x++; return s.x; // Error initializer so x is immutable "); try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } } @Test public void testVar7() { CodeGen code = new CodeGen("struct S{int x;}; S s=new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable "); try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } } @Test public void testVar8() { @@ -198,7 +198,7 @@ return s.x; @Test public void testVar10() { CodeGen code = new CodeGen("struct S{int x;}; val s=new S; s.x++; return s.x; // Error, has val so x is immutable "); try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } } @Test public void testVar11() { @@ -216,7 +216,7 @@ val xfoo = foo; // Throw away mutability xfoo.bar.x++; // Error, cannot mutate through xfoo """); try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); } + catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); } } @Test public void testVar12() { diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java index c0e28412..00d41a23 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -33,7 +33,7 @@ while(arg--) { } return a; """); - code.parse().opto().typeCheck().GCM(); + code.parse().opto().typeCheck(); assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString()); assertEquals("1", Eval2.eval(code, 0)); assertEquals("2", Eval2.eval(code, 1)); @@ -78,7 +78,7 @@ var sq = { int x -> return sq(arg)+sq(3); """); code.driver(Phase.LocalSched); - assertEquals("Stop[ return (#2+#2); return (Parm_x(sq,int)*x); ]", code._stop.toString()); + assertEquals("Stop[ return (Parm_x(sq,int)*x); return (#2+#2); ]", code._stop.toString()); assertEquals("13", Eval2.eval(code, 2)); } @@ -116,7 +116,7 @@ var fcn = arg ? { int x -> x*x; } : { int x -> x+x; }; return fcn(3); """); code.parse().opto(); - assertEquals("Stop[ return #2; return (Parm_x($fun1,int,3)*x); return (Parm_x($fun2,int,3)<<1); ]", code._stop.toString()); + assertEquals("Stop[ return (Parm_x($fun2,int,3)<<1); return (Parm_x($fun1,int,3)*x); return #2; ]", code._stop.toString()); assertEquals("6", Eval2.eval(code, 0)); assertEquals("9", Eval2.eval(code, 1)); } @@ -126,7 +126,7 @@ return fcn(3); public void testFcn5() { CodeGen code = new CodeGen("val fact = { int x -> x <= 1 ? 1 : x*fact(x-1); }; return fact(arg);"); code.parse().opto().typeCheck(); - assertEquals("Stop[ return #2; return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*#2)); ]", code._stop.toString()); + assertEquals("Stop[ return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*#2)); return #2; ]", code._stop.toString()); assertEquals( "1", Eval2.eval(code, 0)); assertEquals( "1", Eval2.eval(code, 1)); assertEquals( "2", Eval2.eval(code, 2)); @@ -158,7 +158,7 @@ val g = {->2;}; return 2; """); code.parse().opto().typeCheck().GCM(); - assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString()); + assertEquals("Stop[ return 2; return 1; return 1; ]", code._stop.toString()); assertEquals("1", Eval2.eval(code, 0)); } @@ -176,7 +176,7 @@ for(;;) { } """); code.driver(Phase.LocalSched); - assertEquals("Stop[ return #2; return Parm_i(x,int); ]", code._stop.toString()); + assertEquals("Stop[ return Parm_i(x,int); return #2; ]", code._stop.toString()); assertEquals("3", Eval2.eval(code, 0)); } @@ -214,7 +214,7 @@ val fcn = { Person?[] ps, int x -> var ps = new Person?[2]; ps[0] = new Person; ps[1] = new Person; -fcn(ps,1); +return fcn(ps,1); """); code.driver(Phase.LocalSched); assertEquals("return 0;", code._stop.toString()); @@ -249,6 +249,7 @@ for(;;) { if (i2i(2) == arg) break; i2i = null; } +return 0; """); try { code.parse().opto().typeCheck(); fail(); } catch( Exception e ) { assertEquals("Might be null calling { int -> int #1}?",e.getMessage()); } @@ -347,7 +348,7 @@ if (i2i) return i2i(arg); return f2f(o)(1); """); code.driver(Phase.LocalSched); - assertEquals("Stop[ return Phi(Region,#2,#2); return Parm_i(o,int); ]", code._stop.toString()); + assertEquals("Stop[ return Parm_i(i2i.o,int); return Phi(Region,#2,#2); ]", code._stop.toString()); assertEquals("1", Eval2.eval(code, 2)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/src/test/java/com/seaofnodes/simple/Chapter19Test.java index bce75c81..b8ef22d7 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter19Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java @@ -25,7 +25,7 @@ return 0; public void testString() throws IOException { String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp")); CodeGen code = new CodeGen(src).parse().opto().typeCheck().GCM().localSched(); - assertEquals("Stop[ return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); return Phi(Region,1,0,0,1); ]", code._stop.toString()); + assertEquals("Stop[ return Phi(Region,1,0,0,1); return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code._stop.toString()); //assertEquals("-4898613127354160978", Eval2.eval(code, 2)); } @@ -271,6 +271,7 @@ for( int i=0; i x*x; } : { int x -> x+x; }; return fcn(2)*10 + fcn(3); """); code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); - assertEquals("Stop[ return (add,#2,(muli,#2)); return (mul,Parm_x($fun1,int),x); return (shli,Parm_x($fun2,int)); ]", code.print()); + assertEquals("Stop[ return (shli,Parm_x($fun2,int)); return (mul,Parm_x($fun1,int),x); return (add,#2,(muli,#2)); ]", code.print()); } @Test @@ -326,6 +327,6 @@ val sq = { int x -> x*x; }; return sq(arg) + sq(3); """); code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); - assertEquals("Stop[ return (add,#2,#2); return (mul,Parm_x(sq,int),x); ]", code.print()); + assertEquals("Stop[ return (mul,Parm_x(sq,int),x); return (add,#2,#2); ]", code.print()); } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/src/test/java/com/seaofnodes/simple/Chapter20Test.java index 5b33d1e7..f442ae1a 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter20Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter20Test.java @@ -63,9 +63,10 @@ val sqrt = { int x -> guess = next; } }; -return sqrt(arg) + sqrt(arg+2); +int cast_int = arg+2; +return sqrt(arg) + sqrt(cast_int); """; - testCPU(src,"x86_64_v2", "Win64" ,23,null); + testCPU(src,"x86_64_v2", "Win64" ,55,null); testCPU(src,"riscv" , "SystemV",19,null); testCPU(src,"arm" , "SystemV",19,null); } @@ -103,7 +104,7 @@ return sqrt(arg) + sqrt(arg+2); String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp")); testCPU(src,"x86_64_v2", "SystemV", 9,null); testCPU(src,"riscv" , "SystemV", 5,null); - testCPU(src,"arm" , "SystemV", 6,null); + testCPU(src,"arm" , "SystemV", 3,null); } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/src/test/java/com/seaofnodes/simple/Chapter21Test.java index d8a5cebc..77d1e337 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -39,7 +39,7 @@ public class Chapter21Test { } @Test public void testInfinite() { - String src = "struct S { int i; }; S !s = new S; while(1) s.i++;"; + String src = "struct S { int i; }; S !s = new S; while(1) s.i++; return s.i;"; testCPU(src,"x86_64_v2", "SystemV",0,"return Top;"); testCPU(src,"riscv" , "SystemV",2,"return Top;"); testCPU(src,"arm" , "SystemV",2,"return Top;"); @@ -66,7 +66,7 @@ public class Chapter21Test { String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp")); testCPU(src,"x86_64_v2", "SystemV", 9,null); testCPU(src,"riscv" , "SystemV", 5,null); - testCPU(src,"arm" , "SystemV", 6,null); + testCPU(src,"arm" , "SystemV", 3,null); } @Test public void testStringExport() throws IOException { @@ -150,14 +150,14 @@ public class Chapter21Test { String fib = "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]"; TestC.run("fib", fib, 24); - EvalRisc5 R5 = TestRisc5.build("fib", 9, 16, false); + EvalRisc5 R5 = TestRisc5.build("fib", 9, 17, false); int trap = R5.step(100); assertEquals(0,trap); // Return register A0 holds fib(8)==55 assertEquals(55,R5.regs[riscv.A0]); // arm - EvalArm64 A5 = TestArm64.build("fib", 9, 16, false); + EvalArm64 A5 = TestArm64.build("fib", 9, 17, false); int trap_arm = A5.step(100); assertEquals(0,trap_arm); // Return register X0 holds fib(8)==55 diff --git a/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/src/test/java/com/seaofnodes/simple/Chapter22Test.java new file mode 100644 index 00000000..394ba336 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/Chapter22Test.java @@ -0,0 +1,240 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import com.seaofnodes.simple.node.cpus.arm.arm; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter22Test { + + @Test @Ignore + public void testJig() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp")); + //String src = Files.readString(Path.of("docs/examples/BubbleSort.smp")); + testCPU(src,"x86_64_v2", "Win64" ,-1,null); + testCPU(src,"riscv" , "SystemV",-1,null); + testCPU(src,"arm" , "SystemV",-1,null); + } + + static CodeGen testCPU( String src, String cpu, String os, int spills, String stop ) { + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os); + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + if( stop != null ) + assertEquals(stop, code._stop.toString()); + return code; + } + + + static int testCPUSize( String src, String cpu, String os, int spills, String stop ) { + return testCPU(src,cpu,os,spills,stop)._encoding._bits.size(); + } + + // Should not fold away + @Test public void testSextFail() throws IOException { + String src = """ +struct Person { i32 age;}; +Person !p = new Person; +p.age = (arg<<17)>>17; +return 0; +"""; + assertEquals(46, testCPUSize(src, "x86_64_v2","Win64",2,"return 0;")); + assertEquals(60, testCPUSize(src, "riscv","SystemV",4,"return 0;")); + assertEquals(56, testCPUSize(src, "arm","SystemV",4,"return 0;")); + + // do assertEquals here + EvalRisc5 R5 = TestRisc5.build("sext_str_not_fold_away", 0, 4, false); + int trap = R5.step(100); + assertEquals(0,trap); + + EvalArm64 A5 = TestArm64.build("sext_str_not_fold_away", 0, 4, false); + trap = A5.step(100); + assertEquals(0,trap); + + } + + // Should not fold away + @Test public void testSextFail2() throws IOException { + String src = """ + + struct Person { i32 age;}; + Person !p = new Person; + p.age = (arg<<48)>>48; + return 0; + """; + + EvalRisc5 R5 = TestRisc5.build("sext_str_not_fold_away_2", 0, 4, false); + int trap = R5.step(100); + assertEquals(0,trap); + + EvalArm64 A5 = TestArm64.build("sext_str_not_fold_away_2", 0, 4, false); + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + + assertEquals(46, testCPUSize(src, "x86_64_v2","Win64",2,"return 0;")); + assertEquals(60, testCPUSize(src, "riscv","SystemV",4,"return 0;")); + assertEquals(56, testCPUSize(src, "arm","SystemV",4,"return 0;")); + + } + + // Should fold away + @Test public void testSextSuccess() throws IOException { + String src = """ +// Should fold away sign extend +struct Person { i8 age;}; +Person !p = new Person; +p.age = (arg<<48)>>48; +return 0; + """; + + EvalRisc5 R5 = TestRisc5.build("sext_str_fold_away", 0, 5, false); + int trap = R5.step(100); + assertEquals(0,trap); + + EvalArm64 A5 = TestArm64.build("sext_str_fold_away", 0, 5, false); + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + + assertEquals(41, testCPUSize(src, "x86_64_v2","Win64",2,"return 0;")); + assertEquals(56, testCPUSize(src, "riscv","SystemV",5,"return 0;")); + assertEquals(52, testCPUSize(src, "arm","SystemV",5,"return 0;")); + // do assertEquals here + } + + + // Int now is changed to 4 bytes. + @Test public void testPerson() throws IOException { + String person = "6\n"; + TestC.run("person", person, 0); + + // Memory layout starting at PS: + int ps = 1<<16; // Person array pointer starts at heap start + // Person[3] = { len,pad,P0,P1,P2 }; // sizeof = 4*8 + // P0 = { age } // sizeof=8 + int p0 = ps+4*8+0*8; + // P1 = { age } // sizeof=8 + int p1 = ps+4*8+1*8; + // P2 = { age } // sizeof=8 + int p2 = ps+4*8+2*8; + EvalRisc5 R5 = TestRisc5.build("person", ps, 0, false); + R5.regs[riscv.A1] = 1; // Index 1 + R5.st8(ps,3); // Length + R5.st8(ps+1*8,p0); + R5.st8(ps+2*8,p1); + R5.st8(ps+3*8,p2); + R5.st8(p0, 5); // age= 5 + R5.st8(p1,17); // age=17 + R5.st8(p2,60); // age=60 + + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals( 5+0,R5.ld8(p0)); + assertEquals(17+1,R5.ld8(p1)); + assertEquals(60+0,R5.ld8(p2)); + + EvalArm64 A5 = TestArm64.build("person", ps, 0, false); + A5.regs[arm.X1] = 1; // Index 1 + A5.st8(ps, 3); + A5.st8(ps+1*8,p0); + A5.st8(ps+2*8,p1); + A5.st8(ps+3*8,p2); + A5.st8(p0, 5); // age= 5 + A5.st8(p1,17); // age=17 + A5.st8(p2,60); // age=60 + + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + assertEquals( 5+0, A5.ld8(p0)); + assertEquals(17+1, A5.ld8(p1)); + assertEquals(60+0, A5.ld8(p2)); + } + + @Test + public void testCoRecur() { + String src = """ +struct A { B? b; C? c; i64 ax; val az = x*2; }; +struct B { A? a; C? c; f32 bx; val bz = x*3; }; +struct C { A? a; B? b; f64 cx; val cz = x*x; }; +A !aa = new A{ ax=17; }; +B !bb = new B{ bx=3.14; a = aa; }; +C !cc = new C{ cx=2.73; a = aa; b = bb; }; +aa.b = bb; +aa.c = cc; +bb.c = cc; +val x = 5; // aa.az; // Error to self-define forward ref +return cc.cz; +"""; + CodeGen code = new CodeGen(src).parse().opto().typeCheck(); + assertEquals("return 25;", code._stop.toString()); + assertEquals("25", Eval2.eval(code, 0)); + } + + @Test + public void testHelloWorld() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/helloWorld.smp")); + TestC.run(src,TestC.CALL_CONVENTION,null,null,"build/objs/helloWorld","","Hello, World!",0); + + // Evaluate on RISC5 emulator + EvalRisc5 R5 = TestRisc5.build("helloWorld", 0, 2, false); + int trap = R5.step(100); + assertEquals(0,trap); + assertEquals(0,R5.regs[riscv.A0]); + assertEquals("Hello, World!",R5._stdout.toString()); + + // Evaluate on ARM emulator + EvalArm64 arm = TestArm64.build("helloWorld", 0, 2, false); + trap = arm.step(100); + assertEquals(0,trap); + assertEquals(0,arm.regs[0]); + assertEquals("Hello, World!",arm._stdout.toString()); + } + + @Test + public void testFinalArray() { + String src = """ +int N=4; +i32[] !is = new i32[N]; +for( int i=0; i // final array + int sum=0; + for( int i=0; i> 24) & 0xFF; - // [31:26] - int opcode2 = (ir >> 26) & 0x3F; - - switch(opcode2) { - case 5: { - //b - int imm26 = ir << 6 >> 6; - - pc = pc + (imm26 * 4) - 4; - // won't hit pc += 4 at the bottom because continue - // do it manually - pc += 4; + + // for the opcode we just match for bits[31:24]; + switch( opcode1 ) { + case 0x14, 0x15, 0x16, 0x17: { //b + int imm26 = ir << 6 >> 6; + pc = pc + (imm26 * 4) - 4; + rdid = -1; + break; + } + case 0x1E: { // floats + is_f = true; + int rs1 = (ir >> 5) & 0x1F; + int rs2 = (ir >> 16) & 0x1F; + + int op = (ir >> 10) & 0x3F; + switch(op) { + case 16: frval = fregs[rs1]; break; // fmov + case 10:frval = fregs[rs1] + fregs[rs2]; break; // fadd + + case 6: if (fregs[rs2] == 0) frval = 0; else frval = fregs[rs1] / fregs[rs2]; break; // fdiv + case 8: { + // fcmp + double lhs = fregs[rs1]; + double rhs = fregs[rs2]; + + Z = (lhs == rhs); + N = (lhs < rhs); + C = lhs >= rhs; + V = Double.isNaN(lhs) || Double.isNaN(rhs); rdid = -1; - continue ; - } - case 0x25: { - // bl (just calloc) - rval = pc + 4; - regs[arm.X30] = rval; - if(pc + 4 == 0) { - regs[rdid] = rval; - break outer; - } - int imm26 = (ir & 0x03FFFFFF); - int imm = (imm26 << 6) >> 6; - pc = pc + (imm << 2) ; - if (pc == SENTINEL_CALLOC) { - long size = regs[arm.X0]*regs[arm.X1]; - regs[arm.X0] = _heap; - _heap += (int)size; - // unwind pc - pc = (int)rval; - } - continue; + + break; + } + case 2: frval = fregs[rs1] * fregs[rs2]; break; // fmul + case 14: frval = fregs[rs1] - fregs[rs2]; break; // fsub + default: trap = (2+1); } + break; } - // for the opcode we just match for bits[31:24]; - switch(opcode1) { - case 0xD2: { - // movz - // get immediate - rval = (ir >> 5) & 0x7FFF; - break; + + case 0x38: { + // STRB/LDRB(register) + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch( ir >> 21 & 0x7 ) { + case 0x1 -> { // store + if( opcode1 == arm.OP_STORE_R_8 ) st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + else trap = (2+1); + rdid = -1; } - case 0x38: { - // STRB/LDRB(register) - int opc = ir >> 21 & 0x7; - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - // full opcode - opcode1 = (ir >> 21) & 0x7FF; - switch(opc) { - case 0x1: { - // store - if(opcode1 == arm.OP_STORE_R_8) { - st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); - }else { - trap = (2+1); - } - rdid = -1; - break; - } - case 0x3: { - // load - if(opcode1 == arm.OP_LOAD_R_8) { - rval = ld1s((int)regs[rn] + (int)regs[rm]); - } else { - trap = (2+1); - } - break; - } - default: trap = (2+1); - } + case 0x3 -> { // load + if( opcode1 == arm.OP_LOAD_R_8 ) rval = ld1s((int)regs[rn] + (int)regs[rm]); + else trap = (2+1); + } + default -> trap = (2+1); + } + break; + } + case 0x39: { + // STRB/LDRB(immediate) + // str/ldr(immediate) + // rn + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_8) { + st1((int)rval, (int)regs[rdid]); + } else trap = (2+1); + rdid = -1; + break; } - case 0x39: { - // STRB/LDRB(immediate) - // str/ldr(immediate) - // rn - int base = ir >> 5 & 0x1F; - int imm = (ir >> 10) & 0xFFF; - rval = regs[base] + imm; - int opc = (ir >> 22) & 0x3; - // full opcode - opcode1 = (ir >> 22) & 0x3FF; - - switch(opc) { - case 0x0: { - // store - if(opcode1 == arm.OP_STORE_IMM_8) { - st1((int)rval, (int)regs[rdid]); - } else trap = (2+1); - rdid = -1; - - break; - } - case 0x1: { - // LDR (immediate) - if(opcode1 == arm.OP_LOAD_IMM_8) { - rval = ld1s((int)rval); - } else trap = (2+1); - rdid = -1; - break; - } - default: trap = (2+1); - } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_8) { + rval = ld1s((int)rval); + } else trap = (2+1); + rdid = -1; break; } - case 0x78: { - // STRH/LDRH(register) - int opc = ir >> 21 & 0x7; - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - // full opcode - opcode1 = (ir >> 21) & 0x7FF; - switch(opc) { - case 0x1: { - // store - if(opcode1 == arm.OP_STORE_R_16) { - st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); - }else { - trap = (2+1); - } - rdid = -1; - break; - } - case 0x3: { - // load - if(opcode1 == arm.OP_LOAD_R_16) { - rval = ld1s((int)regs[rn] + (int)regs[rm]); - } else { - trap = (2+1); - } - break; - } - default: trap = (2+1); + default: trap = (2+1); + } + break; + } + case 0x54: { + // b.cond + int imm19 = ir << 8 >> 13; + imm19 = pc + (imm19 * 4) - 4; + rdid = -1; + + switch(ir & 0xF) { + case 0x0: if(Z) pc = imm19; break; // eq + case 0x1: if(!Z) pc = imm19; break; // ne + case 0x2: if(C) pc = imm19; break; // cs + case 0x3: if(!C) pc = imm19; break; + case 0x4: if(N) pc = imm19; break; // mi + case 0x5: if(!N) pc = imm19; break; // pl + case 0x6: if(V) pc = imm19; break; // vs + case 0x7: if(!V) pc = imm19; break; // vc + case 0x8: if(C && !Z) pc = imm19; break; // hi + case 0x9: if(!C || Z) pc = imm19; break; // ls + case 0xA: if(N == V) pc = imm19; break; // ge + case 0xB: if(N != V) pc = imm19; break; // lt + case 0xC: if(!Z && N == V) pc = imm19; break; // gt + case 0xD: if(Z || N != V) pc = imm19; break; // le + case 0xE: pc = imm19; break; // always executed(al) + default: trap = (2+1); + } + break; + } + + case 0x5c: { + // ldr + is_f = true; + // address = delta + int address = ir << 8 >> 13; + address *= 4; + if(address >= _buf.length-3) trap = (5 + 1); + frval = ld8f(pc + address); + break; + } + + case 0x78: { + // STRH/LDRH(register) + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + // store + if(opcode1 == arm.OP_STORE_R_16) { + st1((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); } + rdid = -1; break; } - - case 0x79: { - // STRH/LDRH(immediate) - int base = ir >> 5 & 0x1F; - int imm = (ir >> 10) & 0xFFF; - imm *= 2; - rval = regs[base] + imm; - int opc = (ir >> 22) & 0x3; - // full opcode - opcode1 = (ir >> 22) & 0x3FF; - - switch(opc) { - case 0x0: { - // store - if(opcode1 == arm.OP_STORE_IMM_16) { - st2((int)rval, (int)regs[rdid]); - } else trap = (2+1); - rdid = -1; - - break; - } - case 0x1: { - // LDR (immediate) - if(opcode1 == arm.OP_LOAD_IMM_16) { - rval = ld2s((int)rval); - } else trap = (2+1); - rdid = -1; - break; - } - default: trap = (2+1); + case 0x3: { + // load + if(opcode1 == arm.OP_LOAD_R_16) { + rval = ld1s((int)regs[rn] + (int)regs[rm]); + } else { + trap = (2+1); } break; } + default: trap = (2+1); + } + break; + } + case 0x79: { + // STRH/LDRH(immediate) + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + imm *= 2; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_16) { + st2((int)rval, (int)regs[rdid]); + } else trap = (2+1); + rdid = -1; - case 0x5c: { - // ldr - is_f = true; - // address = delta - int address = ir << 8 >> 13; - address *= 4; - if(address >= _buf.length-3) trap = (5 + 1); - frval = ld8f(pc + address); break; } - case 0xB9: { - // str/ldr(immediate) 32 bits - int base = ir >> 5 & 0x1F; - int imm = (ir >> 10) & 0xFFF; - imm *= 4; - - rval = regs[base] + imm; - int opc = (ir >> 22) & 0x3; - - opcode1 = (ir >> 22) & 0x3FF; - switch(opc) { - case 0x0: { - // store - if(opcode1 == arm.OP_STORE_IMM_32) { - st4((int)rval, (int)regs[rdid]); - } else trap = (2+1); - - rdid = -1; - break; - } - case 0x1: { - // LDR (immediate) - if(opcode1 == arm.OP_LOAD_IMM_32) { - rval = ld4s((int) rval); - } else trap = (2+1); - break; - } - default: trap = (2+1); - } + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_16) { + rval = ld2s((int)rval); + } else trap = (2+1); + rdid = -1; break; } - case 0xF9: { - // str/ldr(immediate) 64 bits - int base = ir >> 5 & 0x1F; - int imm = (ir >> 10) & 0xFFF; - - imm *= 8; - - rval = regs[base] + imm; - int opc = (ir >> 22) & 0x3; - // full opcode - opcode1 = (ir >> 22) & 0x3FF; - switch(opc) { - case 0x0: { - // store - if(opcode1 == arm.OP_STORE_IMM_64) { - st8((int)rval, regs[rdid]); - } else trap = (2+1); - - rdid = -1; - break; - } - case 0x1: { - // LDR (immediate) - if(opcode1 == arm.OP_LOAD_IMM_64) { - rval = ld8((int)rval); - } else trap = (2+1); - break; - } - default: trap = (2+1); - } - break; + default: trap = (2+1); } - case 0xf8: { - // LDR/STR (register + index) 64 bits - int opc = ir >> 21 & 0x7; - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - // full opcode - opcode1 = (ir >> 21) & 0x7FF; - switch(opc) { - case 0x1: { - if(opcode1 == arm.OP_STORE_R_64) { - st8((int)regs[rn] + (int)regs[rm], regs[rdid]); - }else { - trap = (2+1); - } - // store(expected to store the length here) - - rdid = -1; - break; - } - case 0x3: { - // load - if(opcode1 == arm.OP_LOAD_R_64) { - rval = ld8((int) (regs[rn] + regs[rm])); - } else { - trap = (2+1); - } - break; - } - default: trap = (2+1); - } + break; + } - break; - } + case 0x8A: { // AND + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + rval = regs[rn] & regs[rm]; + break; + } - case 0xB8: { - // LDR/STR (register + index) 32 bits - int opc = ir >> 21 & 0x7; - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - // full opcode - opcode1 = (ir >> 21) & 0x7FF; - switch(opc) { - case 0x1: { - if(opcode1 == arm.OP_STORE_R_32) { - st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); - }else { - trap = (2+1); - } - // store(expected to store the length here) - - rdid = -1; - break; - } - case 0x3: { - // load - if( opcode1 == arm.OP_LOAD_R_32) { - rval = ld4s((int) (regs[rn] + regs[rm])); - } else { - trap = (2+1); - } - break; - } - default: trap = (2+1); - } + case 0x8B: { + // add(shifted register) + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + long lhs = regs[rn]; + long rhs = regs[rm]; + rval = lhs + rhs; + break; + } - break; - } + case 0x90: { // adrp + int immhi = ( (ir& ~0x1F) << 8 ) >> 8; + int immlo = (ir>>29)& 3; + int imm = ((immhi<<2) | immlo) << 12; + rval = (pc & ~0xFFF) + imm; + break; + } - case 0xFC: { - // LDR/STR (register + index) float 64 bits - int opc = ir >> 21 & 0x7; - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - is_f = true; - // full opcode - opcode1 = (ir >> 21) & 0x7FF; - switch(opc) { - case 0x1: { - if(opcode1 == arm.OPF_STORE_R_64) { - st8((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); - }else { - trap = (2+1); - } - // store(expected to store the length here) - - rdid = -1; - break; - } - case 0x3: { - // load - if(opcode1 == arm.OPF_LOAD_R_64) { - frval = ld8f((int) (regs[rn] + regs[rm])); - } else { - trap = (2+1); - } - break; - } - default: trap = (2+1); - } + case 0x91: { // add(immediate) + int rn = (ir >> 5) & 0x1F; + int immediate = ir << 10 >> 20; + // only hit it twice + rval = regs[rn] + immediate; + break; + } + case 0x92: { // and(immediate) + int imm12 = (ir >> 10) & 0x1FFF; + long immediate = arm.decodeImm12(imm12); + int rn = (ir >> 5) & 0x1F; + rval = regs[rn] & immediate; + break; + } + case 0x93: { + // asr(immediate) + int imm = (ir >> 16) & 0x3F; + int rn = (ir >> 5) & 0x1F; + rval = regs[rn] >> (imm); + break; + } - break; + case 0x94, 0x95, 0x96, 0x97: { // bl + rval = pc + 4; + regs[arm.X30] = rval; + int imm26 = (ir & 0x03FFFFFF); + int imm = (imm26 << 6) >> 6; + pc = pc + (imm << 2) ; + if( pc == Encoding.SENTINEL_CALLOC ) { + assert (_heap&7) == 0; // 8-byte aligned + long size = regs[arm.X0]*regs[arm.X1]; + size = (size+7) & -8; // 8-byte aligned + regs[arm.X0] = _heap; + _heap += (int)size; + pc = (int)rval; + rdid = -1; } + if( pc == Encoding.SENTINEL_WRITE ) { + ByteArrayOutputStream baos = switch((int)regs[0]) { + case 1 -> _stdout==null ? (_stdout = new ByteArrayOutputStream()) : _stdout; + case 2 -> _stderr==null ? (_stderr = new ByteArrayOutputStream()) : _stderr; + default -> throw new IllegalArgumentException(); + }; + baos.write(_buf,(int)regs[1],(int)regs[2]); + regs[0] = regs[2]; + pc = (int)rval; + rdid = -1; + } + pc -= 4; + break; + } - case 0xBC: { - // LDR/STR (register + index) float 32 bits - int opc = ir >> 21 & 0x7; - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - is_f = true; - // full opcode - opcode1 = (ir >> 21) & 0x7FF; - switch(opc) { - case 0x1: { - if(opcode1 == arm.OPF_STORE_R_32) { - st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); - }else { - trap = (2+1); - } - // store(expected to store the length here) - - rdid = -1; - break; - } - case 0x3: { - // load - if(opcode1 == arm.OPF_LOAD_R_32) { - frval = ld4f((int) (regs[rn] + regs[rm])); - } else { - trap = (2+1); - } - break; - } - default: trap = (2+1); + case 0x9A: { + // conditional select(csel) + int rm = (ir >> 16) & 0x1F; + switch((ir >> 12) & 0xF) { + case 0x0: if(Z) rval = regs[rm]; break; // eq + case 0x1: if(!Z) rval = regs[rm]; break; // ne + case 0x2: if(C) rval = regs[rm]; break; // cs + case 0x3: if(!C) rval = regs[rm]; break; + case 0x4: if(N) rval = regs[rm]; break; // mi + case 0x5: if(!N) rval = regs[rm]; break; // pl + case 0x6: if(V) rval = regs[rm]; break; // vs + case 0x7: if(!V) rval = regs[rm]; break; // vc + case 0x8: if(C && !Z) rval = regs[rm]; break; // hi + case 0x9: if(!C || Z) rval = regs[rm]; break; // ls + case 0xA: if(N == V) rval = regs[rm]; break; // ge + case 0xB: if(N != V) rval = regs[rm]; break; // lt + case 0xC: if(!Z && N == V) rval = regs[rm]; break; // gt + case 0xD: if(Z || N != V) rval = regs[rm]; break; // le + case 0xE: rval = regs[rm]; break; // always executed(al) + default: rval = regs[(ir >> 5) & 0x1F]; + } + break; + } + case 0x9B: { // mul + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + rval = regs[rn] * regs[rm]; + break; + } + case 0x9E: { + // scvtf + is_f = true; + int rs1 = (ir >> 5) & 0x1F; + frval = (double)regs[rs1]; + break; + } + + case 0xAA: { + // MOV(register) + int rm = (ir >> 16) & 0x1F; + rval = regs[rm]; + break; + } + + case 0xB8: { + // LDR/STR (register + index) 32 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OP_STORE_R_32) { + st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); } + // store(expected to store the length here) + rdid = -1; break; } - - case 0xBD: { - // LDR/STR(immediate) 32 bits floats - int base = ir >> 5 & 0x1F; - int imm = (ir >> 10) & 0xFFF; - - imm *= 4; - is_f = true; - rval = regs[base] + imm; - int opc = (ir >> 22) & 0x3; - // full opcode - opcode1 = (ir >> 22) & 0x3FF; - switch(opc) { - case 0x0: { - // store - if(opcode1 == arm.OPF_STORE_IMM_32) { - st4((int)rval, (int)regs[rdid]); - } else trap = (2+1); - - rdid = -1; - break; - } - case 0x1: { - // LDR (immediate) - if(opcode1 == arm.OPF_LOAD_IMM_32) { - frval = ld4s((int)rval); - } else trap = (2+1); - break; - } - default: trap = (2+1); + case 0x3: { + // load + if( opcode1 == arm.OP_LOAD_R_32) { + rval = ld4s((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); } break; } - case 0xFD: { - // LDR/STR(immediate) 64 bits floats - int base = ir >> 5 & 0x1F; - int imm = (ir >> 10) & 0xFFF; - - imm *= 4; - is_f = true; - rval = regs[base] + imm; - int opc = (ir >> 22) & 0x3; - // full opcode - opcode1 = (ir >> 22) & 0x3FF; - switch(opc) { - case 0x0: { - // store - if(opcode1 == arm.OPF_STORE_IMM_64) { - st8((int)rval, regs[rdid]); - } else trap = (2+1); - - rdid = -1; - break; - } - case 0x1: { - // LDR (immediate) - if(opcode1 == arm.OPF_LOAD_IMM_64) { - frval = ld8((int)rval); - } else trap = (2+1); - break; - } - default: trap = (2+1); - } + default: trap = (2+1); + } + + break; + } + + case 0xB9: { + // str/ldr(immediate) 32 bits + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + imm *= 4; + + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_32) { + st4((int)rval, (int)regs[rdid]); + } else trap = (2+1); + + rdid = -1; break; } - case 0x9b: { - // mul - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - rval = regs[rn] * regs[rm]; + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_32) { + rval = ld4s((int) rval); + } else trap = (2+1); break; } - case 0x54: { - // b.cond - int imm19 = ir << 8 >> 13; - imm19 = pc + (imm19 * 4) - 4; - rdid = -1; + default: trap = (2+1); + } + break; + } - switch(ir & 0xF) { - case 0x0: if(Z) pc = imm19; break; // eq - case 0x1: if(!Z) pc = imm19; break; // ne - case 0x2: if(C) pc = imm19; break; // cs - case 0x3: if(!C) pc = imm19; break; - case 0x4: if(N) pc = imm19; break; // mi - case 0x5: if(!N) pc = imm19; break; // pl - case 0x6: if(V) pc = imm19; break; // vs - case 0x7: if(!V) pc = imm19; break; // vc - case 0x8: if(C && !Z) pc = imm19; break; // hi - case 0x9: if(!C || Z) pc = imm19; break; // ls - case 0xA: if(N == V) pc = imm19; break; // ge - case 0xB: if(N != V) pc = imm19; break; // lt - case 0xC: if(!Z && N == V) pc = imm19; break; // gt - case 0xD: if(Z || N != V) pc = imm19; break; // le - case 0xE: pc = imm19; break; // always executed(al) - default: trap = (2+1); + case 0xBC: { + // LDR/STR (register + index) float 32 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + is_f = true; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OPF_STORE_R_32) { + st4((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); } + // store(expected to store the length here) + + rdid = -1; break; } - case 0x9a: { - // conditional select(csel) - int rm = (ir >> 16) & 0x1F; - switch((ir >> 12) & 0xF) { - case 0x0: if(Z) rval = regs[rm]; break; // eq - case 0x1: if(!Z) rval = regs[rm]; break; // ne - case 0x2: if(C) rval = regs[rm]; break; // cs - case 0x3: if(!C) rval = regs[rm]; break; - case 0x4: if(N) rval = regs[rm]; break; // mi - case 0x5: if(!N) rval = regs[rm]; break; // pl - case 0x6: if(V) rval = regs[rm]; break; // vs - case 0x7: if(!V) rval = regs[rm]; break; // vc - case 0x8: if(C && !Z) rval = regs[rm]; break; // hi - case 0x9: if(!C || Z) rval = regs[rm]; break; // ls - case 0xA: if(N == V) rval = regs[rm]; break; // ge - case 0xB: if(N != V) rval = regs[rm]; break; // lt - case 0xC: if(!Z && N == V) rval = regs[rm]; break; // gt - case 0xD: if(Z || N != V) rval = regs[rm]; break; // le - case 0xE: rval = regs[rm]; break; // always executed(al) - default: { - rval = regs[(ir >> 5) & 0x1F]; - } + case 0x3: { + // load + if(opcode1 == arm.OPF_LOAD_R_32) { + frval = ld4f((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); } break; } - case 0xeb: { - // subs(shifted register) - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - long lhs = regs[rn]; - long rhs = regs[rm]; - rval = lhs - rhs; - - // set flags as a side effect - Z = (rval == 0); - N = (rval < 0); - C = lhs >= rhs; + default: trap = (2+1); + } + + break; + } + + case 0xBD: { + // LDR/STR(immediate) 32 bits floats + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + + imm *= 4; + is_f = true; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OPF_STORE_IMM_32) { + st4((int)rval, (int)regs[rdid]); + } else trap = (2+1); - boolean sign_lhs = (lhs >>> 63) != 0; - boolean sign_rhs = (rhs >>> 63) != 0; - boolean sign_res = (rval >>> 63) != 0; - V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs); rdid = -1; break; } - case 0xF1: { - // subs(immediate) - int rn = (ir >> 5) & 0x1F; - int immediate = ir << 10 >> 20; - long lhs = regs[rn]; + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OPF_LOAD_IMM_32) { + frval = ld4s((int)rval); + } else trap = (2+1); + break; + } + default: trap = (2+1); + } + break; + } + + case 0xD2: { + // movz + // get immediate + rval = (ir >> 5) & 0x7FFF; + break; + } + + case 0xEB: { + // subs(shifted register) + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + long lhs = regs[rn]; + long rhs = regs[rm]; + rval = lhs - rhs; + + // set flags as a side effect + Z = (rval == 0); + N = (rval < 0); + C = lhs >= rhs; + + boolean sign_lhs = (lhs >>> 63) != 0; + boolean sign_rhs = (rhs >>> 63) != 0; + boolean sign_res = (rval >>> 63) != 0; + V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs); + rdid = -1; + break; + } + + case 0xD3: { + // lsl(immediate) + int imm = (ir >> 16) & 0x3F; + int rn = (ir >> 5) & 0x1F; + rval = regs[rn] << (64 - imm); + break; + } + case 0xD6: { + rdid = -1; + // Return to zero breaks simulation + if(regs[30] == 0) + break outer; + throw Utils.TODO(); + } - rval = lhs - immediate; + case 0xF1: { + // subs(immediate) + int rn = (ir >> 5) & 0x1F; + int immediate = ir << 10 >> 20; + long lhs = regs[rn]; - // set flags as a side effect - Z = (rval == 0); - N = (rval < 0); - C = lhs >= immediate; + rval = lhs - immediate; + + // set flags as a side effect + Z = (rval == 0); + N = (rval < 0); + C = lhs >= immediate; + + boolean sign_lhs = (lhs >>> 63) != 0; + boolean sign_rhs = (immediate >>> 63) != 0; + boolean sign_res = (rval >>> 63) != 0; + V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs); + rdid = -1; + break; + } + case 0xF8: { + // LDR/STR (register + index) 64 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OP_STORE_R_64) { + st8((int)regs[rn] + (int)regs[rm], regs[rdid]); + }else { + trap = (2+1); + } + // store(expected to store the length here) - boolean sign_lhs = (lhs >>> 63) != 0; - boolean sign_rhs = (immediate >>> 63) != 0; - boolean sign_res = (rval >>> 63) != 0; - V = (sign_lhs != sign_rhs) && (sign_res != sign_lhs); rdid = -1; break; } - case 0x8B: { - // add(shifted register) - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - long lhs = regs[rn]; - long rhs = regs[rm]; - rval = lhs + rhs; + case 0x3: { + // load + if(opcode1 == arm.OP_LOAD_R_64) { + rval = ld8((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); + } break; } - case 0x91: { - // add(immediate) - int rn = (ir >> 5) & 0x1F; - int immediate = ir << 10 >> 20; - // only hit it twice - rval = regs[rn] + immediate; - break; + default: trap = (2+1); } - case 0xD3: { - // lsl(immediate) - int imm = (ir >> 16) & 0x3F; - int rn = (ir >> 5) & 0x1F; - rval = regs[rn] << (64 - imm); + + break; + } + case 0xF9: { + // str/ldr(immediate) 64 bits + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + + imm *= 8; + + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OP_STORE_IMM_64) { + st8((int)rval, regs[rdid]); + } else trap = (2+1); + + rdid = -1; break; } - case 0x93: { - // asr(immediate) - int imm = (ir >> 16) & 0x3F; - int rn = (ir >> 5) & 0x1F; - rval = regs[rn] >> (imm); + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OP_LOAD_IMM_64) { + rval = ld8((int)rval); + } else trap = (2+1); break; } - case 0x92: { - // and(immediate) - int imm12 = (ir >> 10) & 0x1FFF; - long immediate = arm.decodeImm12(imm12); - int rn = (ir >> 5) & 0x1F; - rval = regs[rn] & immediate; - break; + default: trap = (2+1); } - case 0xAA: { - // MOV(register) - int rm = (ir >> 16) & 0x1F; - rval = regs[rm]; + break; + } + + case 0xFC: { + // LDR/STR (register + index) float 64 bits + int opc = ir >> 21 & 0x7; + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + is_f = true; + // full opcode + opcode1 = (ir >> 21) & 0x7FF; + switch(opc) { + case 0x1: { + if(opcode1 == arm.OPF_STORE_R_64) { + st8((int)regs[rn] + (int)regs[rm], (int)regs[rdid]); + }else { + trap = (2+1); + } + // store(expected to store the length here) + + rdid = -1; break; } - case 0xD6: { - rdid = -1; - if(ld4s(pc + 4) == 0) { - break outer; + case 0x3: { + // load + if(opcode1 == arm.OPF_LOAD_R_64) { + frval = ld8f((int) (regs[rn] + regs[rm])); + } else { + trap = (2+1); } - } - // bitwise - case 0x8A: { - // AND - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - rval = regs[rn] & regs[rm]; break; } - // floats - case 0x1E: { - is_f = true; - int rs1 = (ir >> 5) & 0x1F; - int rs2 = (ir >> 16) & 0x1F; - - int op = (ir >> 10) & 0x3F; - switch(op) { - case 16: frval = fregs[rs1]; break; // fmov - case 10:frval = fregs[rs1] + fregs[rs2]; break; // fadd - - case 6: if (fregs[rs2] == 0) frval = 0; else frval = fregs[rs1] / fregs[rs2]; break; // fdiv - case 8: { - // fcmp - double lhs = fregs[rs1]; - double rhs = fregs[rs2]; - - Z = (lhs == rhs); - N = (lhs < rhs); - C = lhs >= rhs; - V = Double.isNaN(lhs) || Double.isNaN(rhs); - rdid = -1; - - break; - } - case 2: frval = fregs[rs1] * fregs[rs2]; break; // fmul - case 14: frval = fregs[rs1] - fregs[rs2]; break; // fsub - default: trap = (2+1); - } + default: trap = (2+1); + } + + break; + } + case 0xFD: { + // LDR/STR(immediate) 64 bits floats + int base = ir >> 5 & 0x1F; + int imm = (ir >> 10) & 0xFFF; + + imm *= 4; + is_f = true; + rval = regs[base] + imm; + int opc = (ir >> 22) & 0x3; + // full opcode + opcode1 = (ir >> 22) & 0x3FF; + switch(opc) { + case 0x0: { + // store + if(opcode1 == arm.OPF_STORE_IMM_64) { + st8((int)rval, regs[rdid]); + } else trap = (2+1); + + rdid = -1; break; } - case 0x9e: { - // scvtf - is_f = true; - int rs1 = (ir >> 5) & 0x1F; - frval = (double)regs[rs1]; + case 0x1: { + // LDR (immediate) + if(opcode1 == arm.OPF_LOAD_IMM_64) { + frval = ld8((int)rval); + } else trap = (2+1); break; } default: trap = (2+1); + } + break; } + + default: trap = (2+1); + } + if(trap != 0) { _pc = pc; break; diff --git a/src/test/java/com/seaofnodes/simple/EvalRisc5.java b/src/test/java/com/seaofnodes/simple/EvalRisc5.java index fd953342..0a1c034f 100644 --- a/src/test/java/com/seaofnodes/simple/EvalRisc5.java +++ b/src/test/java/com/seaofnodes/simple/EvalRisc5.java @@ -10,10 +10,10 @@ import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.node.cpus.riscv.riscv; - +import java.io.ByteArrayOutputStream; import java.util.Arrays; - public class EvalRisc5 { +public class EvalRisc5 { // Memory image, always 0-based public final byte[] _buf; @@ -28,6 +28,9 @@ import java.util.Arrays; // Start of free memory for allocation int _heap; + // Standard out and err streams + ByteArrayOutputStream _stdout, _stderr; + // Cycle counters int _cycle; @@ -100,6 +103,17 @@ import java.util.Arrays; if( (reladdy & 0x00100000)!=0 ) reladdy |= 0xffe00000; // Sign extension. rval = pc + 4; pc = pc + reladdy - 4; + if( pc+4 == Encoding.SENTINEL_WRITE ) { + ByteArrayOutputStream baos = switch((int)regs[10]) { + case 1 -> _stdout==null ? (_stdout = new ByteArrayOutputStream()) : _stdout; + case 2 -> _stderr==null ? (_stderr = new ByteArrayOutputStream()) : _stderr; + default -> throw new IllegalArgumentException(); + }; + baos.write(_buf,(int)regs[11],(int)regs[12]); + regs[10] = regs[12]; + rdid = 0; + pc = (int)(rval - 4); // Unwind PC, as-if returned from write + } break; } case 0x67: { // JALR (0b1100111) @@ -112,11 +126,11 @@ import java.util.Arrays; break outer; } // Inline CALLOC effect - if( pc == Encoding.SENTINAL_CALLOC ) { + if( pc == Encoding.SENTINEL_CALLOC ) { + assert (_heap&7) == 0; // 8-byte aligned int size = (int)(regs[10]*regs[11]); + size = (size+7) & -8; // 8-byte aligned regs[10] = _heap; // Return next free address - // Pre-zeroed; epsilon (null) collector, never recycles memory so always zero - // Arrays.fill(_buf,_heap,_heap+size, (byte) 0 ); _heap += size; pc = (int)(rval - 4); // Unwind PC, as-if returned from calloc } else @@ -233,11 +247,11 @@ import java.util.Arrays; } else { rval = switch( (ir >> 12) & 7 ) { // These could be either op-immediate or op commands. Be careful. case 0 -> (is_reg && (ir & 0x40000000) != 0) ? (rs1 - rs2) : (rs1 + rs2); - case 1 -> rs1 << (rs2 & 0x1F); + case 1 -> rs1 << (rs2 & 0x3F); case 2 -> (rs1 < rs2) ? 1 : 0; case 3 -> (rs1 < rs2) ? 1 : 0; case 4 -> rs1 ^ rs2; - case 5 -> (ir & 0x40000000) != 0 ? (((int) rs1) >> (rs2 & 0x1F)) : (rs1 >> (rs2 & 0x1F)); + case 5 -> (ir & 0x40000000) != 0 ? (rs1 >>> (rs2 & 0x3F)) : (rs1 >> (rs2 & 0x3F)); case 6 -> rs1 | rs2; case 7 -> rs1 & rs2; default -> rval; diff --git a/src/test/java/com/seaofnodes/simple/Simple.java b/src/test/java/com/seaofnodes/simple/Simple.java index 9b1e2827..1465aa6d 100644 --- a/src/test/java/com/seaofnodes/simple/Simple.java +++ b/src/test/java/com/seaofnodes/simple/Simple.java @@ -17,7 +17,7 @@ public class Simple { static final int DUMP_AFTER_OPTO = 1<<1; static final int DUMP_AFTER_TYPE_CHECK = 1<<2; static final int DUMP_AFTER_LOOP_TREE = 1<<3; - static final int DUMP_AFTER_INSTR_SELECT = 1<<4; + static final int DUMP_AFTER_SELECT = 1<<4; static final int DUMP_AFTER_GCM = 1<<5; static final int DUMP_AFTER_LOCAL_SCHED = 1<<6; static final int DUMP_AFTER_REG_ALLOC = 1<<7; @@ -32,33 +32,30 @@ public class Simple { static final int DUMP_DISASSEMBLE = 1<<31; - static String system_cpu = null; - static String system_abi = null; - static void usage() { System.out.println( """ -simple [options] [--eval|--run ...] +simple [options] ...args... Options: - --dump - dump final intermediate representation - --dump-after-parse - dump intermediate representation after parse - --dump-after-opto - dump intermediate representation after opto pass - --dump-after-type-check - dump intermediate representation after type check pass - --dump-after-instr-select - dump intermediate representation after instrution selection pass - --dump-after-gcm - dump intermediate representation after GCM pass - --dump-after-local-sched - dump intermediate representation after local scheduling pass - --dump-after-reg-alloc - dump intermediate representation after register allocation pass - --dump-after-encode - dump intermediate representation after encoding pass - --dump-after-all - dump intermediate representation after all passes - --dot - dump grapical representation of intermediate code into *.dot file(s) - -S - dump generated assembler code - --eval ... - evaluate the compiled code in emulator - --run ... - run the compiled code natively - --dump-size - print the size of generated code - --dump-time - print compilation and execution times - --cpu - use specific CPU (x86_64_v2, riscv, arm) - --abi - use speific ABI variant (SystemV) - --target - print native CPU and ABI + --dump - dump final intermediate representation + --dump-after-parse - dump intermediate representation after parse + --dump-after-opto - dump intermediate representation after opto pass + --dump-after-type-check - dump intermediate representation after type check pass + --dump-after-select - dump intermediate representation after instrution selection pass + --dump-after-gcm - dump intermediate representation after GCM pass + --dump-after-local-sched - dump intermediate representation after local scheduling pass + --dump-after-reg-alloc - dump intermediate representation after register allocation pass + --dump-after-encode - dump intermediate representation after encoding pass + --dump-after-all - dump intermediate representation after all passes + --dot - dump grapical representation of intermediate code into *.dot file(s) + -S - dump generated assembler code + --eval - evaluate the compiled code in emulator + --run - run the compiled code natively; this is the default + --dump-size - print the size of generated code + --dump-time - print compilation and execution times + --cpu - use specific CPU (x86_64_v2, riscv, arm) + --abi - use speific ABI variant (SystemV) + --target - print native CPU and ABI --version --help """ @@ -66,46 +63,40 @@ Options: System.exit(0); } - static void bad_usage() { - System.err.println("ERROR: Invalid usage (use --help)"); + static void bad_usage() { bad("Invalid usage (use --help)"); } + static RuntimeException bad(String err) { + System.err.println("ERROR: "+err); System.exit(1); + return new RuntimeException(err); } static void target() { - if (system_cpu == null || system_abi == null) { - System.err.println("ERROR: Unknown target"); - System.exit(1); - } - System.out.print(system_cpu); + System.out.print(TestC.CPU_PORT); System.out.print("-"); - System.out.println(system_abi); - System.exit(0); + System.out.println(TestC.CALL_CONVENTION); } static void dump(CodeGen code, int dump, int pass) { if ((dump & pass) != 0) { if ((dump & DUMP_DOT) != 0) { String fn = switch (pass) { - case DUMP_AFTER_PARSE -> "01-parse.dot"; - case DUMP_AFTER_OPTO -> "02-opto.dot"; - case DUMP_AFTER_TYPE_CHECK -> "03-type_check.dot"; - case DUMP_AFTER_LOOP_TREE -> "04-loop_tree.dot"; - case DUMP_AFTER_INSTR_SELECT -> "05-instr_select.dot"; - case DUMP_AFTER_GCM -> "06-gcm.dot"; - case DUMP_AFTER_LOCAL_SCHED -> "07-local_sched.dot"; - case DUMP_AFTER_REG_ALLOC -> "08-reg_allos.dot"; - case DUMP_AFTER_ENCODE -> "09-local_sched.dot"; - case DUMP_FINAL -> "10-final.dot"; - default -> throw Utils.TODO(); + case DUMP_AFTER_PARSE -> "01-parse.dot"; + case DUMP_AFTER_OPTO -> "02-opto.dot"; + case DUMP_AFTER_TYPE_CHECK -> "03-type_check.dot"; + case DUMP_AFTER_LOOP_TREE -> "04-loop_tree.dot"; + case DUMP_AFTER_SELECT -> "05-instr_select.dot"; + case DUMP_AFTER_GCM -> "06-gcm.dot"; + case DUMP_AFTER_LOCAL_SCHED -> "07-local_sched.dot"; + case DUMP_AFTER_REG_ALLOC -> "08-reg_allos.dot"; + case DUMP_AFTER_ENCODE -> "09-local_sched.dot"; + case DUMP_FINAL -> "10-final.dot"; + default -> throw Utils.TODO(); }; try { Files.writeString(Path.of(fn), - new GraphVisualizer().generateDotOutput(code._stop, null, null)); - } catch(IOException e) { - System.err.println("ERROR: Cannot write DOT file"); - System.exit(1); - } + new GraphVisualizer().generateDotOutput(code._stop, null, null)); + } catch(IOException e) { throw bad("Cannot write DOT file"); } } else { if ((dump & DUMP_PASS_NAME) != 0) { System.err.println(switch (pass) { @@ -113,11 +104,11 @@ Options: case DUMP_AFTER_OPTO -> "After OPTO:"; case DUMP_AFTER_TYPE_CHECK -> "After Type Check:"; case DUMP_AFTER_LOOP_TREE -> "After Loop Tree:"; - case DUMP_AFTER_INSTR_SELECT -> "After Instruction Selection:"; + case DUMP_AFTER_SELECT -> "After Code Selection:"; case DUMP_AFTER_GCM -> "After GCM:"; case DUMP_AFTER_LOCAL_SCHED -> "After Local Scheduling:"; case DUMP_AFTER_REG_ALLOC -> "After Register Allocation:"; - case DUMP_AFTER_ENCODE -> "After Instruction Encoding:"; + case DUMP_AFTER_ENCODE -> "After Encoding:"; case DUMP_FINAL -> "Final:"; default -> throw Utils.TODO(); }); @@ -132,24 +123,24 @@ Options: double t, total = 0; total += t = code._tParse / 1e3; - System.out.println(String.format("Parsing Time: %.3f sec", t)); + System.out.printf( "Parsing Time: %.3f sec%n", t); total += t = code._tOpto / 1e3; - System.out.println(String.format("Optimization Time: %.3f sec", t)); + System.out.printf( "Optimization Time: %.3f sec%n", t); total += t = code._tTypeCheck / 1e3; - System.out.println(String.format("Type Checking Time: %.3f sec", t)); + System.out.printf( "Type Checking Time: %.3f sec%n", t); total += t = code._tLoopTree / 1e3; - System.out.println(String.format("Loop Tree Time: %.3f sec", t)); + System.out.printf( "Loop Tree Time: %.3f sec%n", t); total += t = code._tInsSel / 1e3; - System.out.println(String.format("Instruction Selection Time: %.3f sec", t)); + System.out.printf( "Code Selection Time: %.3f sec%n", t); total += t = code._tGCM / 1e3; - System.out.println(String.format("GCM Time: %.3f sec", t)); + System.out.printf( "GCM Time: %.3f sec%n", t); total += t = code._tLocal / 1e3; - System.out.println(String.format("Local Scheduling Time: %.3f sec", t)); + System.out.printf( "Local Scheduling Time: %.3f sec%n", t); total += t = code._tRegAlloc / 1e3; - System.out.println(String.format("Register Allocation Time: %.3f sec", t)); + System.out.printf( "Register Allocation Time: %.3f sec%n", t); total += t = code._tEncode / 1e3; - System.out.println(String.format("Instruction Encoding Time: %.3f sec", t)); - System.out.println(String.format("TOTAL COMPILATION TIME: %.3f sec", total)); + System.out.printf( "Encoding Time: %.3f sec%n", t); + System.out.printf( "TOTAL COMPILATION TIME: %.3f sec%n", total); } static String getInput() { @@ -159,9 +150,8 @@ Options: public static void main(String[] args) throws Exception { String input_filename = null; - boolean use_stdin = false; boolean do_eval = false; - boolean do_run = false; + boolean do_run = true; boolean do_codegen = false; boolean do_print_size = false; boolean do_print_time = false; @@ -171,78 +161,62 @@ Options: String cpu = null; String abi = null; - // TODO: autodetect - system_cpu = "x86_64_v2"; - system_abi = "SystemV"; - // Parse command line -loop: for (int i = 0; i < args.length; i++) { + int i; for( i = 0; i < args.length; i++ ) { String arg = args[i]; - if (arg.charAt(0) == '-') { - switch (arg) { - case "-": use_stdin = true; break; - case "--dump": dump |= DUMP_FINAL; break; - case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break; - case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break; - case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break; - case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break; - case "--dump-after-instr-select": dump |= DUMP_AFTER_INSTR_SELECT; break; - case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break; - case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break; - case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break; - case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break; - case "--dump-after-all": dump |= DUMP_AFTER_ALL; break; - case "--dot": dump |= DUMP_DOT; break; - case "-S": dump |= DUMP_DISASSEMBLE; break; - case "--eval": do_eval = true; first_arg = i + 1; break loop; - case "--run": do_run = true; first_arg = i + 1; break loop; - case "--dump-size": do_print_size = true; break; - case "--dump-time": do_print_time = true; break; - case "--cpu": if (cpu != null - || i + 1 >= args.length - || args[i + 1].charAt(0) == '-') bad_usage(); - cpu = args[i + 1]; - i++; - break; - case "--abi": if (abi != null - || i + 1 >= args.length - || args[i + 1].charAt(0) == '-') bad_usage(); - abi = args[i + 1]; - i++; - break; - case "--target": target(); - case "--version": throw Utils.TODO(); - case "--help": usage(); - default: bad_usage(); - } - } else { - if (input_filename != null || use_stdin) bad_usage(); + if( arg.charAt(0) != '-' ) { input_filename = arg; + break; } - } - if (input_filename == null && !use_stdin) { - System.err.println("ERROR: no input file' (use --help)"); - System.exit(1); + switch (arg) { + case "-": input_filename=""; break; + case "--dump": dump |= DUMP_FINAL; break; + case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break; + case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break; + case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break; + case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break; + case "--dump-after-select": dump |= DUMP_AFTER_SELECT; break; + case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break; + case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break; + case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break; + case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break; + case "--dump-after-all": dump |= DUMP_AFTER_ALL; break; + case "--dot": dump |= DUMP_DOT; break; + case "-S": dump |= DUMP_DISASSEMBLE; break; + case "--eval": do_eval = true ; do_run = false; break; + case "--run": do_run = true ; break; + case "--norun": do_run = false; break; + case "--dump-size": do_print_size = true; break; + case "--dump-time": do_print_time = true; break; + case "--cpu": if (cpu != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage(); + cpu = args[++i]; + break; + case "--abi": if (abi != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage(); + abi = args[++i]; + break; + case "--target": target(); break; + case "--version": throw Utils.TODO(); + case "--help": usage(); + default: bad_usage(); + } } + if (input_filename == null) throw bad("no input file' (use --help)"); + if( !input_filename.isEmpty() && !input_filename.endsWith(".smp") ) + throw bad("File extension must be .smp"); + String base = input_filename.substring(0,input_filename.length()-4); + if (do_run || (dump & DUMP_DISASSEMBLE) != 0 || do_print_size) { - if (cpu == null) cpu = system_cpu; - if (abi == null) abi = system_abi; - if (cpu == null || abi == null) { - System.err.println("ERROR: Cannot compile for unknown target"); - System.exit(1); - } + if (cpu == null) cpu = TestC.CPU_PORT; + if (abi == null) abi = TestC.CALL_CONVENTION; do_codegen = true; } // Read input file try { - src = use_stdin ? getInput() : Files.readString(Path.of(input_filename)); - } catch(IOException e) { - System.err.println("ERROR: Cannot read input file"); - System.exit(1); - } + src = input_filename.isEmpty() ? getInput() : Files.readString(Path.of(input_filename)); + } catch( IOException e ) { throw bad("Cannot read input file: "+input_filename); } // Compilation pipeline CodeGen code = new CodeGen(src); @@ -260,8 +234,8 @@ loop: for (int i = 0; i < args.length; i++) { dump(code, dump, DUMP_AFTER_LOOP_TREE); if (do_codegen) { - code.instSelect(PORTS, cpu, abi); - dump(code, dump, DUMP_AFTER_INSTR_SELECT); + code.instSelect(cpu, abi, PORTS); + dump(code, dump, DUMP_AFTER_SELECT); } code.GCM(); @@ -276,6 +250,8 @@ loop: for (int i = 0; i < args.length; i++) { code.encode(); dump(code, dump, DUMP_AFTER_ENCODE); + + code.exportELF(base+".o"); } dump(code, dump, DUMP_FINAL); @@ -285,7 +261,7 @@ loop: for (int i = 0; i < args.length; i++) { } if (do_print_size) { - System.out.println(String.format("Code Size: %d", code._encoding._bits.size())); + System.out.printf( "Code Size: %d%n", code._encoding._bits.size()); } if (do_print_time) { @@ -295,19 +271,19 @@ loop: for (int i = 0; i < args.length; i++) { if (do_eval) { // TODO: Support for evaluation of functions with different argument numbers and types long t = System.currentTimeMillis(); - long arg = (first_arg < args.length) ? Integer.valueOf(args[first_arg]) : 0; + long arg = (i+1 < args.length) ? Integer.parseInt(args[i+1]) : 0; System.out.println(Eval2.eval(code, arg, 100000)); if (do_print_time) { - System.out.println(String.format("EXECUTION TIME: %.3f sec", - (System.currentTimeMillis() - t) / 1e3)); - } - } else if (do_run) { - if (cpu != system_cpu || abi != system_abi) { - System.err.println("ERROR: cannot run code on not native target"); - System.exit(1); + System.out.printf( "EXECUTION TIME: %.3f sec%n", + (System.currentTimeMillis() - t) / 1e3); } - - throw Utils.TODO(); + } + if (do_run) { + if ( !TestC.CPU_PORT.equals( cpu ) || !TestC.CALL_CONVENTION.equals( abi ) ) + throw bad("cannot run code on not native target"); + String exe = TestC.OS.startsWith("Windows") ? base+".exe" : base; + String result = com.seaofnodes.simple.TestC.gcc(base+".o", null, null, true, exe); + System.out.print(result); } } } diff --git a/src/test/java/com/seaofnodes/simple/TestC.java b/src/test/java/com/seaofnodes/simple/TestC.java index 5609758b..93fa9b66 100644 --- a/src/test/java/com/seaofnodes/simple/TestC.java +++ b/src/test/java/com/seaofnodes/simple/TestC.java @@ -1,10 +1,10 @@ package com.seaofnodes.simple; import com.seaofnodes.simple.codegen.CodeGen; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.io.InputStream; -import java.io.IOException; +import java.util.concurrent.TimeUnit; import static org.junit.Assert.*; // Call a C driver program, which calls a Simple-generated .o, and self-checks. @@ -16,8 +16,8 @@ public abstract class TestC { public static final String CALL_CONVENTION = OS.startsWith("Windows") ? "Win64" : "SystemV"; public static final String CPU_PORT = switch( CPU ) { - case "amd64" -> "x86_64_v2"; - default -> throw Utils.TODO("Map Yer CPU Port Here"); + case "amd64" -> "x86_64_v2"; + default -> throw Utils.TODO("Map Yer CPU Port Here"); }; public static void run( String file, int spills ) throws IOException { run(file,"",spills); } @@ -35,48 +35,16 @@ public abstract class TestC { // Compile and export Simple String src = Files.readString(Path.of(sfile)); - _run(src,CALL_CONVENTION,"",cfile,efile,"S",expected,spills); + run(src,CALL_CONVENTION,"",cfile,efile,"S",expected,spills); } - static void _run( String src, String simple_conv, String c_conv, String cfile, String efile, String xtn, String expected, int spills ) throws IOException { + public static void run( String src, String simple_conv, String c_conv, String cfile, String efile, String xtn, String expected, int spills ) throws IOException { String bin = efile+xtn; String obj = bin+".o"; + String exe = OS.startsWith("Windows") ? bin+".exe" : bin; // Compile simple, emit ELF CodeGen code = new CodeGen(src).driver( CPU_PORT, simple_conv, obj); - - // Compile the C program - var params = new String[] { - //if (USE_WSL) "wsl.exe"; - "gcc", - cfile, - obj, - "-lm", // Picks up 'sqrt' for newtonFloat tests to compare - "-g", - "-o", - bin, - "-D", - "CALL_CONV="+c_conv, - }; - Process gcc = new ProcessBuilder(params).redirectErrorStream(true).start(); - byte error; - try { error = (byte)gcc.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); } - String result = new String(gcc.getInputStream().readAllBytes()); - if( error!=0 ) { - System.err.println("gcc error code: "+error); - System.err.println(result); - } - assertEquals( 0, error ); - //assertTrue(result.isEmpty()); // No data in error stream - - // Execute results - Process smp = new ProcessBuilder(bin).redirectErrorStream(true).start(); - try { error = (byte)smp.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); } - result = new String(smp.getInputStream().readAllBytes()); - if( error!=0 ) { - System.err.println("exec error code: "+error); - System.err.println(result); - } - assertEquals( 0, error ); + String result = gcc(obj, c_conv, cfile, false, exe ); assertEquals(expected,result); // Allocation quality not degraded @@ -84,6 +52,55 @@ public abstract class TestC { if( delta==0 ) delta = 1; if( spills != -1 ) assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + } + + public static String gcc( String obj, String c_conv, String cfile, boolean stdin, String... args ) throws IOException { + + // Compile the C program. Compiling code and constants in the low + // 2Gig. Pointers are 64b BUT since always in the low 2G all the + // high bits are zero - and Simple code can be emitted treating + // pointers as 4bytes. + var params = new Ary<>(String.class); + params.add("gcc"); + if( cfile!=null ) params.add(cfile); // Associated C driver, usually has a `main` + params.addAll(new String[] { + obj, + "-lm", // Picks up 'sqrt' for newtonFloat tests to compare + "-g", + "-o", + args[0], + }); + // Calling convention for C calls, if any + if( cfile!=null ) { + params.add("-D"); + params.add("CALL_CONV="+c_conv); + } + // Run GCC to link (optionally compile C driver code) + Process gcc = new ProcessBuilder(params.asAry()).redirectErrorStream(true).start(); + int exit; + try { + boolean normal = gcc.waitFor(2, TimeUnit.SECONDS); + exit = normal ? gcc.exitValue() : -1; // no exit??? + } catch( InterruptedException e ) { + throw new IOException("interrupted"); + } + String result = new String(gcc.getInputStream().readAllBytes()); + if( exit!=0 ) { + System.err.println("gcc error code: "+exit); + System.err.println(result); + } + assertEquals( 0, exit ); + //assertTrue(result.isEmpty()); // No data in error stream + + // Execute results + ProcessBuilder smp = new ProcessBuilder(args); + if( stdin ) smp.redirectInput(ProcessBuilder.Redirect.INHERIT); + Process p = smp.start(); + try { exit = (byte)p.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); } + result = new String(p.getInputStream().readAllBytes()); + if( exit!=0 ) + System.err.println("exec exit code: "+exit); + return result; } } diff --git a/src/test/java/com/seaofnodes/simple/TypeTest.java b/src/test/java/com/seaofnodes/simple/TypeTest.java index d94f5b5a..660f984a 100644 --- a/src/test/java/com/seaofnodes/simple/TypeTest.java +++ b/src/test/java/com/seaofnodes/simple/TypeTest.java @@ -20,9 +20,9 @@ public class TypeTest { TypeStruct s2 = TypeStruct.make("s2", new Field[]{ Field.make("a", TypeInteger.BOT,-3, false), Field.make("b", TypeInteger.BOT,-4, false) }); - Assert.assertEquals(s1.makeRO(), s1.glb()); + Assert.assertEquals(s1.makeRO(), s1.glb(false)); Assert.assertNotEquals(s1, s1.dual()); - Assert.assertEquals(s1.makeRO(), s1.dual().glb()); + Assert.assertEquals(s1.makeRO(), s1.dual().glb(false)); TypeMem m1 = TypeMem.make(1,TypeNil.NIL); TypeMem m2 = TypeMem.make(2,TypeInteger.U16); @@ -38,9 +38,9 @@ public class TypeTest { Assert.assertEquals(TypeMem.make(0,Type.BOTTOM), m1.meet(m3)); Assert.assertEquals(TypeMem .BOT, m3.meet(m4)); - Assert.assertEquals(TypeMem.make(1,Type.BOTTOM), m1.glb()); + Assert.assertEquals(TypeMem.make(1,Type.BOTTOM), m1.glb(false)); Assert.assertEquals(TypeMem.make(1,Type.XNIL), m1.dual()); - Assert.assertEquals(m4.dual(), m4.glb().dual()); + Assert.assertEquals(m4.dual(), m4.glb(false).dual()); TypeMemPtr ptr1 = TypeMemPtr.make(s1); Assert.assertEquals(s1, ptr1._obj); @@ -55,13 +55,13 @@ public class TypeTest { Assert.assertEquals(s2, ptr2nil._obj); Assert.assertNotEquals(ptr1, ptr2); - Assert.assertNotEquals(ptr1, ptr1.glb()); - Assert.assertEquals(ptr1nil.makeRO(), ptr1.glb()); + Assert.assertNotEquals(ptr1, ptr1.glb(false)); + Assert.assertEquals(ptr1nil.makeRO(), ptr1.glb(false)); Assert.assertEquals(ptr1, ptr1.dual().dual()); - Assert.assertEquals(ptr1.glb().makeRO(), ptr1.dual().glb()); + Assert.assertEquals(ptr1.glb(false).makeRO(), ptr1.dual().glb(false)); Assert.assertEquals(TypeMemPtr.makeNullable(TypeStruct.BOT), ptr1.meet(ptr2nil)); - Assert.assertEquals(ptr1.glb(), ptr1.meet(TypeNil.NIL).makeRO()); + Assert.assertEquals(ptr1.glb(false), ptr1.meet(TypeNil.NIL).makeRO()); TypeMemPtr TOP = TypeMemPtr.TOP; TypeMemPtr BOT = TypeMemPtr.makeNullable(TypeStruct.BOT); diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java index f119fa1a..4f86d0be 100644 --- a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java +++ b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java @@ -97,7 +97,7 @@ public class ScriptGenerator { private static final List INTTYPES = new ArrayList<>(); static { - for( var e : Parser.defaultTypes().entrySet() ) { + for( var e : Parser.TYPES.entrySet() ) { if( e.getValue() instanceof TypeInteger ) { INTTYPES.add(new TypeInt(e.getKey())); } diff --git a/src/test/java/com/seaofnodes/simple/progs/echo.smp b/src/test/java/com/seaofnodes/simple/progs/echo.smp new file mode 100644 index 00000000..8eafd73b --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/echo.smp @@ -0,0 +1,3 @@ +// -*- mode: java; -*- +// Echo stdin to stdout. +return sys.io.p( sys.io.stdin() ); diff --git a/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp b/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp new file mode 100644 index 00000000..07ff935f --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/helloWorld.smp @@ -0,0 +1,3 @@ +// -*- mode: java; -*- +sys.io.p("Hello, World!"); +return 0; diff --git a/src/test/java/com/seaofnodes/simple/progs/jig.smp b/src/test/java/com/seaofnodes/simple/progs/jig.smp index 1daeee09..fd3e7b9c 100644 --- a/src/test/java/com/seaofnodes/simple/progs/jig.smp +++ b/src/test/java/com/seaofnodes/simple/progs/jig.smp @@ -1,2 +1,6 @@ // -*- mode: java; -*- +// Should fold away sign extend +struct Person { i8 age;}; +Person !p = new Person; +p.age = (arg<<48)>>48; return 0; diff --git a/src/test/java/com/seaofnodes/simple/progs/person.smp b/src/test/java/com/seaofnodes/simple/progs/person.smp index 9f0f5bea..507fb83b 100644 --- a/src/test/java/com/seaofnodes/simple/progs/person.smp +++ b/src/test/java/com/seaofnodes/simple/progs/person.smp @@ -1,6 +1,6 @@ // -*-java-*- struct Person { - int age; + i32 age; }; val fcn = { Person?[] ps, int x -> diff --git a/src/test/java/com/seaofnodes/simple/progs/riscv_load.smp b/src/test/java/com/seaofnodes/simple/progs/riscv_load.smp new file mode 100644 index 00000000..67369feb --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/riscv_load.smp @@ -0,0 +1,4 @@ +struct Person { i32 age;}; +Person !p = new Person; +p.age += arg; +return p.age; diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp new file mode 100644 index 00000000..cfb3fbcc --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_1.smp @@ -0,0 +1,4 @@ +struct Person {i64 age;} +Person !p = new Person; +p.age += arg; +return p.age; diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp new file mode 100644 index 00000000..0cf70392 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_fold_away.smp @@ -0,0 +1,5 @@ +// Should fold away sign extend +struct Person { i8 age;}; +Person !p = new Person; +p.age = (arg<<48)>>48; +return 0; \ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp new file mode 100644 index 00000000..48cc6409 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away.smp @@ -0,0 +1,4 @@ +struct Person { i32 age;}; +Person !p = new Person; +p.age = (arg<<17)>>17; +return 0; \ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp new file mode 100644 index 00000000..3120429b --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sext_str_not_fold_away_2.smp @@ -0,0 +1,4 @@ +struct Person { i32 age;}; +Person !p = new Person; +p.age = (arg<<48)>>48; +return 0; \ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/store_peep.smp b/src/test/java/com/seaofnodes/simple/progs/store_peep.smp new file mode 100644 index 00000000..c3feddd5 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/store_peep.smp @@ -0,0 +1,3 @@ +i64 a = 123456789012345; +i32 b = a; // truncate high order bits +return b; \ No newline at end of file -- cgit v1.2.3