From 5dd5b7a2d03885cb767bbf2b609d56f8ef897301 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Thu, 1 Oct 2026 14:58:31 -0700 Subject: Move much of the ISA support to shared code The ISA shared handlers here are required for a proper compiler, but contain only trivial bit-massaging operations. --- .../java/com/seaofnodes/simple/BrainFuckTest.java | 4 + .../java/com/seaofnodes/simple/Chapter21Test.java | 4 + .../java/com/seaofnodes/simple/Chapter22Test.java | 4 + .../test/java/com/seaofnodes/simple/EvalArm64.java | 765 --------------------- .../test/java/com/seaofnodes/simple/EvalRisc5.java | 367 ---------- .../com/seaofnodes/simple/MemoryEncodingTest.java | 4 + .../java/com/seaofnodes/simple/MergeSortTest.java | 2 + .../test/java/com/seaofnodes/simple/TestArm64.java | 2 + .../test/java/com/seaofnodes/simple/TestRisc5.java | 2 + 9 files changed, 22 insertions(+), 1132 deletions(-) delete mode 100644 chapter22/src/test/java/com/seaofnodes/simple/EvalArm64.java delete mode 100644 chapter22/src/test/java/com/seaofnodes/simple/EvalRisc5.java (limited to 'chapter22/src/test/java/com/seaofnodes') diff --git a/chapter22/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/chapter22/src/test/java/com/seaofnodes/simple/BrainFuckTest.java index bf809d2a..9f626805 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/BrainFuckTest.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/BrainFuckTest.java @@ -1,5 +1,9 @@ package com.seaofnodes.simple; +import com.seaofnodes.isa.eval.EvalRisc5; + +import com.seaofnodes.isa.eval.EvalArm64; + import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.cpus.arm.arm; import com.seaofnodes.simple.node.cpus.riscv.riscv; diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java index df58f32a..926acf93 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -1,5 +1,9 @@ package com.seaofnodes.simple; +import com.seaofnodes.isa.eval.EvalRisc5; + +import com.seaofnodes.isa.eval.EvalArm64; + import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.cpus.arm.arm; diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java index 5c6dc601..2494ed2c 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java @@ -1,5 +1,9 @@ package com.seaofnodes.simple; +import com.seaofnodes.isa.eval.EvalRisc5; + +import com.seaofnodes.isa.eval.EvalArm64; + import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen; import com.seaofnodes.simple.codegen.CodeGen; import java.io.IOException; diff --git a/chapter22/src/test/java/com/seaofnodes/simple/EvalArm64.java b/chapter22/src/test/java/com/seaofnodes/simple/EvalArm64.java deleted file mode 100644 index 0a3e9196..00000000 --- a/chapter22/src/test/java/com/seaofnodes/simple/EvalArm64.java +++ /dev/null @@ -1,765 +0,0 @@ -package com.seaofnodes.simple; - -import com.seaofnodes.simple.codegen.Encoding; -import com.seaofnodes.simple.node.cpus.arm.arm; -import java.io.ByteArrayOutputStream; - -public class EvalArm64 { - // Memory image, always 0-based - public final byte[] _buf; - - // GPRs - final long[] regs; - // FRs - final double[] fregs; - - int _pc; - - // Start of free memory for allocation - int _heap; - - // Standard out and err streams - ByteArrayOutputStream _stdout, _stderr; - - // 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); st4(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; - - // for the opcode we just match for bits[31:24]; - switch( opcode1 ) { - case 0x14, 0x15, 0x16, 0x17: { //b - int imm26 = ir << 6 >> 6; - pc = pc + (imm26 * 4) - 4; - rdid = -1; - break; - } - case 0x1E: { // floats - 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 0x38: { - // STRB/LDRB(register) - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - // full opcode - opcode1 = (ir >> 21) & 0x7FF; - switch( ir >> 21 & 0x7 ) { - 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; - } - case 0x3 -> { // load - if( opcode1 == arm.OP_LOAD_R_8 ) rval = ld1s((int)regs[rn] + (int)regs[rm]); - else trap = (2+1); - } - 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 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 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 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 0x8A: { // AND - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - rval = regs[rn] & regs[rm]; - break; - } - - case 0x8B, 0xCB: { - // add/sub(register), unshifted form emitted by Simple - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - long lhs = regs[rn]; - long rhs = regs[rm]; - rval = opcode1==0x8B ? lhs + rhs : lhs - rhs; - break; - } - - case 0x90: { // adrp - int immhi = ( (ir& ~0x1F) << 8 ) >> 8; - int immlo = (ir>>29)& 3; - int imm = ((immhi<<2) | immlo) << 12; - rval = (pc & ~0xFFF) + imm; - break; - } - - case 0x91, 0xD1: { // ADD/SUB (immediate), unsigned imm12, optional LSL #12 - int rn = (ir >> 5) & 0x1F; - long immediate = (ir >>> 10) & 0xFFF; - if( (ir & (1<<22))!=0 ) immediate <<= 12; - rval = opcode1==0x91 ? regs[rn] + immediate : regs[rn] - immediate; - 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 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; - } - - case 0x94, 0x95, 0x96, 0x97: { // bl - rdid = -1; // BL writes only X30, not a displacement-selected register. - rval = pc + 4; - regs[arm.X30] = rval; - int imm26 = (ir & 0x03FFFFFF); - int imm = (imm26 << 6) >> 6; - pc = pc + (imm << 2) ; - if( pc == Encoding.SENTINEL_CALLOC ) { - assert (_heap&7) == 0; // 8-byte aligned - long size = regs[arm.X0]*regs[arm.X1]; - size = (size+7) & -8; // 8-byte aligned - regs[arm.X0] = _heap; - _heap += (int)size; - pc = (int)rval; - rdid = -1; - } - if( pc == Encoding.SENTINEL_WRITE ) { - ByteArrayOutputStream baos = switch((int)regs[0]) { - case 1 -> _stdout==null ? (_stdout = new ByteArrayOutputStream()) : _stdout; - case 2 -> _stderr==null ? (_stderr = new ByteArrayOutputStream()) : _stderr; - default -> throw new IllegalArgumentException(); - }; - baos.write(_buf,(int)regs[1],(int)regs[2]); - regs[0] = regs[2]; - pc = (int)rval; - rdid = -1; - } - pc -= 4; - 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 0x9B: { // mul - int rn = (ir >> 5) & 0x1F; - int rm = (ir >> 16) & 0x1F; - rval = regs[rn] * regs[rm]; - break; - } - case 0x9E: { - // scvtf - is_f = true; - int rs1 = (ir >> 5) & 0x1F; - frval = (double)regs[rs1]; - break; - } - - case 0xAA: { - // MOV(register) - int rm = (ir >> 16) & 0x1F; - rval = regs[rm]; - 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 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 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], Float.floatToRawIntBits((float)fregs[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, Float.floatToRawIntBits((float)fregs[rdid])); - } else trap = (2+1); - - rdid = -1; - break; - } - case 0x1: { - // LDR (immediate) - if(opcode1 == arm.OPF_LOAD_IMM_32) { - frval = ld4f((int)rval); - } else trap = (2+1); - break; - } - default: trap = (2+1); - } - break; - } - - case 0xD2: // MOVZ - case 0xF2: { // MOVK preserves the other halfwords - // movz - // get immediate - int shift = ((ir >> 21) & 3)*16; - rval = ((ir >> 5) & 0xFFFFL) << shift; - if( opcode1==0xF2 ) rval |= regs[rdid] & ~(0xFFFFL << shift); - 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 0xD6: { - int op = ir & 0xFFFFFC1F; - int rn = (ir >>> 5) & 31; - int target = (int)regs[rn]; // Read before BLR overwrites X30. - rdid = -1; - if( op==0xD63F0000 ) regs[arm.X30] = pc+4; // BLR - else if( op!=0xD65F0000 ) { trap = 3; break; } // RET - pc = target; - if( op==0xD65F0000 && pc==0 ) break outer; - pc -= 4; - 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 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 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 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], Double.doubleToRawLongBits(fregs[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 0xFD: { - // LDR/STR(immediate) 64 bits floats - int base = ir >> 5 & 0x1F; - int imm = (ir >> 10) & 0xFFF; - - imm *= 8; - 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, Double.doubleToRawLongBits(fregs[rdid])); - } else trap = (2+1); - - rdid = -1; - break; - } - case 0x1: { - // LDR (immediate) - if(opcode1 == arm.OPF_LOAD_IMM_64) { - frval = ld8f((int)rval); - } else trap = (2+1); - break; - } - default: trap = (2+1); - } - 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/chapter22/src/test/java/com/seaofnodes/simple/EvalRisc5.java b/chapter22/src/test/java/com/seaofnodes/simple/EvalRisc5.java deleted file mode 100644 index 00711f4e..00000000 --- a/chapter22/src/test/java/com/seaofnodes/simple/EvalRisc5.java +++ /dev/null @@ -1,367 +0,0 @@ - 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.io.ByteArrayOutputStream; -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; - - // Standard out and err streams - ByteArrayOutputStream _stdout, _stderr; - - // 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); st4(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; - if( pc+4 == Encoding.SENTINEL_WRITE ) { - ByteArrayOutputStream baos = switch((int)regs[10]) { - case 1 -> _stdout==null ? (_stdout = new ByteArrayOutputStream()) : _stdout; - case 2 -> _stderr==null ? (_stderr = new ByteArrayOutputStream()) : _stderr; - default -> throw new IllegalArgumentException(); - }; - baos.write(_buf,(int)regs[11],(int)regs[12]); - regs[10] = regs[12]; - rdid = 0; - pc = (int)(rval - 4); // Unwind PC, as-if returned from write - } - 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.SENTINEL_CALLOC ) { - assert (_heap&7) == 0; // 8-byte aligned - int size = (int)(regs[10]*regs[11]); - size = (size+7) & -8; // 8-byte aligned - regs[10] = _heap; // Return next free address - _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 & 0x3F); - case 2 -> (rs1 < rs2) ? 1 : 0; - case 3 -> (rs1 < rs2) ? 1 : 0; - case 4 -> rs1 ^ rs2; - case 5 -> (ir & 0x40000000) != 0 ? (rs1 >> (rs2 & 0x3F)) : (rs1 >>> (rs2 & 0x3F)); - 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/chapter22/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java b/chapter22/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java index 1717fbcd..f20fe6f5 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java @@ -1,5 +1,9 @@ package com.seaofnodes.simple; +import com.seaofnodes.isa.eval.EvalRisc5; + +import com.seaofnodes.isa.eval.EvalArm64; + import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.codegen.RegAllocTestSupport; import com.seaofnodes.simple.node.*; diff --git a/chapter22/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/chapter22/src/test/java/com/seaofnodes/simple/MergeSortTest.java index 82b1e58e..788bd9c2 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/MergeSortTest.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/MergeSortTest.java @@ -1,5 +1,7 @@ package com.seaofnodes.simple; +import com.seaofnodes.isa.eval.EvalRisc5; + import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.cpus.riscv.riscv; import java.io.IOException; diff --git a/chapter22/src/test/java/com/seaofnodes/simple/TestArm64.java b/chapter22/src/test/java/com/seaofnodes/simple/TestArm64.java index edce92f3..a22063eb 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/TestArm64.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/TestArm64.java @@ -1,5 +1,7 @@ package com.seaofnodes.simple; +import com.seaofnodes.isa.eval.EvalArm64; + import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen; diff --git a/chapter22/src/test/java/com/seaofnodes/simple/TestRisc5.java b/chapter22/src/test/java/com/seaofnodes/simple/TestRisc5.java index adb256eb..ace2a7ea 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/TestRisc5.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/TestRisc5.java @@ -1,5 +1,7 @@ package com.seaofnodes.simple; +import com.seaofnodes.isa.eval.EvalRisc5; + import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.node.FunNode; -- cgit v1.2.3