summaryrefslogtreecommitdiff
path: root/chapter24/src/test/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'chapter24/src/test/java/com')
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter21Test.java17
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter22Test.java73
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/EvalArm64.java21
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/progs/c_returns.c28
4 files changed, 127 insertions, 12 deletions
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index afa3ce6a..dcdda33b 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -13,6 +13,23 @@ import org.junit.Test;
import static org.junit.Assert.*;
public class Chapter21Test {
+
+ @Test public void testArmBitfieldExtracts() {
+ // Independent SXT/UXT instruction words, plus ASR, LSR and LSL aliases.
+ int[] ops = {0x93401c20,0xd3401c20,0x93403c20,0xd3403c20,
+ 0x93407c20,0xd3407c20,0x9344fc20,0xd344fc20,0xd37cec20};
+ long value = 0xa5a5a5a580008080L;
+ long[] expected = {(byte)value,value&255,(short)value,value&65535,
+ (int)value,value&0xffffffffL,value>>4,value>>>4,value<<4};
+ for( int i=0; i<ops.length; i++ ) {
+ EvalArm64 cpu = new EvalArm64(new byte[1<<16],1<<15);
+ cpu.st4(0,ops[i]); cpu.regs[1]=value;
+ assertEquals(0,cpu.step(1));
+ assertEquals(expected[i],cpu.regs[0]);
+ }
+ }
+
+
@Test public void testReturnedFunctionEncoding() throws IOException {
String src = "if(arg) return {->42;}; return {->43;};";
CodeGen code = new CodeGen(src).driver("riscv","SystemV",null);
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter22Test.java
index 6571b4f9..b1b5ad3e 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter22Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter22Test.java
@@ -14,6 +14,79 @@ import static org.junit.Assert.*;
public class Chapter22Test {
+ @Test public void testNarrowCReturnsEmulated() {
+ String[] types = {"i8","u8","i16","u16","i32","u32","int"};
+ long[] values = {0xa5a5a5a500000000L,0xa5a5a5a50000007fL,0xa5a5a5a500000080L,
+ 0xa5a5a5a500007fffL,0xa5a5a5a500008000L,0xa5a5a5a57fffffffL,
+ 0xa5a5a5a580000000L,0xa5a5a5a5ffffffffL};
+ for( String target : new String[]{"arm","riscv"} )
+ for( int i=0; i<types.length; i++ ) {
+ String src = "{"+types[i]+"} c_narrow=\"C\"; return c_narrow();";
+ CodeGen code = new CheckedCodeGen(src).driver(CodeGen.Phase.Encoding,target,"SystemV");
+ var enc = code._encoding;
+ // Link the external call to a tiny independent native stub.
+ var call = code._stop.walk(n -> n instanceof com.seaofnodes.simple.node.CallNode ? n : null);
+ int start = enc._opStart[call._nid];
+ ((com.seaofnodes.simple.codegen.RIPRelSize)call).patch(enc,start,enc._opLen[call._nid],0x1000-start);
+ for( long value : values ) {
+ long expected = switch(i) {
+ case 0 -> (byte)value; case 1 -> value&255;
+ case 2 -> (short)value; case 3 -> value&65535;
+ case 4 -> (int)value; case 5 -> value&0xffffffffL;
+ default -> value;
+ };
+ byte[] image = new byte[1<<16];
+ byte[] bits = enc.bits();
+ System.arraycopy(bits,0,image,0,bits.length);
+ if( target.equals("arm") ) {
+ EvalArm64 cpu = new EvalArm64(image,1<<15);
+ // MOVZ X0,0x1100; LDR X0,[X0]; RET X30.
+ cpu._pc = enc._opStart[code.link(code._main)._nid];
+ cpu.st4(0x1000,0xd2822000); cpu.st4(0x1004,0xf9400000);
+ cpu.st4(0x1008,0xd65f03c0);
+ cpu.st8(0x1100,value);
+ assertEquals(0,cpu.step(1000));
+ assertEquals(0,cpu._pc);
+ assertEquals(types[i],expected,cpu.regs[0]);
+ } else {
+ EvalRisc5 cpu = new EvalRisc5(image,1<<15);
+ // AUIPC a0,0; LD a0,12(a0); RET.
+ cpu._pc = enc._opStart[code.link(code._main)._nid];
+ cpu.st4(0x1000,0x00000517); cpu.st4(0x1004,0x00c53503);
+ cpu.st4(0x1008,0x00008067);
+ // RV64 widens even u32 returns by sign-extending bit 31.
+ cpu.st8(0x100c,i==6 ? value : (int)expected);
+ assertEquals(0,cpu.step(1000));
+ assertEquals(0,cpu._pc);
+ assertEquals(types[i],expected,cpu.regs[riscv.A0]);
+ }
+ }
+ }
+ }
+
+ @Test public void testNarrowCReturns() throws IOException {
+ String src = """
+ {i8} c_i8="C"; {u8} c_u8="C";
+ {i16} c_i16="C"; {u16} c_u16="C";
+ {i32} c_i32="C"; {u32} c_u32="C";
+ {int} c_i64="C";
+ val test_i8={->return c_i8();};
+ val test_u8={->return c_u8();};
+ val test_i16={->return c_i16();};
+ val test_u16={->return c_u16();};
+ val test_i32={->return c_i32();};
+ val test_u32={->return c_u32();};
+ val test_i64={->return c_i64();};
+ val test_cmp={->return c_i32()==-1;};
+ """;
+ for( String conv : new String[]{"SystemV","Win64"} )
+ TestC.run(src,conv,com.seaofnodes.simple.type.TypeInteger.BOT,conv.equals("SystemV") ? "sysv_abi" : "ms_abi",
+ "src/test/java/com/seaofnodes/simple/progs/c_returns.c",
+ "build/objs/c_returns"+conv,"S","",-1);
+ }
+
+
+
@Test public void testReturnedFunctions() throws IOException {
for( String src : new String[]{"return {->42;};", "val f={->42;}; return f;", "return sys.io.p;"} )
for( String target : new String[]{"riscv","arm"} ) {
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/EvalArm64.java b/chapter24/src/test/java/com/seaofnodes/simple/EvalArm64.java
index 5dc56ef7..bd425908 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/EvalArm64.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/EvalArm64.java
@@ -320,11 +320,15 @@ public class EvalArm64 {
rval = regs[rn] & immediate;
break;
}
- case 0x93: {
- // asr(immediate)
- int imm = (ir >> 16) & 0x3F;
- int rn = (ir >> 5) & 0x1F;
- rval = regs[rn] >> (imm);
+ case 0x93, 0xD3: { // SBFM/UBFM: shifts and signed/unsigned extracts
+ int immr = (ir >>> 16) & 63;
+ int imms = (ir >>> 10) & 63;
+ int rn = (ir >>> 5) & 31;
+ boolean signed = (ir & 0x40000000)==0;
+ long value = regs[rn] << (63-imms);
+ value = signed ? value >> (63-imms) : value >>> (63-imms);
+ rval = immr > imms ? value << (64-immr)
+ : signed ? value >> immr : value >>> immr;
break;
}
@@ -573,13 +577,6 @@ public class EvalArm64 {
break;
}
- case 0xD3: {
- // lsl(immediate)
- int imm = (ir >> 16) & 0x3F;
- int rn = (ir >> 5) & 0x1F;
- rval = regs[rn] << (64 - imm);
- break;
- }
case 0xD6: {
int op = ir & 0xFFFFFC1F;
int rn = (ir >>> 5) & 31;
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/progs/c_returns.c b/chapter24/src/test/java/com/seaofnodes/simple/progs/c_returns.c
new file mode 100644
index 00000000..6d0ff2fe
--- /dev/null
+++ b/chapter24/src/test/java/com/seaofnodes/simple/progs/c_returns.c
@@ -0,0 +1,28 @@
+#include <stdint.h>
+#include <stdio.h>
+
+uint64_t c_bits;
+// Legal narrow C returns: unused RAX bits deliberately contain garbage.
+#define RET(name) __asm__(".text\n.globl " #name "\n" #name ":\nmovq c_bits(%rip), %rax\nret\n");
+RET(c_i8) RET(c_u8) RET(c_i16) RET(c_u16) RET(c_i32) RET(c_u32) RET(c_i64)
+#define DECL(name) extern int64_t __attribute__((CALL_CONV)) name(void);
+DECL(test_i8) DECL(test_u8) DECL(test_i16) DECL(test_u16)
+DECL(test_i32) DECL(test_u32) DECL(test_i64) DECL(test_cmp)
+
+int main(void) {
+ uint64_t values[] = {0xa5a5a5a500000000ULL, 0xa5a5a5a50000007fULL,
+ 0xa5a5a5a500000080ULL, 0xa5a5a5a500007fffULL, 0xa5a5a5a500008000ULL,
+ 0xa5a5a5a57fffffffULL, 0xa5a5a5a580000000ULL, 0xa5a5a5a5ffffffffULL};
+ for(unsigned i=0; i<sizeof(values)/sizeof(values[0]); i++) {
+ c_bits=values[i];
+ int64_t actual[]={test_i8(),test_u8(),test_i16(),test_u16(),test_i32(),test_u32(),test_i64(),test_cmp()};
+ int64_t expected[]={(int8_t)c_bits,(uint8_t)c_bits,(int16_t)c_bits,(uint16_t)c_bits,
+ (int32_t)c_bits,(uint32_t)c_bits,(int64_t)c_bits,(int32_t)c_bits==-1};
+ for(unsigned j=0; j<8; j++)
+ if(actual[j]!=expected[j]) {
+ printf("case %u type %u: %lld != %lld\n",i,j,(long long)actual[j],(long long)expected[j]);
+ return 1;
+ }
+ }
+ return 0;
+}