diff options
| author | Cliff Click <cliffc@acm.org> | 2025-04-24 14:38:36 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2025-04-24 14:38:36 -0700 |
| commit | cadc530688041b5455e6251055754df6f4182aec (patch) | |
| tree | 1f83d6d9285adb0c10c9147da3e64700e8ea2820 /src/test/java/com | |
| parent | f6c0b12e6cc1af4879042c0426fc96532cdfddc3 (diff) | |
| download | Simple-cadc530688041b5455e6251055754df6f4182aec.tar.gz Simple-cadc530688041b5455e6251055754df6f4182aec.zip | |
Chapter 21: Instruction Encoding and ELFlinear-chapter21
Co-authored-by: Co-authored-by: Cliff Click <cliffc@acm.org>
Co-authored-by: Co-authored-by: godalming123 <68993177+godalming123@users.noreply.github.com>
Diffstat (limited to 'src/test/java/com')
49 files changed, 2537 insertions, 485 deletions
diff --git a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java index c23bb301..2733620e 100644 --- a/src/test/java/com/seaofnodes/simple/BrainFuckTest.java +++ b/src/test/java/com/seaofnodes/simple/BrainFuckTest.java @@ -1,71 +1,38 @@ package com.seaofnodes.simple; import com.seaofnodes.simple.codegen.CodeGen; -import java.nio.charset.StandardCharsets; +import com.seaofnodes.simple.node.cpus.arm.arm; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; -import static com.seaofnodes.simple.Main.PORTS; public class BrainFuckTest { - @Test - public void testBrainfuck() { - var program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.".getBytes(StandardCharsets.UTF_8); - var encoded = new StringBuilder("u8[] !program = new u8[").append(program.length).append("];"); - for (int i = 0; i < program.length; i++) { - int value = program[i] & 0xFF; - encoded.append("program[").append(i).append("] = ").append(value).append(";"); - } + @Test public void testBrainFuck() throws IOException { + String brain_fuck = "Hello World!\n"; + TestC.run("brain_fuck", brain_fuck, 40); - String src = encoded + """ + EvalRisc5 R5 = TestRisc5.build("brain_fuck", 0, 44, false); + int trap = R5.step(100000); + assertEquals(0,trap); + int ptr = (int)R5.regs[riscv.A0]; + assertEquals(brain_fuck.length(),R5.ld4s(ptr)); + for( int i=0; i<brain_fuck.length(); i++ ) + assertEquals(brain_fuck.charAt(i), R5.ld1z(ptr+4+i)); -int d = 0; -u8[] !output = new u8[0]; -u8[] !data = new u8[100]; + EvalArm64 A5 = TestArm64.build("brain_fuck", 0, 28, false); + int trap_arm = A5.step(100000); + assertEquals(0,trap_arm); + assertEquals(0,trap); + int ptr_arm = (int)A5.regs[arm.X0]; + assertEquals(brain_fuck.length(),A5.ld4s(ptr_arm)); -for( int pc = 0; pc < program#; pc++ ) { - var command = program[pc]; - if (command == 62) { - d++; - } else if (command == 60) { - d--; - } else if (command == 43) { - data[d]++; - } else if (command == 45) { - data[d]--; - } else if (command == 46) { - // Output a byte; increase the output array size - var old = output; - output = new u8[output# + 1]; - for( int i = 0; i < old#; i++ ) - output[i] = old[i]; - output[old#] = data[d]; // Add the extra byte on the end - } else if (command == 44) { - data[d] = 42; - } else if (command == 91) { - if (data[d] == 0) { - for( int d = 1; d > 0; ) { - command = program[++pc]; - if (command == 91) d++; - if (command == 93) d--; - } - } - } else if (command == 93) { - if (data[d]) { - for( int d = 1; d > 0; ) { - command = program[--pc]; - if (command == 93) d++; - if (command == 91) d--; - } - } - } -} -return output; -"""; - Chapter20Test.testCPU(src,"x86_64_v2", "SystemV",28,null); - Chapter20Test.testCPU(src,"riscv" , "SystemV",18,null); - Chapter20Test.testCPU(src,"arm" , "SystemV",12,null); - //assertEquals("Hello World!\n", Eval2.eval(code, 0, 10000)); + for( int i=0; i<brain_fuck.length(); i++ ) + assertEquals(brain_fuck.charAt(i), A5.ld1z(ptr+4+i)); } + } diff --git a/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/src/test/java/com/seaofnodes/simple/Chapter05Test.java index a584ad5a..d1dfd216 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter05Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter05Test.java @@ -100,7 +100,7 @@ else return a; """); code.parse().opto(); - assertEquals("return Phi(Region,Phi(Region,2,3),Phi(Region,4,5));", code.print()); + assertEquals("return Phi(Region,3,5,2,4);", code.print()); } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/src/test/java/com/seaofnodes/simple/Chapter06Test.java index 24d12921..b6bc2bf3 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter06Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter06Test.java @@ -145,7 +145,7 @@ else return a; """); code.parse().opto(); - assertEquals("return Phi(Region,3,Phi(Region,4,5));", code.print()); + assertEquals("return Phi(Region,3,5,4);", code.print()); } @Test @@ -258,7 +258,7 @@ if( arg ) { return a+b+c; """); code.parse().opto(); - assertEquals("return (Phi(Region,Phi(Region,2,3),0)+Phi(Region,3,1));", code.print()); + assertEquals("return Phi(Region,6,1,5);", code.print()); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/src/test/java/com/seaofnodes/simple/Chapter08Test.java index 7e90d484..09669cc7 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter08Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter08Test.java @@ -23,7 +23,7 @@ while(arg < 10) { return arg; """); code.parse().opto(); - assertEquals("return Phi(Region,Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add),Add);", code.print()); + assertEquals("return Phi(Region,(Phi(Loop,arg,Add)+1),Add,Phi_arg);", code.print()); assertTrue(code.ctrl() instanceof RegionNode); Assert.assertEquals( "5", Eval2.eval(code, 1)); Assert.assertEquals("10", Eval2.eval(code, 6)); @@ -46,7 +46,7 @@ while(arg < 10) { return a; """); code.parse().opto(); - assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1)));", code.print()); + assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1),Phi_a));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/src/test/java/com/seaofnodes/simple/Chapter11Test.java index c2b9a86d..f6539ac4 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter11Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter11Test.java @@ -289,7 +289,7 @@ else { } """); code.parse().opto().typeCheck().GCM(); - assertEquals("return Phi(Region,9,Top,Top,Top,Top);", code.print()); + assertEquals("return 9;", code.print()); } @@ -321,7 +321,7 @@ while( prime <= arg ) { return primeCount; """); code.parse().opto(); - assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi(Region,(Phi_primeCount+1),Phi_primeCount))));", code.print()); + assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi_primeCount,(Phi_primeCount+1))));", code.print()); assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below assertEquals("1", Eval2.eval(code, 2)); // 2 assertEquals("2", Eval2.eval(code, 3)); // 2, 3 diff --git a/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/src/test/java/com/seaofnodes/simple/Chapter13Test.java index 977595cb..6bf45a22 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter13Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter13Test.java @@ -33,7 +33,7 @@ while( arg ) { return head.next.i; """); try { code.parse().opto().typeCheck(); fail(); } - catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); } + catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); } } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/src/test/java/com/seaofnodes/simple/Chapter14Test.java index b83516e3..7af35b56 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter14Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter14Test.java @@ -174,7 +174,7 @@ while(v1) break; return v1; """); code.parse().opto(); - assertEquals("return (Phi(Loop,((1<<arg)&1),0)&Phi(Loop,Shl,4294967295));", code.print()); + assertEquals("return Phi(Loop,(((1<<arg)&1)&Shl),0);", code.print()); assertEquals("1", Eval2.eval(code, 0)); } diff --git a/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/src/test/java/com/seaofnodes/simple/Chapter15Test.java index 7dd0710a..8b138b46 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter15Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter15Test.java @@ -1,6 +1,9 @@ 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 org.junit.Test; import static org.junit.Assert.*; @@ -127,7 +130,7 @@ else { return rez; """); code.parse().opto(); - assertEquals("return Phi(Region,1.2,Phi(Region,2.3,.y));", code.print()); + assertEquals("return Phi(Region,1.2,.y,2.3);", code.print()); assertEquals("3.14", Eval2.eval(code, 0)); assertEquals("1.2", Eval2.eval(code, 1)); } @@ -152,7 +155,7 @@ if( iss[arg] ) rez = iss[arg][2].y; return rez;"""); code.parse().opto().typeCheck(); - assertEquals("return Phi(Region,Phi(Region,.y,1.2),1.2);", code.print()); + assertEquals("return Phi(Region,1.2,1.2,.y);", 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()); } @@ -210,44 +213,12 @@ return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 } @Test - public void sieveOEratosthenes() { - CodeGen code = new CodeGen( -""" -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; -"""); + public void sieveOEratosthenes() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp")); + CodeGen code = new CodeGen(src+"return sieve(arg);"); code.parse().opto(); - assertEquals("return [int];", code.print()); - assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); + assertEquals("return [u32];", code.print()); + assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/src/test/java/com/seaofnodes/simple/Chapter17Test.java index 6e43dbc8..78515083 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter17Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter17Test.java @@ -368,7 +368,7 @@ var b = arg ? new Bar { next = (arg==2) ? new Bar{x=2;}; x=1; }; return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0" """); code.parse().opto(); - assertEquals("return Phi(Region,Phi(Region,.x,.x),0);", code.print()); + assertEquals("return Phi(Region,.x,0);", code.print()); assertEquals("0", Eval2.eval(code, 0)); assertEquals("1", Eval2.eval(code, 1)); assertEquals("2", Eval2.eval(code, 2)); diff --git a/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/src/test/java/com/seaofnodes/simple/Chapter18Test.java index d0623d12..c0e28412 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter18Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -1,5 +1,6 @@ package com.seaofnodes.simple; +import com.seaofnodes.simple.codegen.CodeGen.Phase; import com.seaofnodes.simple.codegen.CodeGen; import org.junit.Ignore; import org.junit.Test; @@ -14,7 +15,7 @@ public class Chapter18Test { """ return 0; """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("return 0;", code._stop.toString()); assertEquals("0", Eval2.eval(code, 2)); } @@ -61,11 +62,10 @@ return x2; {int -> int}? sq = { int x -> x*x; }; -return sq; """); code.parse().opto(); - assertEquals("Stop[ return { sq}; return (Parm_x(sq,int)*x); ]", code._stop.toString()); - assertEquals("{ int -> int #1}", Eval2.eval(code, 3)); + assertEquals("return (Parm_x(sq,int)*x);", code._stop.toString()); + //assertEquals("{ int -> int #1}", Eval2.eval(code, 3)); } @Test @@ -77,7 +77,7 @@ var sq = { int x -> }; return sq(arg)+sq(3); """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("Stop[ return (#2+#2); return (Parm_x(sq,int)*x); ]", code._stop.toString()); assertEquals("13", Eval2.eval(code, 2)); } @@ -175,7 +175,7 @@ for(;;) { i2i = id(x); } """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("Stop[ return #2; return Parm_i(x,int); ]", code._stop.toString()); assertEquals("3", Eval2.eval(code, 0)); } @@ -191,9 +191,9 @@ for(;;) { arg = x(3); } """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("return Top;", code._stop.toString()); - assertEquals(null, Eval2.eval(code, 0)); + assertEquals("null", Eval2.eval(code, 0)); } @@ -216,7 +216,7 @@ ps[0] = new Person; ps[1] = new Person; fcn(ps,1); """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("return 0;", code._stop.toString()); assertEquals("0", Eval2.eval(code, 0)); } @@ -346,7 +346,7 @@ if (arg) i2i = null; if (i2i) return i2i(arg); return f2f(o)(1); """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("Stop[ return Phi(Region,#2,#2); return Parm_i(o,int); ]", code._stop.toString()); assertEquals("1", Eval2.eval(code, 2)); } @@ -362,7 +362,7 @@ Person !p = new Person; p.coffee_count += 1; return p.coffee_count; """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("return 1;", 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 bb7e8716..bce75c81 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter19Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter19Test.java @@ -1,11 +1,12 @@ package com.seaofnodes.simple; +import com.seaofnodes.simple.codegen.CodeGen.Phase; import com.seaofnodes.simple.codegen.CodeGen; -import org.junit.Ignore; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import org.junit.Test; -import static com.seaofnodes.simple.Main.PORTS; import static org.junit.Assert.*; -import static org.junit.Assert.fail; public class Chapter19Test { @@ -21,143 +22,108 @@ return 0; @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)+hashCode(s);"""); - code.parse().opto().typeCheck().GCM().localSched(); - assertEquals("Stop[ return (#2+#2); 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)); + 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("-4898613127354160978", Eval2.eval(code, 2)); } @Test public void testBasic0() { - CodeGen code = new CodeGen("return 0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return 0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return 0;", code._stop.toString()); } @Test public void testBasic1() { - CodeGen code = new CodeGen("return arg+1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg+1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (inc,arg);", code._stop.toString()); } @Test public void testBasic2() { - CodeGen code = new CodeGen("return -17;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return -17;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return -17;", code._stop.toString()); } @Test public void testBasic3() { - CodeGen code = new CodeGen("return arg==1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg==1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (set==,(cmp,arg));", code._stop.toString()); } @Test public void testBasic4() { - CodeGen code = new CodeGen("return arg<<1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg<<1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (shli,arg);", code._stop.toString()); } @Test public void testBasic5() { - CodeGen code = new CodeGen("return arg >> 1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg >> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (sari,arg);", code._stop.toString()); } @Test public void testBasic6() { - CodeGen code = new CodeGen("return arg >>> 1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg >>> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (shri,arg);", code._stop.toString()); } @Test public void testBasic7() { - CodeGen code = new CodeGen("return arg / 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg / 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (div,arg,2);", code._stop.toString()); } @Test public void testBasic8() { - CodeGen code = new CodeGen("return arg * 6;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg * 6;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (muli,arg);", code._stop.toString()); } @Test public void testBasic9() { - CodeGen code = new CodeGen("return arg & 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg & 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (andi,arg);", code._stop.toString()); } @Test public void testBasic10() { - CodeGen code = new CodeGen("return arg | 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg | 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (ori,arg);", code._stop.toString()); } @Test public void testBasic11() { - CodeGen code = new CodeGen("return arg ^ 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg ^ 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (xori,arg);", code._stop.toString()); } @Test public void testBasic12() { - CodeGen code = new CodeGen("return arg + 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); - assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString()); + CodeGen code = new CodeGen("return arg + 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (addf,(cvtf,arg),2.0f);", code._stop.toString()); } @Test public void testBasic13() { - CodeGen code = new CodeGen("return arg - 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); - assertEquals("return (subf,(i2f8,arg),2.0f);", code._stop.toString()); + CodeGen code = new CodeGen("return arg - 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (subf,(cvtf,arg),2.0f);", code._stop.toString()); } @Test public void testBasic14() { - CodeGen code = new CodeGen("return arg * 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); - assertEquals("return (mulf,(i2f8,arg),2.0f);", code._stop.toString()); + CodeGen code = new CodeGen("return arg * 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (mulf,(cvtf,arg),2.0f);", code._stop.toString()); } @Test public void testBasic15() { - CodeGen code = new CodeGen("return arg / 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); - assertEquals("return (mulf,(i2f8,arg),0.5f);", code._stop.toString()); + CodeGen code = new CodeGen("return arg / 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (mulf,(cvtf,arg),0.5f);", code._stop.toString()); } @Test @@ -166,7 +132,7 @@ hashCode(s)+hashCode(s);"""); """ int arg1 = arg + 1; return arg1 / arg;"""); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (div,(inc,arg),arg);", code._stop.toString()); } @@ -177,18 +143,18 @@ return arg1 / arg;"""); int arg1 = arg + 1; return arg1 * arg; """); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (mul,(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(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); - assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString()); +int a = arg; +return a + 2.0; +""" + ).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return (addf,(cvtf,arg),2.0f);", code._stop.toString()); } @Test @@ -202,7 +168,7 @@ else { a = arg-3; } return a;"""); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print()); } @@ -217,7 +183,7 @@ if( arg==1 ) else a=b+1; return a+b;"""); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print()); } @@ -229,7 +195,7 @@ int sum=0; for( int i=0; i<arg; i++ ) sum += i; return sum;"""); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return Phi(Loop,0,(add,Phi_sum,Phi(Loop,0,(inc,Phi_i))));", code.print()); } @@ -239,42 +205,42 @@ return sum;"""); """ struct S { int a; S? c; }; return new S;"""); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return S;", code.print()); } @Test public void testLea1() { CodeGen code = new CodeGen("int x = arg/3; return arg+x+7;"); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (lea,arg,(div,arg,3));", code.print()); } @Test public void testLea2() { CodeGen code = new CodeGen("int x = arg/3; return arg+x*4+7;"); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (lea,arg,(div,arg,3));", code.print()); } @Test public void testLea3() { CodeGen code = new CodeGen("int x = arg/3; return x*4+arg;"); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (lea,arg,(div,arg,3));", code.print()); } @Test public void testAlloc2() { CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];"); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return .[];", code.print()); } @Test public void testAlloc3() { CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1]+3;"); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return .[];", code.print()); } @@ -291,7 +257,7 @@ for( int i=0; i<ary#-1; i++ ) ary[i+1] += ary[i]; return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 """); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return .[];", code.print()); } @@ -306,7 +272,7 @@ for( int i=0; i<A#; i++ ) for( int i=0; i<A#; i++ ) B[i] += A[i]; """); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return 0;", code.print()); } @@ -319,60 +285,25 @@ for( int i=0; i<A#; i++ ) A[i]++; return A[1]; """); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return .[];", code.print()); } @Test - public void testNewton() { - CodeGen code = new CodeGen( -""" -// Newtons approximation to the square root -val sqrt = { flt x -> - 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(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); - assertEquals("return Phi(Loop,(i2f8,arg),(mulf,(addf,(divf,i2f8,Phi_guess),Phi_guess),0.5f));", code.print()); + public void testNewton() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp")) + + "flt farg = arg; return test_sqrt(farg);"; + CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return Phi(Loop,(cvtf,arg),(mulf,(addf,(divf,cvtf,Phi_guess),Phi_guess),0.5f));", 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<nprimes; j++ ) - rez[j] = primes[j]; -return rez; -"""); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); - assertEquals("return [int];", code.print()); - //assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); + public void sieveOfEratosthenes() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp")); + CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); + assertEquals("return [u32];", code.print()); + //assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); } @@ -383,7 +314,7 @@ return rez; val fcn = arg ? { int x -> x*x; } : { int x -> x+x; }; return fcn(2)*10 + fcn(3); """); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + 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()); } @@ -394,7 +325,7 @@ return fcn(2)*10 + fcn(3); val sq = { int x -> x*x; }; return sq(arg) + sq(3); """); - code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("Stop[ return (add,#2,#2); return (mul,Parm_x(sq,int),x); ]", code.print()); } } diff --git a/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/src/test/java/com/seaofnodes/simple/Chapter20Test.java index 008f5e0c..5b33d1e7 100644 --- a/src/test/java/com/seaofnodes/simple/Chapter20Test.java +++ b/src/test/java/com/seaofnodes/simple/Chapter20Test.java @@ -2,10 +2,12 @@ package com.seaofnodes.simple; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.print.ASMPrinter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; -import static com.seaofnodes.simple.Main.PORTS; import static org.junit.Assert.*; public class Chapter20Test { @@ -20,10 +22,11 @@ public class Chapter20Test { static void testCPU( String src, String cpu, String os, int spills, String stop ) { CodeGen code = new CodeGen(src); - code.parse().opto().typeCheck().instSelect(PORTS,cpu,os).GCM().localSched().regAlloc().encode(); + code.driver(CodeGen.Phase.RegAlloc,cpu,os); int delta = spills>>3; if( delta==0 ) delta = 1; - assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); if( stop != null ) assertEquals(stop, code._stop.toString()); } @@ -35,7 +38,9 @@ public class Chapter20Test { } @Test public void testAlloc0() { - testAllCPUs("return new u8[arg];", 1, "return [u8];"); + testCPU("return new u8[arg];","x86_64_v2", "SystemV",3,"return [u8];"); + testCPU("return new u8[arg];","riscv" , "SystemV",5,"return [u8];"); + testCPU("return new u8[arg];","arm" , "SystemV",5,"return [u8];"); } @Test public void testBasic1() { @@ -60,103 +65,53 @@ val sqrt = { int x -> }; return sqrt(arg) + sqrt(arg+2); """; - testCPU(src,"x86_64_v2", "SystemV",26,null); + testCPU(src,"x86_64_v2", "Win64" ,23,null); testCPU(src,"riscv" , "SystemV",19,null); - testCPU(src,"arm" , "SystemV",26,null); + testCPU(src,"arm" , "SystemV",19,null); } @Test - public void testNewtonFloat() { - String src = -""" -// Newtons approximation to the square root -val sqrt = { flt x -> - flt guess = x; - while( 1 ) { - flt next = (x/guess + guess)/2; - if( next == guess ) return guess; - guess = next; - } -}; -flt farg = arg; -return sqrt(farg) + sqrt(farg+2.0); -"""; - testCPU(src,"x86_64_v2", "SystemV",25,null); - testCPU(src,"riscv" , "SystemV",21,null); + public void testNewtonFloat() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp")) + + "flt farg = arg; return test_sqrt(farg) + test_sqrt(farg+2.0);"; + testCPU(src,"x86_64_v2", "SystemV",39,null); + testCPU(src,"riscv" , "SystemV",17,null); testCPU(src,"arm" , "SystemV",18,null); } @Test public void testAlloc2() { String src = "int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];"; - testAllCPUs(src,1,"return .[];"); + testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];"); + testCPU(src,"riscv" , "SystemV", 6,"return .[];"); + testCPU(src,"arm" , "SystemV", 6,"return .[];"); } - @Test - public void testArray1() { - String src = -""" -int[] !ary = new int[arg]; -// Fill [0,1,2,3,4,...] -for( int i=0; i<ary#; i++ ) - ary[i] = i; -// Fill [0,1,3,6,10,...] -for( int i=0; i<ary#-1; i++ ) - ary[i+1] += ary[i]; -return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 -"""; - testCPU(src,"x86_64_v2", "SystemV",3,"return .[];"); - testCPU(src,"riscv" , "SystemV",1,"return (add,.[],(mul,.[],1000));"); - testCPU(src,"arm" , "SystemV",1,"return (add,.[],(muli,.[]));"); - } - @Test - public void testString() { - String src = """ -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]; + @Test + public void testArray1() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/array1.smp")); + testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];"); + testCPU(src,"riscv" , "SystemV", 8,"return (add,.[],(mul,.[],1000));"); + testCPU(src,"arm" , "SystemV", 5,"return (add,.[],(mul,.[],1000));"); } - 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); -"""; - testCPU(src,"x86_64_v2", "SystemV",18,null); - testCPU(src,"riscv" , "SystemV", 3,null); - testCPU(src,"arm" , "SystemV", 3,null); + + @Test + public void testString() throws IOException { + 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); } @Test public void testCast() { String src = "struct Bar { int x; }; var b = arg ? new Bar; return b ? b.x++ + b.x++ : -1;"; - testAllCPUs(src,0,null); + testCPU(src,"x86_64_v2", "SystemV",2,null); + testCPU(src,"riscv" , "SystemV",2,null); + testCPU(src,"arm" , "SystemV",2,null); } @Test diff --git a/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/src/test/java/com/seaofnodes/simple/Chapter21Test.java new file mode 100644 index 00000000..ea23728c --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -0,0 +1,276 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.cpus.arm.arm; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Chapter21Test { + + @Test + public void testJig() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp")); + testCPU(src,"x86_64_v2", "Win64" ,-1,null); + testCPU(src,"riscv" , "SystemV",-1,null); + testCPU(src,"arm" , "SystemV",-1,null); + } + + static void 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()); + } + + + @Test public void testBasic1() { + String src = "return arg | 2;"; + testCPU(src,"x86_64_v2", "SystemV",1,"return (ori,mov(arg));"); + testCPU(src,"riscv" , "SystemV",0,"return ( arg | #2 );"); + testCPU(src,"arm" , "SystemV",0,"return (ori,arg);"); + } + + @Test public void testInfinite() { + String src = "struct S { int i; }; S !s = new S; while(1) 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;"); + } + + @Test + public void testArray1() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/array1.smp")); + testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];"); + testCPU(src,"riscv" , "SystemV", 7,"return (add,.[],(mul,.[],1000));"); + testCPU(src,"arm" , "SystemV", 5,"return (add,.[],(mul,.[],1000));"); + } + + @Test + public void testAntiDeps1() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/antiDep1.smp")); + testCPU(src,"x86_64_v2", "SystemV", 7,"return mov(mov(S));"); + testCPU(src,"riscv" , "SystemV",10,"return mov(mov(S));"); + testCPU(src,"arm" , "SystemV",10,"return mov(mov(S));"); + } + + @Test + public void testString() throws IOException { + 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); + } + + @Test public void testStringExport() throws IOException { + TestC.run("stringHash", 9); + } + + @Test public void testLoop2() throws IOException { + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/loop2.smp")); + testCPU(src,"x86_64_v2", "Win64" ,0,"return (inc,Phi(Loop,0,inc));"); + testCPU(src,"riscv" , "SystemV",0,"return ( Phi(Loop,0,addi) + #1 );"); + testCPU(src,"arm" , "SystemV",0,"return (inc,Phi(Loop,0,inc));"); + } + + @Test public void testNewtonExport() throws IOException { + String result = """ +0 0.000000 (0) +1 1.000000 (0) +2 1.414214 (2.22045e-16) +3 1.732051 (0) +4 2.000000 (0) +5 2.236068 (0) +6 2.449490 (0) +7 2.645751 (0) +8 2.828427 (4.44089e-16) +9 3.000000 (0) +"""; + TestC.run("newtonFloat",result,34); + + EvalRisc5 R5 = TestRisc5.build("newtonFloat", 0, 10, false); + R5.fregs[riscv.FA0 - riscv.F_OFFSET] = 3.0; + int trap_r5 = R5.step(1000); + assertEquals(0,trap_r5); + // Return register A0 holds fib(8)==55 + assertEquals(1.732051,R5.fregs[riscv.FA0 - riscv.F_OFFSET], 0.00001); + + // arm + EvalArm64 A5 = TestArm64.build("newtonFloat", 0, 10, false); + A5.fregs[arm.D0 - arm.D_OFFSET] = 3.0; + int trap_arm = A5.step(1000); + assertEquals(0,trap_arm); + assertEquals(1.732051, A5.fregs[arm.D0 - arm.D_OFFSET], 0.00001); + } + + + @Test public void testSieve() throws IOException { + // The primes + int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, }; + SB sb = new SB().p(primes.length).p("["); + for( int prime : primes ) + sb.p(prime).p(", "); + String sprimes = sb.p("]").toString(); + + // Compile, link against native C; expect the above string of primes to be printed out by C + TestC.run("sieve",sprimes, 257); + + // Evaluate on RISC5 emulator; expect return of an array of primes in + // the simulated heap. + EvalRisc5 R5 = TestRisc5.build("sieve", 100, 160, false); + int trap = R5.step(10000); + assertEquals(0,trap); + // Return register A0 holds sieve(100) + int ary = (int)R5.regs[riscv.A0]; + // Memory layout starting at ary(length,pad, prime1, primt2, prime3, prime4) + assertEquals(primes.length, R5.ld4s(ary)); + for( int i=0; i<primes.length; i++ ) + assertEquals(primes[i], R5.ld4s(ary + 4 + i*4)); + + // Evaluate on ARM5 emulator; expect return of an array of primes in + // the simulated heap. + EvalArm64 A5 = TestArm64.build("sieve", 100, 160, false); + int trap_arm = A5.step(10000); + assertEquals(0, trap_arm); + int ary_arm = (int)A5.regs[arm.X0]; + // Memory layout starting at ary(length,pad, prime1, primt2, prime3, prime4) + assertEquals(primes.length, A5.ld4s(ary_arm)); + for( int i = 0; i<primes.length; i++ ) + assertEquals(primes[i], A5.ld4s(ary_arm + 4 + i * 4)); + } + + @Test public void testFibExport() throws IOException { + 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); + 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); + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + // Return register X0 holds fib(8)==55 + assertEquals(55, A5.regs[arm.X0]); + } + + @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.st4(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.st4(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 testArgCount() throws IOException { + // Test passes more args than registers in Sys5, which is far far more + // than what Win64 allows - so Win64 gets a lot more spills here. + String arg_count = "191.000000\n"; + TestC.run("arg_count", arg_count, + TestC.CALL_CONVENTION.equals("Win64") ? 42 : 15); + + + EvalRisc5 R5 = TestRisc5.build("no_stack_arg_count", 0, 0, false); + + // Todo: handle stack(imaginary stack in emulator) + // pass in float arguments + R5.fregs[riscv.FA0 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA1 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA2 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA3 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA4 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA5 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA6 - riscv.F_OFFSET] = 1.1; + R5.fregs[riscv.FA7 - riscv.F_OFFSET] = 1.1; + + // a0 is passed in arg + R5.regs[riscv.A1] = 2; + R5.regs[riscv.A2] = 2; + R5.regs[riscv.A3] = 2; + R5.regs[riscv.A4] = 2; + R5.regs[riscv.A5] = 2; + R5.regs[riscv.A6] = 2; + R5.regs[riscv.A7] = 2; + + int trap = R5.step(100); + assertEquals(0,trap); + + double result = R5.fregs[riscv.FA0 - riscv.F_OFFSET]; + + assertEquals(22.8, result, 0.00001); + + // arm + EvalArm64 A5 = TestArm64.build("no_stack_arg_count", 0, 0, false); + + A5.fregs[arm.D0 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D1 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D2 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D3 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D4 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D5 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D6 - arm.D_OFFSET] = 1.1; + A5.fregs[arm.D7 - arm.D_OFFSET] = 1.1; + + A5.regs[arm.X1] = 2; + A5.regs[arm.X2] = 2; + A5.regs[arm.X3] = 2; + A5.regs[arm.X4] = 2; + A5.regs[arm.X5] = 2; + A5.regs[arm.X6] = 2; + A5.regs[arm.X7] = 2; + + int trap_arm = A5.step(100); + assertEquals(0,trap_arm); + + double result1 = A5.fregs[arm.D0 - arm.D_OFFSET]; + assertEquals(22.8, result1, 0.00001); + } +} diff --git a/src/test/java/com/seaofnodes/simple/Compile.java b/src/test/java/com/seaofnodes/simple/Compile.java new file mode 100644 index 00000000..088055f9 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/Compile.java @@ -0,0 +1,65 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.FunNode; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import org.junit.Ignore; +import org.junit.Test; +import static org.junit.Assert.*; + +public class Compile { + + private static Process run(String simple, String c) throws IOException, InterruptedException { + boolean USE_WSL = System.getProperty("os.name").startsWith("Windows"); + CodeGen code = new CodeGen(simple); + code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched().regAlloc().encode(); + if (c != null) for (var n:code._start._outputs) if (n instanceof FunNode f && f._name.equals("main")) f._name = "simple_main"; + code.exportELF("test.o"); + var params = new ArrayList<String>(); + if (USE_WSL) params.add("wsl.exe"); + params.add("gcc"); + params.add("-o"); + params.add("test"); + if (c != null) { + Files.writeString(Path.of("test.c"), c); + params.add("test.c"); + } + params.add("test.o"); + Process p = Runtime.getRuntime().exec(params.toArray(String[]::new)); + assertEquals(0, p.waitFor()); + return Runtime.getRuntime().exec(USE_WSL ? new String[]{"wsl.exe", "./test"}:new String[]{"./test"}); + } + + @Test + @Ignore + public void testCompile() throws IOException, InterruptedException { + var p = run(""" + val testMy = { flt x -> + flt guess = x; + while( 1 ) { + flt next = (x/guess + guess)/2; + if( next == guess ) return guess; + guess = next; + } + }; + """,""" + + #include <stdio.h> + + extern double testMy(double); + + int main(int argc, char** argv) { + printf("%f\\n", testMy(2.0)); + return 0; + } + + """); + assertEquals(0, p.waitFor()); + assertEquals("1.414214\n", new String(p.getInputStream().readAllBytes(), StandardCharsets.UTF_8)); + } + +} diff --git a/src/test/java/com/seaofnodes/simple/EvalArm64.java b/src/test/java/com/seaofnodes/simple/EvalArm64.java new file mode 100644 index 00000000..3ea9bea0 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/EvalArm64.java @@ -0,0 +1,751 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.node.cpus.arm.arm; + +public class EvalArm64 { + // Memory image, always 0-based + public final byte[] _buf; + + // GPRs + final long[] regs; + // FRs + final double[] fregs; + + int _pc; + int SENTINEL_CALLOC = -4; + // Start of free memory for allocation + int _heap; + // Cycle counters + int _cycle; + + // flags + boolean N = false; + boolean Z = false; + boolean C = false; + boolean V = false; + + EvalArm64(byte[] buf, int stackSize) { + _buf = buf; + regs = new long[32]; + fregs = new double[32]; + // Stack grows down, heap grows up + regs[arm.RSP] = _heap = stackSize; + _pc = 0; + } + + // Little endian + public int ld1s(int x) { return _buf[x ] ; } + public int ld1z(int x) { return _buf[x ]&0xFF; } + public int ld2s(int x) { return ld1z(x) | ld1s(x+1)<< 8; } + public int ld2z(int x) { + return ld1z(x) | ld1z(x+1)<< 8; } + public int ld4s(int x) { return ld2z(x) | ld2s(x+2)<<16; } + public long ld8 (int x) { return (long)ld4s(x)&0xFFFFFFFFL | ((long)ld4s(x+4))<<32; } + + + public float ld4f(int x) { return Float.intBitsToFloat(ld4s(x)); } + public double ld8f(int x) { return Double.longBitsToDouble(ld8(x)); } + + public void st1(int x, int val) { _buf[x] = (byte)val; } + public void st2(int x, int val) { st1(x,val); st1(x+1,val>> 8); } + public void st4(int x, int val) { st2(x,val); st2(x+2,val>>16); } + public void st8(int x, long val) { st4(x,(int)val); st2(x+4,(int)(val>>32)); } + + public int step(int maxops) { + int trap = 0; + long rval = 0; + double frval = 0; + int pc = _pc; + int cycle = _cycle; + boolean is_f = false; + outer: + for(int icount = 0; icount < maxops; icount++) { + int ir = 0; + rval = 0; + frval = 0; + cycle++; + if( pc >= _buf.length ) { + trap = 1 + 1; // Handle access violation on instruction read. + break; + } + + if( (pc & 3)!=0 ) { + trap = 1; // Handle PC-misaligned access + break; + } + + // Load instruction from image buffer at PC + ir = ld4s( pc ); + + int rdid = ir & 0x1f; + // [31:24] + int opcode1 = (ir >> 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; + 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; + } + } + // 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 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); + } + 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); + } + 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); + } + 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); + } + 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 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); + } + 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; + } + 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; + } + + 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); + } + + 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); + } + + 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); + } + + 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); + } + 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); + } + break; + } + case 0x9b: { + // mul + int rn = (ir >> 5) & 0x1F; + int rm = (ir >> 16) & 0x1F; + rval = regs[rn] * regs[rm]; + 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 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 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 0xF1: { + // subs(immediate) + int rn = (ir >> 5) & 0x1F; + int immediate = ir << 10 >> 20; + long lhs = regs[rn]; + + 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 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; + } + case 0x91: { + // add(immediate) + int rn = (ir >> 5) & 0x1F; + int immediate = ir << 10 >> 20; + // only hit it twice + rval = regs[rn] + immediate; + break; + } + case 0xD3: { + // lsl(immediate) + int imm = (ir >> 16) & 0x3F; + int rn = (ir >> 5) & 0x1F; + rval = regs[rn] << (64 - imm); + break; + } + case 0x93: { + // asr(immediate) + int imm = (ir >> 16) & 0x3F; + int rn = (ir >> 5) & 0x1F; + rval = regs[rn] >> (imm); + 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 0xAA: { + // MOV(register) + int rm = (ir >> 16) & 0x1F; + rval = regs[rm]; + break; + } + case 0xD6: { + rdid = -1; + if(ld4s(pc + 4) == 0) { + break outer; + } + } + // 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); + } + break; + } + case 0x9e: { + // scvtf + is_f = true; + int rs1 = (ir >> 5) & 0x1F; + frval = (double)regs[rs1]; + break; + } + default: trap = (2+1); + } + if(trap != 0) { + _pc = pc; + break; + } + if(rdid != -1) { + if(is_f) {fregs[rdid] = frval; is_f = false;} + else regs[rdid] = rval; + } + + pc += 4; + } + // Handle traps and interrupts. + if(trap != 0) { + return trap; + } + _cycle = cycle; + _pc = pc; + return 0; + } + + +} diff --git a/src/test/java/com/seaofnodes/simple/EvalRisc5.java b/src/test/java/com/seaofnodes/simple/EvalRisc5.java new file mode 100644 index 00000000..fd953342 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/EvalRisc5.java @@ -0,0 +1,353 @@ + package com.seaofnodes.simple; + +/** Simple RISC5 CPU Emulator + * + * Original works credit Charles Lohr. + * This is a complete rewrite in Java. + * + * Load code into memory and go! + */ + +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.node.cpus.riscv.riscv; + +import java.util.Arrays; + + public class EvalRisc5 { + + // Memory image, always 0-based + public final byte[] _buf; + + // GPRs + final long[] regs; + // FRs + final double[] fregs; + // PC + int _pc; + + // Start of free memory for allocation + int _heap; + + // Cycle counters + int _cycle; + + EvalRisc5( byte[] buf, int stackSize ) { + _buf = buf; + regs = new long[32]; + fregs = new double[32]; + // Stack grows down, heap grows up + regs[riscv.RSP] = _heap = stackSize; + _pc = 0; + } + + // Little endian + public int ld1s(int x) { return _buf[x ] ; } + public int ld1z(int x) { return _buf[x ]&0xFF; } + public int ld2s(int x) { return ld1z(x) | ld1s(x+1)<< 8; } + public int ld2z(int x) { return ld1z(x) | ld1z(x+1)<< 8; } + public int ld4s(int x) { return ld2z(x) | ld2s(x+2)<<16; } + public long ld8 (int x) { return (long)ld4s(x)&0xFFFFFFFFL | ((long)ld4s(x+4))<<32; } + + public float ld4f(int x) { return Float.intBitsToFloat(ld4s(x)); } + public double ld8f(int x) { return Double.longBitsToDouble(ld8(x)); } + + + public void st1(int x, int val) { _buf[x] = (byte)val; } + public void st2(int x, int val) { st1(x,val); st1(x+1,val>> 8); } + public void st4(int x, int val) {st2(x,val); st2(x+2,val>>16); } + public void st8(int x, long val) { st4(x,(int)val); st2(x+4,(int)(val>>32)); } + + // Note: only a few bits are used. (Machine = 3, User = 0) + // Bits 0..1 = privilege. + // Bit 2 = WFI (Wait for interrupt) + // Bit 3+ = Load/Store reservation LSBs. + int _extraflags; + + public int step( int maxops ) { + int trap = 0; + long rval = 0; + double frval = 0; + int pc = _pc; + int cycle = _cycle; + boolean is_f = false; + outer: + for( int icount = 0; icount < maxops; icount++ ) { + int ir = 0; + rval = 0; + cycle++; + + if( pc >= _buf.length ) { + trap = 1 + 1; // Handle access violation on instruction read. + break; + } + if( (pc & 3)!=0 ) { + trap = 1; // Handle PC-misaligned access + break; + } + // Load instruction from image buffer at PC + ir = ld4s( pc ); + int rdid = (ir >> 7) & 0x1f; + + switch( ir & 0x7f ) { + case 0x37: // LUI (0b0110111) + rval = ( ir & 0xfffff000 ); + break; + case 0x17: // AUIPC (0b0010111) + rval = pc + ( ir & 0xfffff000 ); + break; + case 0x6F: { // JAL (0b1101111) + int reladdy = ((ir & 0x80000000)>>11) | ((ir & 0x7fe00000)>>20) | ((ir & 0x00100000)>>9) | ((ir&0x000ff000)); + if( (reladdy & 0x00100000)!=0 ) reladdy |= 0xffe00000; // Sign extension. + rval = pc + 4; + pc = pc + reladdy - 4; + break; + } + case 0x67: { // JALR (0b1100111) + int imm_se = ir >> 20; // Sign extend 12 bits + rval = pc + 4; + pc = ((int)regs[ (ir >> 15) & 0x1f ] + imm_se) & ~1; + // Return from top-level ; exit sim + if( pc==0 ) { + if( rdid!=0 ) regs[rdid] = rval; // Write PC into register before exit + break outer; + } + // Inline CALLOC effect + if( pc == Encoding.SENTINAL_CALLOC ) { + int size = (int)(regs[10]*regs[11]); + 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 + pc -= 4; + break; + } + case 0x63: { // Branch (0b1100011) + int immm4 = ((ir & 0xf00)>>7) | ((ir & 0x7e000000)>>20) | ((ir & 0x80) << 4) | ((ir >> 31)<<12); + if( (immm4 & 0x1000)!=0 ) immm4 |= 0xffffe000; + long rs1 = regs[(ir >> 15) & 0x1f]; + long rs2 = regs[(ir >> 20) & 0x1f]; + immm4 = pc + immm4 - 4; + rdid = 0; + if( switch( (ir >> 12) & 0x7 ) { + case 0 -> rs1 == rs2; + case 1 -> rs1 != rs2; + case 4 -> rs1 < rs2; + case 5 -> rs1 >= rs2; + case 6 -> Long.compareUnsigned(rs1,rs2) < 0; //BLTU + case 7 -> Long.compareUnsigned(rs1,rs2) >= 0; //BGEU + default -> { trap = (2+1); yield false; } + } ) pc = immm4; + break; + } + case 0x03: { // Load (0b0000011) + int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b + int imm = ir >> 20; + int imm_se = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0 ); + int rsval = rs1 + imm_se; + + if( rsval >= _buf.length-3 ) { + trap = (5+1); + rval = rsval; + } else { + switch( ( ir >> 12 ) & 0x7 ) { + //LB, LH, LW, LBU, LHU + case 0: rval = ld1s( rsval ); break; + case 1: rval = ld2s( rsval ); break; + case 2: rval = ld4s( rsval ); break; + case 3: rval = ld8 ( rsval ); break; + case 4: rval = ld1z( rsval ); break; + case 5: rval = ld2z( rsval ); break; + case 6: rval = ld4s( rsval ); break; + default: trap = (2+1); + } + } + break; + } + // floats + case 0x7: { + is_f = true; + int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b + int imm = ir >> 20; + int imm_se = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0 ); + int rsval = rs1 + imm_se; + + if( rsval >= _buf.length-3 ) { + trap = (5+1); + rval = rsval; + } else { + switch( ( ir >> 12 ) & 0x7 ) { + case 2: frval = ld4f( rsval ); break; + case 3: frval = ld8f( rsval ); break; + default: trap = (2+1); + } + } + break; + } + + case 0x23: { // Store 0b0100011 + int rs1 = (int)regs[(ir >> 15) & 0x1f]; // Address chop to 32b + long rs2 = regs[(ir >> 20) & 0x1f]; + int addy = ( ( ir >> 7 ) & 0x1f ) | ( ( ir & 0xfe000000 ) >> 20 ); + if( (addy & 0x800)!=0 ) addy |= 0xfffff000; + addy += rs1; + rdid = 0; + if( addy >= _buf.length-3 ) { + trap = (7+1); // Store access fault. + rval = addy; + } else { + switch( ( ir >> 12 ) & 0x7 ) { //SB, SH, SW + case 0: st1( addy, (int)rs2 ); break; + case 1: st2( addy, (int)rs2 ); break; + case 2: st4( addy, (int)rs2 ); break; + case 3: st8( addy, rs2 ); break; + default: trap = (2+1); + } + } + break; + } + case 0x13: // Op-immediate 0b0010011 + case 0x33: { // Op 0b0110011 + int imm = ir >> 20; + imm = imm | (( (imm & 0x800)!=0 ) ? 0xfffff000 : 0); + long rs1 = regs[(ir >> 15) & 0x1f]; + boolean is_reg = (ir & 0x20)!=0; + long rs2 = is_reg ? regs[imm & 0x1f] : imm; + + // Insert the detection here + int rs1id = (ir >> 15) & 0x1f; + int funct3 = (ir >> 12) & 0x7; + + if( is_reg && ( ir & 0x02000000 )!=0 ) { + switch( (ir>>12)&7 ) { //0x02000000 = RV32M + case 0: rval = rs1 * rs2; break; // MUL + case 1: rval = (int)((long)((int)rs1) * (long)((int)rs2)) >> 32; break; // MULH + case 2: rval = (int)((long)((int)rs1) * (long)rs2) >> 32; break; // MULHSU + case 3: rval = (int)((long)rs1 * (long)rs2) >> 32; break; // MULHU + case 4: if( rs2 == 0 ) rval = -1; else rval = ((int)rs1 == Integer.MIN_VALUE && (int)rs2 == -1) ? rs1 : ((int)rs1 / (int)rs2); break; // DIV + case 5: if( rs2 == 0 ) rval = 0xffffffff; else rval = rs1 / rs2; break; // DIVU + case 6: if( rs2 == 0 ) rval = rs1; else rval = ((int)rs1 == Integer.MIN_VALUE && (int)rs2 == -1) ? 0 : ((int)((int)rs1 % (int)rs2)); break; // REM + case 7: if( rs2 == 0 ) rval = rs1; else rval = rs1 % rs2; break; // REMU + } + } 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 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 6 -> rs1 | rs2; + case 7 -> rs1 & rs2; + default -> rval; + }; + } + break; + } + case 0x0f: // 0b0001111 + rdid = 0; // fencetype = (ir >> 12) & 0b111; We ignore fences in this impl. + break; + case 0x73: // Zifencei+Zicsr (0b1110011) + trap = (2+1); // Note micrrop 0b100 == undefined. + break; + + case 0x2f: { // RV32A (0b00101111) + long rs1 = regs[(ir >> 15) & 0x1f]; + long rs2 = regs[(ir >> 20) & 0x1f]; + int irmid = ( ir>>27 ) & 0x1f; + + // We don't implement load/store from UART or CLNT with RV32A here. + if( rs1 >= _buf.length-3 ) { + trap = (7+1); //Store/AMO access fault + rval = rs1; + } else { + rval = ld4s( (int)rs1 ); + + // Referenced a little bit of https://github.com/franzflasch/riscv_em/blob/master/src/core/core.c + boolean dowrite = true; + switch( irmid ) { + case 2: //LR.W (0b00010) + dowrite = false; + _extraflags = (_extraflags & 0x07) | (int)(rs1<<3); + break; + case 3: { //SC.W (0b00011) (Make sure we have a slot, and, it's valid) + boolean xflag = _extraflags >> 3 != ( rs1 & 0x1fffffff ); + rval = xflag ? 1 : 0; // Validate that our reservation slot is OK. + dowrite = !xflag; // Only write if slot is valid. + break; + } + case 0: rs2 += rval; break; // AMOADD.W (0b00000) + case 1: break; // AMOSWAP.W (0b00001) + case 4: rs2 ^= rval; break; // AMOXOR.W (0b00100) + case 8: rs2 |= rval; break; // AMOOR.W (0b01000) + case 12: rs2 &= rval; break; // AMOAND.W (0b01100) + case 16: rs2 = Math.min( rs2, rval ); break; //AMOMIN.W (0b10000) + case 20: rs2 = Math.max( rs2, rval ); break; //AMOMAX.W (0b10100) + case 24: rs2 = Math.min( rs2, rval ); break; //AMOMINU.W (0b11000) + case 28: rs2 = Math.max( rs2, rval ); break; //AMOMAXU.W (0b11100) + default: trap = (2+1); dowrite = false; break; //Not supported. + } + if( dowrite ) st4( (int)rs1, (int)rs2 ); + } + break; + } + + case 0x53: { + is_f = true; + int rs1 = (ir >> 15) & 0x1F; + int rs2 = (ir >> 20) & 0x1F; + double lhs = fregs[rs1]; + double rhs = fregs[rs2]; + int func5 = (ir >> 27) & 0x1F; + int func2 = (ir >> 25) & 0x3; + if( func2 != 1 ) throw Utils.TODO(); // 1==double + switch( func5 ) { + case 0: frval = lhs + rhs; break; // fadd.d + case 1: frval = lhs - rhs; break; // fsub.d + case 2: frval = lhs * rhs; break; // fmul.d + case 3: frval = lhs / rhs; break; // fdiv.d + case 5: frval = Math.min(lhs,rhs); break; // fmin.d, also used for move + case 20: + is_f = false; // Output into a GPR + rval = switch( (ir >> 12) & 7 ) { + case 0 -> lhs <= rhs ? 1 : 0; + case 1 -> lhs == rhs ? 1 : 0; + case 2 -> lhs < rhs ? 1 : 0; + default -> throw Utils.TODO(); + }; + break; + case 26: frval = (double)regs[rs1]; break; // fcvt.w.d + default: + trap = (2+1); // Fault: Invalid opcode. + throw Utils.TODO(); + } + break; + } + + default: trap = (2+1); // Fault: Invalid opcode. + } + + // If there was a trap, do NOT allow register writeback. + if( trap!=0 ) { + _pc = pc; + break; + } + + if( rdid!=0 || is_f) { + if(is_f) {fregs[rdid] = frval; is_f = false;} // Write back register. + else regs[rdid] = rval; // Write back register. + } + + pc += 4; + } + + // Handle traps and interrupts. + if( trap!=0 ) + return trap; + + _cycle = cycle; + _pc = pc; + return 0; + } +} diff --git a/src/test/java/com/seaofnodes/simple/Main.java b/src/test/java/com/seaofnodes/simple/Main.java index f3a6784c..589c043b 100644 --- a/src/test/java/com/seaofnodes/simple/Main.java +++ b/src/test/java/com/seaofnodes/simple/Main.java @@ -5,7 +5,6 @@ import java.nio.file.Files; import java.nio.file.Path; public class Main { - public static final String PORTS = "com.seaofnodes.simple.node.cpus"; // Compile and run a simple program public static void main( String[] args ) throws Exception { // First arg is file, 2nd+ args are program args diff --git a/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/src/test/java/com/seaofnodes/simple/MergeSortTest.java index c83fe089..a1004fb5 100644 --- a/src/test/java/com/seaofnodes/simple/MergeSortTest.java +++ b/src/test/java/com/seaofnodes/simple/MergeSortTest.java @@ -1,67 +1,44 @@ package com.seaofnodes.simple; import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; public class MergeSortTest { - @Test - public void testMergeSort() { - String src = -""" -// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort - -val merge_sort = { int[] a, int[] b, int n -> - copy_array(a, 0, n, b); - split_merge(a, 0, n, b); -}; - -val split_merge = { int[] b, int begin, int end, int[] a -> - if (end - begin <= 1) - return 0; - int middle = (end + begin) / 2; - split_merge(a, begin, middle, b); - split_merge(a, middle, end, b); - merge(b, begin, middle, end, a); - return 0; -}; - -val merge = { int[] b, int begin, int middle, int end, int[] a -> - int i = begin, j = middle; - - for (int k = begin; k < end; k++) { - // && and || - bool cond = false; - if (i < middle) { - if (j >= end) cond = true; - else if (a[i] <= a[j]) cond = true; - } - if (cond) b[k] = a[i++]; - else b[k] = a[j++]; + @Test public void testMergeSort0() throws IOException { + int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, }; + SB sb = new SB().p(primes.length).p("["); + for( int prime : primes ) + sb.p(prime).p(", "); + String sprimes = sb.p("]").toString(); + TestC.run("sort", sprimes, 40); + + + EvalRisc5 R5 = TestRisc5.build("src/test/java/com/seaofnodes/simple/progs", "sort", "merge_sort", 0, 36, false); + // Allocate an array of primes, filled in reverse order + int ps = R5._heap; + R5._heap += (primes.length+1)*8; // 25 primes, and length + R5.st4(ps,primes.length); + for( int i=0; i<primes.length; i++ ) + R5.st8(ps+(primes.length-1-i+1)*8,primes[i]); + // Pass array base when calling `merge_sort` + R5.regs[riscv.A0] = ps; // Array base to be sorted + + int trap = R5.step(100000); + assertEquals(0,trap); + + assertEquals(primes.length,R5.ld4s(ps)); + for( int i=0; i<primes.length; i++ ) + assertEquals(primes[i], R5.ld8(ps+(i+1)*8)); + + // TODO: replace with ARM Eval + String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sort.smp")); + Chapter21Test.testCPU(src,"arm", "SystemV",36,null); } -}; - -val copy_array = { int[] a, int begin, int end, int[] b -> - for (int k = begin; k < end; k++) - b[k] = a[k]; -}; - -int[] !a = new int[arg]; -int[] !b = new int[a#]; - -for (int i = 0; i < a#; i++) - a[i] = a# - i; - -merge_sort(a, b, a#); - -return a; -"""; - Chapter20Test.testCPU(src,"x86_64_v2", "SystemV",46,"Stop[ return mov(mov([int])); return 0; ]"); - Chapter20Test.testCPU(src,"riscv" , "SystemV",36,"Stop[ return mov(mov([int])); return 0; ]"); - Chapter20Test.testCPU(src,"arm" , "SystemV",37,"Stop[ return mov(mov([int])); return 0; ]"); -//assertEquals("int[ 1,2,3,4,5,6,7,8,9,10,11]", Eval2.eval(code, 11)); - } - } diff --git a/src/test/java/com/seaofnodes/simple/Simple.java b/src/test/java/com/seaofnodes/simple/Simple.java new file mode 100644 index 00000000..9b1e2827 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/Simple.java @@ -0,0 +1,313 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.print.*; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.io.InputStreamReader; +import java.io.BufferedReader; +import java.io.IOException; +import java.util.stream.Collectors; + +public class Simple { + public static final String PORTS = "com.seaofnodes.simple.node.cpus"; + + static final int DUMP_AFTER_PARSE = 1<<0; + 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_GCM = 1<<5; + static final int DUMP_AFTER_LOCAL_SCHED = 1<<6; + static final int DUMP_AFTER_REG_ALLOC = 1<<7; + static final int DUMP_AFTER_ENCODE = 1<<8; + + static final int DUMP_FINAL = 1<<16; + + static final int DUMP_DOT = 1<<29; + static final int DUMP_PASS_NAME = 1<<30; + static final int DUMP_AFTER_ALL = 0xff | DUMP_PASS_NAME; + static final int DUMP_DOT_AFTER_ALL = 0xff | DUMP_DOT; + + static final int DUMP_DISASSEMBLE = 1<<31; + + static String system_cpu = null; + static String system_abi = null; + + static void usage() { + System.out.println( +""" +simple <input-file> [options] [--eval|--run ...] +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 <cpu-name> - use specific CPU (x86_64_v2, riscv, arm) + --abi <abi-name> - use speific ABI variant (SystemV) + --target - print native CPU and ABI + --version + --help +""" +); + System.exit(0); + } + + static void bad_usage() { + System.err.println("ERROR: Invalid usage (use --help)"); + System.exit(1); + } + + 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("-"); + System.out.println(system_abi); + System.exit(0); + } + + 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(); + }; + + 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); + } + } else { + if ((dump & DUMP_PASS_NAME) != 0) { + System.err.println(switch (pass) { + case DUMP_AFTER_PARSE -> "After Parse:"; + 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_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_FINAL -> "Final:"; + default -> throw Utils.TODO(); + }); + } + + System.err.println(IRPrinter.prettyPrint(code._stop, 9999)); + } + } + } + + static void print_compilation_times(CodeGen code) { + double t, total = 0; + + total += t = code._tParse / 1e3; + System.out.println(String.format("Parsing Time: %.3f sec", t)); + total += t = code._tOpto / 1e3; + System.out.println(String.format("Optimization Time: %.3f sec", t)); + total += t = code._tTypeCheck / 1e3; + System.out.println(String.format("Type Checking Time: %.3f sec", t)); + total += t = code._tLoopTree / 1e3; + System.out.println(String.format("Loop Tree Time: %.3f sec", t)); + total += t = code._tInsSel / 1e3; + System.out.println(String.format("Instruction Selection Time: %.3f sec", t)); + total += t = code._tGCM / 1e3; + System.out.println(String.format("GCM Time: %.3f sec", t)); + total += t = code._tLocal / 1e3; + System.out.println(String.format("Local Scheduling Time: %.3f sec", t)); + total += t = code._tRegAlloc / 1e3; + System.out.println(String.format("Register Allocation Time: %.3f sec", 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)); + } + + static String getInput() { + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + return reader.lines().collect(Collectors.joining("\n")); + } + + 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_codegen = false; + boolean do_print_size = false; + boolean do_print_time = false; + int dump = 0; + int first_arg = 0; + String src = null; + 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++) { + 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(); + input_filename = arg; + } + } + + if (input_filename == null && !use_stdin) { + System.err.println("ERROR: no input file' (use --help)"); + System.exit(1); + } + + 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); + } + 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); + } + + // Compilation pipeline + CodeGen code = new CodeGen(src); + + code.parse(); + dump(code, dump, DUMP_AFTER_PARSE); + + code.opto(); + dump(code, dump, DUMP_AFTER_OPTO); + + code.typeCheck(); + dump(code, dump, DUMP_AFTER_TYPE_CHECK); + + code.loopTree(); + dump(code, dump, DUMP_AFTER_LOOP_TREE); + + if (do_codegen) { + code.instSelect(PORTS, cpu, abi); + dump(code, dump, DUMP_AFTER_INSTR_SELECT); + } + + code.GCM(); + dump(code, dump, DUMP_AFTER_GCM); + + code.localSched(); + dump(code, dump, DUMP_AFTER_LOCAL_SCHED); + + if (do_codegen) { + code.regAlloc(); + dump(code, dump, DUMP_AFTER_REG_ALLOC); + + code.encode(); + dump(code, dump, DUMP_AFTER_ENCODE); + } + + dump(code, dump, DUMP_FINAL); + + if (do_codegen && (dump & DUMP_DISASSEMBLE) != 0) { + System.out.println(code.asm()); + } + + if (do_print_size) { + System.out.println(String.format("Code Size: %d", code._encoding._bits.size())); + } + + if (do_print_time) { + print_compilation_times(code); + } + + 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; + 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); + } + + throw Utils.TODO(); + } + } +} diff --git a/src/test/java/com/seaofnodes/simple/TestArm64.java b/src/test/java/com/seaofnodes/simple/TestArm64.java new file mode 100644 index 00000000..5d7d1361 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/TestArm64.java @@ -0,0 +1,42 @@ +package com.seaofnodes.simple; + + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.cpus.arm.arm; +import junit.framework.Assert; +import java.nio.file.Files; +import java.nio.file.Path; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +// Runs 32-bit ARM code in an emulator + +public class TestArm64 { + public static EvalArm64 build( String file, int arg, int spills, boolean print ) throws IOException { + return build("src/test/java/com/seaofnodes/simple/progs",file, arg, spills, print); + } + + // Compile and run a simple program + public static EvalArm64 build( String dir, String file, int arg, int spills, boolean print ) throws IOException { + // Compile and export Simple + String src = Files.readString(Path.of(dir+"/"+file+".smp")); + CodeGen code = new CodeGen(src).driver("arm", "SystemV",null); + if( print ) { code.print_as_hex(); System.out.print(code.asm()); } + + // Allocation quality not degraded + int delta = spills>>3; + if( delta == 0 ) delta =1; + if( spills != -1 ) assertEquals("Expect spills:", spills, code._regAlloc._spillScaled, delta); + + // Image + byte[] image = new byte[1<<20]; // A megabyte (1024*1024 bytes) + EvalArm64 ARM = new EvalArm64(image, 1<<16); + // Code at offset 0 + System.arraycopy(code._encoding.bits(), 0, image, 0, code._encoding.bits().length); + // Initial incoming int arg + ARM.regs[arm.X0] = arg; + // malloc memory starts at 64K and goes up + return ARM; + } +} diff --git a/src/test/java/com/seaofnodes/simple/TestC.java b/src/test/java/com/seaofnodes/simple/TestC.java new file mode 100644 index 00000000..5609758b --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/TestC.java @@ -0,0 +1,89 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import java.nio.file.Files; +import java.nio.file.Path; +import java.io.InputStream; +import java.io.IOException; +import static org.junit.Assert.*; + +// Call a C driver program, which calls a Simple-generated .o, and self-checks. +// Expects GCC from the default CLI, compiles C, compiles Simple, links and runs. +public abstract class TestC { + + public static final String OS = System.getProperty("os.name"); + public static final String CPU = System.getProperty("os.arch"); + + 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"); + }; + + public static void run( String file, int spills ) throws IOException { run(file,"",spills); } + + public static void run( String file, String expected, int spills ) throws IOException { + run("src/test/java/com/seaofnodes/simple/progs",file,expected,spills); + } + + // Compile and run a simple program + public static void run( String dir, String file, String expected, int spills ) throws IOException { + // Files + String cfile = dir+"/"+file+".c" ; + String sfile = dir+"/"+file+".smp"; + String efile = "build/objs/"+file; + + // Compile and export Simple + String src = Files.readString(Path.of(sfile)); + _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 { + String bin = efile+xtn; + String obj = bin+".o"; + // 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 ); + assertEquals(expected,result); + + // Allocation quality not degraded + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + + } +} diff --git a/src/test/java/com/seaofnodes/simple/TestRisc5.java b/src/test/java/com/seaofnodes/simple/TestRisc5.java new file mode 100644 index 00000000..041ed833 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/TestRisc5.java @@ -0,0 +1,50 @@ +package com.seaofnodes.simple; + +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.node.FunNode; +import com.seaofnodes.simple.node.Node; +import com.seaofnodes.simple.node.cpus.riscv.riscv; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import static org.junit.Assert.*; + +// Runs 32-bit R5 code in an emulator +public abstract class TestRisc5 { + + public static EvalRisc5 build( String file, int arg, int spills, boolean print ) throws IOException { + return build("src/test/java/com/seaofnodes/simple/progs",file, null, arg, spills, print); + } + + // Compile and run a simple program + public static EvalRisc5 build( String dir, String file, String main, int arg, int spills, boolean print ) throws IOException { + // Compile and export Simple + String src = Files.readString(Path.of(dir+"/"+file+".smp")); + CodeGen code = new CodeGen(src).driver("riscv", "SystemV",null); + if( print ) { code.print_as_hex(); System.out.print(code.asm()); } + + // Allocation quality not degraded + int delta = spills>>3; + if( delta==0 ) delta = 1; + if( spills != -1 ) + assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta); + + // Image + byte[] image = new byte[1<<20]; // A megabyte (1024*1024 bytes) + EvalRisc5 R5 = new EvalRisc5(image, 1<<16); + // Code at offset 0 + System.arraycopy(code._encoding.bits(), 0, image, 0, code._encoding.bits().length); + // Initial incoming int arg + R5.regs[riscv.A0] = arg; + // malloc memory starts at 64K and goes up + + // Look up starting PC or zero if not given + if( main!=null ) + for( Node use : code._start._outputs ) + if( use instanceof FunNode fun && main.equals(fun._name) ) + R5._pc = code._encoding._opStart[fun._nid]; + + return R5; + } + +} diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java index 4bfa7628..7ba9272f 100644 --- a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java +++ b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java @@ -248,20 +248,20 @@ public class Scheduler { assert data.block != null; assert data.users == 0; - if (data.node instanceof LoadNode l) { + if (data.node instanceof MemOpNode l && l._isLoad) { assert isValid(data); // Handle anti-deps of load nodes. // At this point all anti-dep nodes are scheduled, // but they did not refine the placement // so do that now. - var mem = l.in(1); + var mem = data.node.in(1); for(var out : mem._outputs) { if (out instanceof PhiNode p) { var r = p.in(0); for (int i = 1; i < p.nIns(); i++) { if (p.in(i) == mem) optionalRefinePlacement(data, r.in(i)); } - } else if (!(out instanceof LoadNode)) { + } else if (!(out instanceof MemOpNode ld && ld._isLoad)) { optionalRefinePlacement(data, out); } } @@ -273,11 +273,11 @@ public class Scheduler { for(var in:data.node._inputs) { if (in!=null) update(d(in), data.block); } - if (data.node instanceof StoreNode s) { + if (data.node instanceof MemOpNode s && !s._isLoad) { // Store nodes have anti-deps to load nodes. // So decrease the uses of these loads when the store is placed. - for (var out: s.in(1)._outputs) { - if (out instanceof LoadNode) od(out).ifPresent(this::decUsers); + for (var out: ((Node)s).in(1)._outputs) { + if (out instanceof MemOpNode ld && ld._isLoad) od(out).ifPresent(this::decUsers); } } } @@ -471,14 +471,14 @@ public class Scheduler { } else { for (var in : node._inputs) if (in != null && !(in instanceof CFGNode)) markAlive(dataQueue, in, false); } - if (node instanceof StoreNode) mem.push(data); + if (node instanceof MemOpNode s && !s._isLoad) mem.push(data); } // Handle store nodes and increase load with an anti-dep to the store. while (!mem.isEmpty()) { var data = mem.pop(); node = data.node; for(var out:node.in(1)._outputs) { - if (out instanceof LoadNode) od(out).ifPresent(d->d.users++); + if (out instanceof MemOpNode ld && ld._isLoad) od(out).ifPresent(d->d.users++); } } } diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java index 669a1e6a..b90b663d 100644 --- a/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java +++ b/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java @@ -101,7 +101,7 @@ class FuzzerUtils { System.setErr(NULL_PRINT_STREAM); System.setOut(NULL_PRINT_STREAM); var code = new CodeGen(script, TypeInteger.BOT, workListSeed); - return code.parse().opto().typeCheck().GCM().localSched(); + return code.driver(CodeGen.Phase.LocalSched); } finally { System.setErr(err); System.setOut(out); diff --git a/src/test/java/com/seaofnodes/simple/progs/antiDep1.smp b/src/test/java/com/seaofnodes/simple/progs/antiDep1.smp new file mode 100644 index 00000000..b18dfd16 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/antiDep1.smp @@ -0,0 +1,11 @@ +// -*- mode: java; -*- +struct S { int f; }; +var v0 = new S; +S? v1; +if (arg) v1 = new S; +if (v1) { + v0.f = v1.f; +} else { + v0.f = 2; +} +return v0; diff --git a/src/test/java/com/seaofnodes/simple/progs/arg_count.c b/src/test/java/com/seaofnodes/simple/progs/arg_count.c new file mode 100644 index 00000000..e247e28a --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/arg_count.c @@ -0,0 +1,10 @@ +#include <stdio.h> + +__attribute__ ((CALL_CONV)) +extern double addAll( long long i0, double f1, long long i2, double f3, long long i4, double f5, long long i6, double f7, long long i8, double f9, long long i10, double f11, long long i12, double f13, long long i14, double f15, long long i16, double f17, long long i18, double f19 ); + +int main() { + double result = addAll( 0, 1.1, 2, 3.1, 4, 5.1, 6, 7.1, 8, 9.1, 10, 11.1, 12, 13.1, 14, 15.1, 16, 17.1, 18, 19.1 ); + printf("%f\n", result); + return 0; +} diff --git a/src/test/java/com/seaofnodes/simple/progs/arg_count.smp b/src/test/java/com/seaofnodes/simple/progs/arg_count.smp new file mode 100644 index 00000000..6b6bc0ae --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/arg_count.smp @@ -0,0 +1,6 @@ +// -*- mode: java; -*- +val addAll = { int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, flt f7, int x8, flt f9, int i10, flt f11, int i12, flt f13, int i14, flt f15, int x16, flt f17 int x18, flt f19 -> + return + i0 + f1+ i2+ f3+ i4+ f5+ i6+ f7+ x8 +f9 + + i10+f11+i12+f13+i14+f15+x16+f17+x18+f19 ; +}; diff --git a/src/test/java/com/seaofnodes/simple/progs/array1.smp b/src/test/java/com/seaofnodes/simple/progs/array1.smp new file mode 100644 index 00000000..6b10f92c --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/array1.smp @@ -0,0 +1,9 @@ +// -*- mode: java; -*- +int[] !ary = new int[arg]; +// Fill [0,1,2,3,4,...] +for( int i=0; i<ary#; i++ ) + ary[i] = i; +// Fill [0,1,3,6,10,...] +for( int i=0; i<ary#-1; i++ ) + ary[i+1] += ary[i]; +return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 diff --git a/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c b/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c new file mode 100644 index 00000000..127cbe73 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c @@ -0,0 +1,18 @@ +#include <stdio.h> +#include <stdbool.h> + +typedef struct AryInt { + unsigned int len; + unsigned char data[]; +} AryInt; + +__attribute__ ((CALL_CONV)) +extern AryInt* brain_fuck(); + +int main() { + AryInt* ps = brain_fuck(); + + printf("%.*s", ps->len, ps->data); + + return 0; +} diff --git a/src/test/java/com/seaofnodes/simple/progs/brain_fuck.smp b/src/test/java/com/seaofnodes/simple/progs/brain_fuck.smp new file mode 100644 index 00000000..ed90cb84 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/brain_fuck.smp @@ -0,0 +1,46 @@ +// -*- mode: java; -*- +val brain_fuck = { -> + u8[] !program = new u8[106];program[0] = 43;program[1] = 43;program[2] = 43;program[3] = 43;program[4] = 43;program[5] = 43;program[6] = 43;program[7] = 43;program[8] = 91;program[9] = 62;program[10] = 43;program[11] = 43;program[12] = 43;program[13] = 43;program[14] = 91;program[15] = 62;program[16] = 43;program[17] = 43;program[18] = 62;program[19] = 43;program[20] = 43;program[21] = 43;program[22] = 62;program[23] = 43;program[24] = 43;program[25] = 43;program[26] = 62;program[27] = 43;program[28] = 60;program[29] = 60;program[30] = 60;program[31] = 60;program[32] = 45;program[33] = 93;program[34] = 62;program[35] = 43;program[36] = 62;program[37] = 43;program[38] = 62;program[39] = 45;program[40] = 62;program[41] = 62;program[42] = 43;program[43] = 91;program[44] = 60;program[45] = 93;program[46] = 60;program[47] = 45;program[48] = 93;program[49] = 62;program[50] = 62;program[51] = 46;program[52] = 62;program[53] = 45;program[54] = 45;program[55] = 45;program[56] = 46;program[57] = 43;program[58] = 43;program[59] = 43;program[60] = 43;program[61] = 43;program[62] = 43;program[63] = 43;program[64] = 46;program[65] = 46;program[66] = 43;program[67] = 43;program[68] = 43;program[69] = 46;program[70] = 62;program[71] = 62;program[72] = 46;program[73] = 60;program[74] = 45;program[75] = 46;program[76] = 60;program[77] = 46;program[78] = 43;program[79] = 43;program[80] = 43;program[81] = 46;program[82] = 45;program[83] = 45;program[84] = 45;program[85] = 45;program[86] = 45;program[87] = 45;program[88] = 46;program[89] = 45;program[90] = 45;program[91] = 45;program[92] = 45;program[93] = 45;program[94] = 45;program[95] = 45;program[96] = 45;program[97] = 46;program[98] = 62;program[99] = 62;program[100] = 43;program[101] = 46;program[102] = 62;program[103] = 43;program[104] = 43;program[105] = 46; + int d = 0; + u8[] !output = new u8[0]; + u8[] !data = new u8[100]; + + for( int pc = 0; pc < program#; pc++ ) { + var command = program[pc]; + if (command == 62) { + d++; + } else if (command == 60) { + d--; + } else if (command == 43) { + data[d]++; + } else if (command == 45) { + data[d]--; + } else if (command == 46) { + // Output a byte; increase the output array size + var old = output; + output = new u8[output# + 1]; + for( int i = 0; i < old#; i++ ) + output[i] = old[i]; + output[old#] = data[d]; // Add the extra byte on the end + } else if (command == 44) { + data[d] = 42; + } else if (command == 91) { + if (data[d] == 0) { + for( int d = 1; d > 0; ) { + command = program[++pc]; + if (command == 91) d++; + if (command == 93) d--; + } + } + } else if (command == 93) { + if (data[d]) { + for( int d = 1; d > 0; ) { + command = program[--pc]; + if (command == 93) d++; + if (command == 91) d--; + } + } + } + } + return output; +}; diff --git a/src/test/java/com/seaofnodes/simple/progs/fcn.smp b/src/test/java/com/seaofnodes/simple/progs/fcn.smp deleted file mode 100644 index 188e0a9f..00000000 --- a/src/test/java/com/seaofnodes/simple/progs/fcn.smp +++ /dev/null @@ -1,15 +0,0 @@ - -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 deleted file mode 100644 index 049132d9..00000000 --- a/src/test/java/com/seaofnodes/simple/progs/fcn2.smp +++ /dev/null @@ -1,5 +0,0 @@ - -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 deleted file mode 100644 index a64564f0..00000000 --- a/src/test/java/com/seaofnodes/simple/progs/fcn3.smp +++ /dev/null @@ -1,42 +0,0 @@ - -//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/fib.c b/src/test/java/com/seaofnodes/simple/progs/fib.c new file mode 100644 index 00000000..263d2f13 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/fib.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <stdbool.h> + + +__attribute__ ((CALL_CONV)) +extern int fib(int x); + +int main() { + printf("["); + for (int i = 0; i < 10; i++) { + printf("%d", fib(i)); + if (i < 9) printf(", "); + } + printf("]"); + return 0; +}
\ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/fib.smp b/src/test/java/com/seaofnodes/simple/progs/fib.smp new file mode 100644 index 00000000..1512541b --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/fib.smp @@ -0,0 +1,11 @@ +// -*- mode: java; -*- +val fib = {int n -> + int f1=1; + int f2=1; + while( n-- > 1 ){ + int temp = f1+f2; + f1=f2; + f2=temp; + } + return f2; +};
\ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/jig.smp b/src/test/java/com/seaofnodes/simple/progs/jig.smp new file mode 100644 index 00000000..00c2a161 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/jig.smp @@ -0,0 +1,9 @@ +// -*- mode: java; -*- +struct s0 { + i16 v1; + u8 v2; +}; +s0 !l4Hi9BCnsd = new s0; +if( new s0.v2+arg ) + while(1) l4Hi9BCnsd=new s0; +while(new s0.v2+new s0.v1<=0) {} diff --git a/src/test/java/com/seaofnodes/simple/progs/loop2.c b/src/test/java/com/seaofnodes/simple/progs/loop2.c new file mode 100644 index 00000000..934f2d89 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/loop2.c @@ -0,0 +1,7 @@ + +__attribute__ ((CALL_CONV)) +extern int loop2(); + +int main() { + return loop2(); +} diff --git a/src/test/java/com/seaofnodes/simple/progs/loop2.smp b/src/test/java/com/seaofnodes/simple/progs/loop2.smp new file mode 100644 index 00000000..01ec827e --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/loop2.smp @@ -0,0 +1,10 @@ +// -*- mode: java; -*- +int i = 0; +while(true) { + i += 1; + if( i==0 ) continue; + if( i==1 ) continue; + if( i==2 ) break; + continue; +} +return i; diff --git a/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c b/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c new file mode 100644 index 00000000..7e7e796d --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <math.h> +#include <stdlib.h> + +extern double test_sqrt(double); + +int main( int argc, char** argv ) { + for( int i=0; i<10; i++ ) { + double d = test_sqrt((double)i), expect = sqrt((double)i); + double delta = fabs(d-expect); + printf("%d %f (%g)\n",i,d,delta); + if( delta > 1e-15 ) + return 1; + } + return 0; +} diff --git a/src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp b/src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp new file mode 100644 index 00000000..be2dffec --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp @@ -0,0 +1,12 @@ +// -*- mode: java; -*- +// Newtons approximation to the square root +val test_sqrt = { flt x -> + flt epsilon = 1e-15; + flt guess = x; + while( 1 ) { + flt next = (x/guess + guess)/2; + if( guess-epsilon <= next & next <= guess+epsilon ) return guess; + //if( guess==next ) return guess; + guess = next; + } +}; diff --git a/src/test/java/com/seaofnodes/simple/progs/no_stack_arg_count.smp b/src/test/java/com/seaofnodes/simple/progs/no_stack_arg_count.smp new file mode 100644 index 00000000..20a05445 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/no_stack_arg_count.smp @@ -0,0 +1,6 @@ +// -*- mode: java; -*- +val addAll = { int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, flt f7, int x8, flt f9, int i10, flt f11, int i12, flt f13, int i14, flt f15 -> + return + i0 + f1+ i2+ f3+ i4+ f5+ i6+ f7+ x8 +f9 + + i10+f11+i12+f13+i14+f15; +}; diff --git a/src/test/java/com/seaofnodes/simple/progs/person.c b/src/test/java/com/seaofnodes/simple/progs/person.c new file mode 100644 index 00000000..df95e1ca --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/person.c @@ -0,0 +1,34 @@ +#include <stdio.h> +#include <stdbool.h> +#include <stdlib.h> + +typedef struct Person { + int age; +} Person; + +typedef struct PersonArray { + unsigned int length; + int pad; + Person* data[]; +} PersonArray; + +__attribute__ ((CALL_CONV)) +extern void fcn(PersonArray* people, int index); + +int main() { + Person base[5] = {{25},{1},{18},{45},{60}}; + unsigned char buf[8+8*5]; + PersonArray* people_array = (PersonArray*)buf; + people_array->length=5; + for (int i=0; i<people_array->length; i++) people_array->data[i] = &base[i]; + + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + fcn(people_array, 1); + + printf("%d\n", people_array->data[1]->age); // expected: 6 + + return 0; +}
\ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/person.smp b/src/test/java/com/seaofnodes/simple/progs/person.smp new file mode 100644 index 00000000..9f0f5bea --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/person.smp @@ -0,0 +1,9 @@ +// -*-java-*- +struct Person { + int age; +}; + +val fcn = { Person?[] ps, int x -> + if( ps[x] ) + ps[x].age++; +};
\ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/sieve.c b/src/test/java/com/seaofnodes/simple/progs/sieve.c new file mode 100644 index 00000000..cac4cfa3 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sieve.c @@ -0,0 +1,19 @@ +#include <stdio.h> +#include <stdbool.h> + +typedef struct AryInt { + unsigned int len; + unsigned int data[]; +} AryInt; + +__attribute__ ((CALL_CONV)) +extern AryInt *sieve(int N); + +int main() { + AryInt* ps = sieve(100); + printf("%d[",ps->len); + for( int i=0; i<ps->len; i++ ) + printf("%d, ",ps->data[i]); + printf("]"); + return 0; +} diff --git a/src/test/java/com/seaofnodes/simple/progs/sieve.smp b/src/test/java/com/seaofnodes/simple/progs/sieve.smp index 0788e6c5..569dbb96 100644 --- a/src/test/java/com/seaofnodes/simple/progs/sieve.smp +++ b/src/test/java/com/seaofnodes/simple/progs/sieve.smp @@ -1,32 +1,31 @@ - -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; +// -*- mode: java; -*- +val sieve = { int N -> + // The main Sieve array + bool[] !ary = new bool[N]; + // The primes less than N + u32[] !primes = new u32[N>>1]; + // Number of primes so far, searching at index p + int nprimes = 0, p=2; + // Find primes while p^2 < N + while( p*p < N ) { + // 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; + p++; } - 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; + + // Now just collect the remaining primes, no more marking + for( ; p < N; p++ ) + if( !ary[p] ) + primes[nprimes++] = p; + + // Copy/shrink the result array + u32[] !rez = new u32[nprimes]; + for( int j=0; j < nprimes; j++ ) + rez[j] = primes[j]; + return rez; +}; diff --git a/src/test/java/com/seaofnodes/simple/progs/sort.c b/src/test/java/com/seaofnodes/simple/progs/sort.c new file mode 100644 index 00000000..ad76cc7f --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sort.c @@ -0,0 +1,28 @@ +#include <stdio.h> +#include <stdlib.h> + +// Test wrapper to call `sort.smp` + +typedef struct AryInt { + unsigned int len; + long long data[]; +} AryInt; + +__attribute__ ((CALL_CONV)) +extern AryInt* merge_sort(AryInt*); + + +long long primes[] = { 25, 97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2 }; + +int main() { + AryInt *as = (AryInt*)primes; + + merge_sort(as); + + printf("%d[",as->len); + for( int i=0; i<as->len; i++ ) + printf("%d, ",as->data[i]); + printf("]"); + + return 0; +}
\ No newline at end of file diff --git a/src/test/java/com/seaofnodes/simple/progs/sort.smp b/src/test/java/com/seaofnodes/simple/progs/sort.smp new file mode 100644 index 00000000..ae3f4f23 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/sort.smp @@ -0,0 +1,38 @@ +// -*- mode: java; -*- +// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort + +val merge_sort = { int[] a -> + int[] !b = new int[a#]; + copy_array (a, 0, a#, b); + split_merge(a, 0, a#, b); +}; + +val split_merge = { int[] b, int begin, int end, int[] a -> + if (end - begin <= 1) + return 0; + int middle = (end + begin) / 2; + split_merge(a, begin, middle, b); + split_merge(a, middle, end, b); + merge(b, begin, middle, end, a); + return 0; +}; + +val merge = { int[] b, int begin, int middle, int end, int[] a -> + int i = begin, j = middle; + + for (int k = begin; k < end; k++) { + // && and || + bool cond = false; + if (i < middle) { + if (j >= end) cond = true; + else if (a[i] <= a[j]) cond = true; + } + if (cond) b[k] = a[i++]; + else b[k] = a[j++]; + } +}; + +val copy_array = { int[] a, int begin, int end, int[] b -> + for (int k = begin; k < end; k++) + b[k] = a[k]; +}; diff --git a/src/test/java/com/seaofnodes/simple/progs/stringHash.c b/src/test/java/com/seaofnodes/simple/progs/stringHash.c new file mode 100644 index 00000000..2c6d0fa9 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/stringHash.c @@ -0,0 +1,30 @@ +#include <stdio.h> +#include <stdbool.h> + +typedef struct ArrayU8 { + unsigned int length; + unsigned char data[]; +} ArrayU8; +typedef struct String { + ArrayU8 * cs; + long long _hashCode; +} String; + + +__attribute__ ((CALL_CONV)) +extern bool equals(String*st1, String*st2); + +int main() { + String test1 = {.cs=(ArrayU8*)"\x04\x00\x00\x00test"}; + String test2 = {.cs=(ArrayU8*)"\x05\x00\x00\x00test1"}; + + if( equals(&test1, &test2) ) { + printf("Expected test1 and test2 unequal"); + return 1; + } + if( !equals(&test2, &test2) ) { + printf("Expected test2 and test2 equal"); + return 1; + } + return 0; +} diff --git a/src/test/java/com/seaofnodes/simple/progs/stringHash.smp b/src/test/java/com/seaofnodes/simple/progs/stringHash.smp new file mode 100644 index 00000000..dafa7456 --- /dev/null +++ b/src/test/java/com/seaofnodes/simple/progs/stringHash.smp @@ -0,0 +1,31 @@ +// -*- mode: java; -*- +struct String { + u8[] cs; + int _hashCode; +}; + +// Compare two Strings +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; +}; + +// Return the String hashCode (cached, and never 0) +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; +}; |
