summaryrefslogtreecommitdiff
path: root/chapter27/src/test/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'chapter27/src/test/java/com')
-rw-r--r--chapter27/src/test/java/com/seaofnodes/simple/NarrowFieldTest.java89
-rw-r--r--chapter27/src/test/java/com/seaofnodes/simple/X86StoreNativeTest.java50
-rw-r--r--chapter27/src/test/java/com/seaofnodes/simple/X86StoreTest.java35
3 files changed, 174 insertions, 0 deletions
diff --git a/chapter27/src/test/java/com/seaofnodes/simple/NarrowFieldTest.java b/chapter27/src/test/java/com/seaofnodes/simple/NarrowFieldTest.java
new file mode 100644
index 00000000..f6cc2e4b
--- /dev/null
+++ b/chapter27/src/test/java/com/seaofnodes/simple/NarrowFieldTest.java
@@ -0,0 +1,89 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
+import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.cpus.riscv.riscv;
+import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.isa.eval.EvalRisc5;
+import org.junit.Test;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import static org.junit.Assert.*;
+
+public class NarrowFieldTest {
+ private static final String[] TYPES = {"u8", "i8", "u16", "i16", "u32", "i32"};
+ private static final long[] MINS = {0, -128, 0, -32768, 0, Integer.MIN_VALUE};
+ private static final long[] MAXS = {255, 127, 65535, 32767, 0xffffffffL, Integer.MAX_VALUE};
+
+ // Both the method result and a later read must see the stored, narrowed value.
+ private static String source(int i, boolean increment, boolean argument) {
+ String name = "C"+i+(increment ? "Inc" : "Dec");
+ long initial = argument ? 0 : (increment ? MAXS[i] : MINS[i]);
+ return "struct "+name+" { "+TYPES[i]+" x="+initial+"; "
+ +"val next_noInline={ -> x"+(increment ? "++" : "--")+"; return x; }; }; "
+ +"var c=new "+name+"; "+(argument ? "c.x=arg; " : "")
+ +"val a=c.next_noInline(); val b=c.next_noInline(); "
+ +"return a+10*b+100*c.x;";
+ }
+
+ private static long expected(int i, boolean increment) {
+ long first = increment ? MINS[i] : MAXS[i];
+ long second = first+(increment ? 1 : -1);
+ return first+110*second;
+ }
+
+ @Test public void testMethodResults() {
+ for( int i = 0; i<TYPES.length; i++ )
+ for( boolean increment : new boolean[]{true,false} )
+ for( int seed : new int[]{2,13,123} ) {
+ String src = source(i,increment,false);
+ CodeGen code = new CodeGen(src,TypeInteger.BOT,seed,true).driver(CodeGen.Phase.TypeCheck);
+ assertEquals(src,Long.toString(expected(i,increment)),Eval2.eval(code,0));
+ }
+ }
+
+ @Test public void testNativeMethodResults() throws IOException {
+ StringBuilder src = new StringBuilder();
+ StringBuilder declarations = new StringBuilder();
+ StringBuilder checks = new StringBuilder();
+ // Supply the initial value through the native argument to also exercise
+ // narrowing an unknown full-width value before the method calls.
+ for( int i = 0; i<TYPES.length; i++ )
+ for( boolean increment : new boolean[]{true,false} ) {
+ String name = "narrow"+i+(increment ? "Inc" : "Dec");
+ src.append("val ").append(name).append("={int arg -> ")
+ .append(source(i,increment,true)).append(" }; ");
+ declarations.append("__attribute__((CALL_CONV)) extern long long ").append(name).append("(long long);\n");
+ checks.append("if (").append(name).append('(').append(increment ? MAXS[i] : MINS[i])
+ .append("LL)!=").append(expected(i,increment)).append("LL) return 1;\n");
+ }
+ Path dir = Path.of("build/objs");
+ Files.createDirectories(dir);
+ Path driver = dir.resolve("narrowField.c");
+ Files.writeString(driver,declarations+"int main(void) {\n"+checks+"return 0; }\n");
+ String abi = TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi";
+ TestC.run(src.toString(),TestC.CALL_CONVENTION,null,abi,driver.toString(),"build/objs/narrowField","S","",-1);
+ }
+
+ @Test public void testRiscvMethodResults() throws IOException {
+ for( int i = 0; i<TYPES.length; i++ )
+ for( boolean increment : new boolean[]{true,false} ) {
+ String src = source(i,increment,false);
+ CodeGen code = new CheckedCodeGen(src).driver("riscv","SystemV",null);
+ int entry = -1;
+ for( var node : code._start._outputs )
+ if( node instanceof FunNode fun && "main".equals(fun._name) )
+ entry = code._encoding._opStart[fun._nid];
+ assertTrue(entry>=0);
+ byte[] image = new byte[1<<20];
+ byte[] bits = code._encoding.bits();
+ System.arraycopy(bits,0,image,0,bits.length);
+ EvalRisc5 cpu = new EvalRisc5(image,1<<16);
+ cpu._pc = entry;
+ assertEquals(src,0,cpu.step(100000));
+ assertEquals(src,expected(i,increment),cpu.regs[riscv.A0]);
+ }
+ }
+}
diff --git a/chapter27/src/test/java/com/seaofnodes/simple/X86StoreNativeTest.java b/chapter27/src/test/java/com/seaofnodes/simple/X86StoreNativeTest.java
new file mode 100644
index 00000000..f0c9d37f
--- /dev/null
+++ b/chapter27/src/test/java/com/seaofnodes/simple/X86StoreNativeTest.java
@@ -0,0 +1,50 @@
+package com.seaofnodes.simple;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import org.junit.Test;
+
+public class X86StoreNativeTest {
+ @Test public void testConstantStoresAndAdds() throws IOException {
+ Path dir = Path.of("build/objs");
+ Files.createDirectories(dir);
+ Files.writeString(dir.resolve("x86ConstantStore.smp"), """
+ struct Narrow { u32 x; u32 guard; };
+ struct Wide { int x; int guard; };
+ val storeNarrow = { !Narrow s -> s.x=4294967295; return 0; };
+ val storePositive = { !Wide s -> s.x=81985529216486895; return 0; };
+ val storeNegative = { !Wide s -> s.x=-2147483649; return 0; };
+ val addPositive = { !Wide s -> s.x+=4294967295; return 0; };
+ val addNegative = { !Wide s -> s.x+=-2147483649; return 0; };
+ """);
+ // Read from C so load forwarding cannot hide an incorrectly encoded Store.
+ Files.writeString(dir.resolve("x86ConstantStore.c"), """
+ #include <stdint.h>
+ struct Narrow { uint32_t x, guard; };
+ struct Wide { int64_t x, guard; };
+ extern long long storeNarrow(struct Narrow *);
+ extern long long storePositive(struct Wide *);
+ extern long long storeNegative(struct Wide *);
+ extern long long addPositive(struct Wide *);
+ extern long long addNegative(struct Wide *);
+ int main(void) {
+ struct Narrow n = {0, 0x12345678};
+ struct Wide w = {0, 0x123456789abcdefLL};
+ storeNarrow(&n);
+ if (n.x != UINT32_MAX || n.guard != 0x12345678) return 1;
+ storePositive(&w);
+ if (w.x != 81985529216486895LL || w.guard != 0x123456789abcdefLL) return 2;
+ storeNegative(&w);
+ if (w.x != -2147483649LL || w.guard != 0x123456789abcdefLL) return 3;
+ w.x = 7;
+ addPositive(&w);
+ if (w.x != 4294967302LL || w.guard != 0x123456789abcdefLL) return 4;
+ addNegative(&w);
+ if (w.x != 2147483653LL || w.guard != 0x123456789abcdefLL) return 5;
+ return 0;
+ }
+ """);
+ TestC.run("build/objs","x86ConstantStore","",-1);
+ }
+}
diff --git a/chapter27/src/test/java/com/seaofnodes/simple/X86StoreTest.java b/chapter27/src/test/java/com/seaofnodes/simple/X86StoreTest.java
new file mode 100644
index 00000000..c07ac904
--- /dev/null
+++ b/chapter27/src/test/java/com/seaofnodes/simple/X86StoreTest.java
@@ -0,0 +1,35 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.StoreX86;
+import com.seaofnodes.simple.type.TypeInteger;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class X86StoreTest {
+ @Test public void testConstantStores() {
+ check("u8",255,false);
+ check("u16",65535,false);
+ check("u32",Integer.MAX_VALUE,false);
+ check("u32",2147483648L,true);
+ check("u32",4294967295L,true);
+ check("int",Integer.MIN_VALUE,false);
+ check("int",Integer.MAX_VALUE,false);
+ for( long value : new long[]{2147483648L,4294967295L,-2147483649L,
+ 0x123456789abcdefL,Long.MIN_VALUE,Long.MAX_VALUE} )
+ check("int",value,true);
+ }
+
+ private static void check(String type, long value, boolean register) {
+ // Returning the object keeps the Store; returning its field could fold.
+ String literal = value==Long.MIN_VALUE ? "(-9223372036854775807-1)" : Long.toString(value);
+ String src = "struct S { "+type+" x="+literal+"; }; return new S;";
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Select,"x86_64_v2","SystemV");
+ StoreX86 store = code._stop.walk(n -> n instanceof StoreX86 st && st._name.equals("x") ? st : null);
+ assertNotNull(src,store);
+ if( register ) {
+ assertNotNull(src,store.in(4));
+ assertEquals(src,value,((TypeInteger)store.in(4)._type).value());
+ } else assertNull(src,store.in(4));
+ }
+}