From e30990ad8929084cf433894843adc04b577d6e9f Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Sun, 6 Apr 2025 10:37:16 -0700 Subject: End-to-end testing of x86 encoding --- .gitignore | 1 + .../java/com/seaofnodes/simple/Chapter19Test.java | 10 ++-- .../java/com/seaofnodes/simple/Chapter20Test.java | 6 +-- .../java/com/seaofnodes/simple/Chapter21Test.java | 32 +++++++---- .../src/test/java/com/seaofnodes/simple/TestC.java | 63 ++++++++++++++++++++++ .../java/com/seaofnodes/simple/progs/newtonFloat.c | 17 ++++++ .../java/com/seaofnodes/simple/progs/stringHash.c | 29 ++++++++++ .../com/seaofnodes/simple/progs/stringHash.smp | 5 -- 8 files changed, 140 insertions(+), 23 deletions(-) create mode 100644 chapter21/src/test/java/com/seaofnodes/simple/TestC.java create mode 100644 chapter21/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c create mode 100644 chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.c diff --git a/.gitignore b/.gitignore index 45ae6585..0ccb7896 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ TAGS # Libs */lib/*jar +Unicorn/ diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java index a8850630..6425fb80 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java @@ -26,8 +26,8 @@ return 0; public void testString() throws IOException { String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp")); CodeGen code = new CodeGen(src).parse().opto().typeCheck().GCM().localSched(); - assertEquals("Stop[ return (#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)); + 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 @@ -151,9 +151,9 @@ return arg1 * arg; @Test public void testToFloat() { CodeGen code = new CodeGen(""" - int a = arg; - return a + 2.0; - """ +int a = arg; +return a + 2.0; +""" ).parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); assertEquals("return (addf,(cvtf,arg),2.0f);", code._stop.toString()); } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java index 69179043..004e2af3 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java @@ -101,9 +101,9 @@ return sqrt(arg) + sqrt(arg+2); @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",20,null); - testCPU(src,"arm" , "SystemV",16,null); + testCPU(src,"x86_64_v2", "SystemV", 1,null); + testCPU(src,"riscv" , "SystemV", 5,null); + testCPU(src,"arm" , "SystemV", 6,null); } @Test diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java index 5f68d0e3..dd38215c 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -52,14 +52,33 @@ public class Chapter21Test { testCPU(src,"arm" , "SystemV",19,null); } + @Test public void testNewtonExport() throws Exception { + String result = """ +0 0.000000 +1 1.000000 +2 1.414214 +3 1.732051 +4 2.000000 +5 2.236068 +6 2.449490 +7 2.645751 +8 2.828427 +9 3.000000 +"""; + TestC.run("newtonFloat",result); + } + @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",18,null); - testCPU(src,"arm" , "SystemV",17,null); + testCPU(src,"x86_64_v2", "SystemV", 1,null); + testCPU(src,"riscv" , "SystemV", 5,null); + testCPU(src,"arm" , "SystemV", 6,null); } + @Test + public void testStringExport() throws Exception { TestC.run("stringHash"); } + @Test public void testArray1() throws IOException { String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/array1.smp")); @@ -76,13 +95,6 @@ public class Chapter21Test { testCPU(src,"arm" , "SystemV",16,null); } - @Test - public void testExport() throws IOException { - String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp")); - CodeGen code = new CodeGen(src); - code.parse().opto().typeCheck().instSelect( "x86_64_v2", "SystemV").GCM().localSched().regAlloc().encode().exportELF("build/objs/newton.o"); - } - // Read test case from disk. // TODO: exportELF not handling external calls yet diff --git a/chapter21/src/test/java/com/seaofnodes/simple/TestC.java b/chapter21/src/test/java/com/seaofnodes/simple/TestC.java new file mode 100644 index 00000000..121ace03 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/TestC.java @@ -0,0 +1,63 @@ +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 ) throws Exception { run(file,""); } + + public static void run( String file, String expected ) throws Exception { + run("src/test/java/com/seaofnodes/simple/progs",file,expected); + } + + // Compile and run a simple program + public static void run( String dir, String file, String expected ) throws IOException { + // Files + String cfile = dir+"/"+file+".c" ; + String sfile = dir+"/"+file+".smp"; + String sofile = "build/objs/"+file+"S.o"; + String efile = "build/objs/"+file; + + // Compile and export Simple + String src = Files.readString(Path.of(sfile)); + CodeGen code = new CodeGen(src).parse().opto().typeCheck().instSelect( CPU_PORT, CALL_CONVENTION).GCM().localSched().regAlloc().encode().exportELF(sofile); + + // Compile the C program + var params = new Ary<>(String.class); + //if (USE_WSL) params.add("wsl.exe"); + params.add("gcc"); + params.add(cfile); + params.add(sofile); + params.add("-o"); + params.add(efile); + Process gcc = new ProcessBuilder(params.toArray(String[]::new)).redirectErrorStream(true).start(); + byte error; + try { error = (byte)gcc.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); } + String result = new String(gcc.getInputStream().readAllBytes()); + assertEquals( 0, error ); + assertTrue(result.isEmpty()); // No data in error stream + + // Execute results + Process smp = new ProcessBuilder(efile).redirectErrorStream(true).start(); + try { error = (byte)smp.waitFor(); } catch( InterruptedException e ) { throw new IOException("interrupted"); } + result = new String(smp.getInputStream().readAllBytes()); + assertEquals( 0, error ); + assertEquals(expected,result); + } +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c b/chapter21/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c new file mode 100644 index 00000000..25ac7930 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c @@ -0,0 +1,17 @@ +#include +#include +#include + +extern double test_sqrt(double); + +int main( int argc, char** argv ) { + double epsilon = 1e-50; + for( int i=0; i<10; i++ ) { + double d = sqrt(i), expect = sqrt(i); + double delta = abs(d-expect); + printf("%d %f\n",i,d); + if( delta > 1e-50 ) + return 1; + } + return 0; +} diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.c b/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.c new file mode 100644 index 00000000..e0704f74 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.c @@ -0,0 +1,29 @@ +#include +#include + +typedef struct ArrayU8 { + unsigned int length; + unsigned char data[]; +} ArrayU8; +typedef struct String { + ArrayU8 * cs; + long long _hashCode; +} String; + + +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/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.smp index 455e0014..dafa7456 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.smp +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/stringHash.smp @@ -29,8 +29,3 @@ val _hashCodeString = { String self -> 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); -- cgit v1.2.3