diff options
Diffstat (limited to 'chapter21/src/test/java')
3 files changed, 270 insertions, 16 deletions
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java index d1db1609..1d37deb0 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -1,6 +1,7 @@ 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; @@ -94,10 +95,12 @@ public class Chapter21Test { // Return register A0 holds fib(8)==55 assertEquals(1.732051,R5.fregs[riscv.FA0 - riscv.F_OFFSET], 0.00001); - // TODO: Run on ARM eval - 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,"arm", "SystemV",19,null); + // arm + EvalArm64 A5 = TestArm64.build("newtonFloat", 0, 10); + 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 { @@ -134,9 +137,12 @@ public class Chapter21Test { // Return register A0 holds fib(8)==55 assertEquals(55,R5.regs[riscv.A0]); - // TODO: run on arm eval - String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/fib.smp")); - testCPU(src,"arm" , "SystemV",16,null); + // arm + EvalArm64 A5 = TestArm64.build("fib", 9, 16); + 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 { @@ -186,6 +192,7 @@ public class Chapter21Test { 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; @@ -198,7 +205,33 @@ public class Chapter21Test { assertEquals(0,trap); double result = R5.fregs[riscv.FA0 - riscv.F_OFFSET]; - // missing a float + assertEquals(22.8, result, 0.00001); + + // arm + EvalArm64 A5 = TestArm64.build("no_stack_arg_count", 0, 0); + + 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/chapter21/src/test/java/com/seaofnodes/simple/EvalArm64.java b/chapter21/src/test/java/com/seaofnodes/simple/EvalArm64.java index 55a16f84..370745dd 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/EvalArm64.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/EvalArm64.java @@ -17,6 +17,12 @@ public class EvalArm64 { // 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]; @@ -26,6 +32,24 @@ public class EvalArm64 { _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; @@ -50,15 +74,205 @@ public class EvalArm64 { } // Load instruction from image buffer at PC - //ir = ld4s( 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 ; + } + } + // for the opcode we just match for bits[31:24]; + switch(opcode1) { + case 0xD2: { + // movz + // get immediate + rval = (ir >> 5) & 0x7FFF; + 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; + int lhs = (int)regs[rn]; + int rhs = (int)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; + int lhs = (int)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; + 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: frval = fregs[rs1] / fregs[rs2]; break; // fdiv + + 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; + } - //int rdid = ir & 0x1f; - //switch(ir & ) + pc += 4; } + // Handle traps and interrupts. if(trap != 0) { return trap; } - _cycle = _cycle; + _cycle = cycle; _pc = pc; return 0; } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/TestArm64.java b/chapter21/src/test/java/com/seaofnodes/simple/TestArm64.java index 1325f1e1..874beca8 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/TestArm64.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/TestArm64.java @@ -8,26 +8,33 @@ 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) throws IOException { - return build("src/test/java/com/seaofnodes/simple/progs",file, arg); + public static EvalArm64 build(String file, int arg, int spills) throws IOException { + return build("src/test/java/com/seaofnodes/simple/progs",file, arg, spills); } // Compile and run a simple program - public static EvalArm64 build(String dir, String file, int arg) throws IOException { + public static EvalArm64 build(String dir, String file, int arg, int spills) throws IOException { // Compile and export Simple String src = Files.readString(Path.of(dir+"/"+file+".smp")); CodeGen code = new CodeGen(src).driver("arm", "SystemV",null); + // 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.D0] = arg; + ARM.regs[arm.X0] = arg; // malloc memory starts at 64K and goes up return ARM; } |
