diff options
Diffstat (limited to 'pk/emulation.c')
-rw-r--r-- | pk/emulation.c | 596 |
1 files changed, 94 insertions, 502 deletions
diff --git a/pk/emulation.c b/pk/emulation.c index 87db2de..4943e0d 100644 --- a/pk/emulation.c +++ b/pk/emulation.c @@ -1,24 +1,76 @@ +#include "emulation.h" +#include "fp_emulation.h" +#include "config.h" +#include "unprivileged_memory.h" #include "mtrap.h" -#include "softfloat.h" #include <limits.h> -void redirect_trap(uintptr_t epc, uintptr_t mstatus) +void illegal_insn_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc) { - write_csr(sepc, epc); - write_csr(scause, read_csr(mcause)); - write_csr(mepc, read_csr(stvec)); + asm (".pushsection .rodata\n" + "illegal_insn_trap_table:\n" + " .word truly_illegal_insn\n" +#ifdef PK_ENABLE_FP_EMULATION + " .word emulate_float_load\n" +#else + " .word truly_illegal_insn\n" +#endif + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" +#ifdef PK_ENABLE_FP_EMULATION + " .word emulate_float_store\n" +#else + " .word truly_illegal_insn\n" +#endif + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word emulate_mul_div\n" + " .word truly_illegal_insn\n" + " .word emulate_mul_div32\n" + " .word truly_illegal_insn\n" +#ifdef PK_ENABLE_FP_EMULATION + " .word emulate_fmadd\n" + " .word emulate_fmadd\n" + " .word emulate_fmadd\n" + " .word emulate_fmadd\n" + " .word emulate_fp\n" +#else + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" +#endif + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word emulate_system\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .word truly_illegal_insn\n" + " .popsection"); - uintptr_t prev_priv = EXTRACT_FIELD(mstatus, MSTATUS_MPP); - uintptr_t prev_ie = EXTRACT_FIELD(mstatus, MSTATUS_MPIE); - kassert(prev_priv <= PRV_S); - mstatus = INSERT_FIELD(mstatus, MSTATUS_SPP, prev_priv); - mstatus = INSERT_FIELD(mstatus, MSTATUS_SPIE, prev_ie); - mstatus = INSERT_FIELD(mstatus, MSTATUS_MPP, PRV_S); - mstatus = INSERT_FIELD(mstatus, MSTATUS_MPIE, 0); - write_csr(mstatus, mstatus); + uintptr_t mstatus; + insn_t insn = get_insn(mepc, &mstatus); - extern void __redirect_trap(); - return __redirect_trap(); + if (unlikely((insn & 3) != 3)) + return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); + + write_csr(mepc, mepc + 4); + + extern int32_t illegal_insn_trap_table[]; + int32_t* pf = (void*)illegal_insn_trap_table + (insn & 0x7c); + emulation_func f = (emulation_func)(uintptr_t)*pf; + f(regs, mcause, mepc, mstatus, insn); } void __attribute__((noinline)) truly_illegal_insn(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc, uintptr_t mstatus, insn_t insn) @@ -28,8 +80,14 @@ void __attribute__((noinline)) truly_illegal_insn(uintptr_t* regs, uintptr_t mca void misaligned_load_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc) { + union { + uint8_t bytes[8]; + uintptr_t intx; + uint64_t int64; + } val; uintptr_t mstatus; insn_t insn = get_insn(mepc, &mstatus); + uintptr_t addr = GET_RS1(insn, regs) + IMM_I(insn); int shift = 0, fp = 0, len; if ((insn & MASK_LW) == MATCH_LW) @@ -51,139 +109,54 @@ void misaligned_load_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc) else return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - uintptr_t addr = GET_RS1(insn, regs) + IMM_I(insn); - uintptr_t val = 0, tmp, tmp2; - unpriv_mem_access("add %[tmp2], %[addr], %[len];" - "1: slli %[val], %[val], 8;" - "lbu %[tmp], -1(%[tmp2]);" - "addi %[tmp2], %[tmp2], -1;" - "or %[val], %[val], %[tmp];" - "bne %[addr], %[tmp2], 1b;", - val, tmp, tmp2, addr, len); - - if (shift) - val = (intptr_t)val << shift >> shift; + val.int64 = 0; + for (intptr_t i = len-1; i >= 0; i--) + val.bytes[i] = load_uint8_t((void *)(addr + i), mepc); if (!fp) - SET_RD(insn, regs, val); + SET_RD(insn, regs, (intptr_t)val.intx << shift >> shift); else if (len == 8) - SET_F64_RD(insn, regs, val); + SET_F64_RD(insn, regs, val.int64); else - SET_F32_RD(insn, regs, val); + SET_F32_RD(insn, regs, val.intx); write_csr(mepc, mepc + 4); } void misaligned_store_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc) { + union { + uint8_t bytes[8]; + uintptr_t intx; + uint64_t int64; + } val; uintptr_t mstatus; insn_t insn = get_insn(mepc, &mstatus); - - uintptr_t val = GET_RS2(insn, regs), error; int len; + + val.intx = GET_RS2(insn, regs); if ((insn & MASK_SW) == MATCH_SW) len = 4; +#ifdef __riscv64 else if ((insn & MASK_SD) == MATCH_SD) len = 8; +#endif else if ((insn & MASK_FSD) == MATCH_FSD) - len = 8, val = GET_F64_RS2(insn, regs); + len = 8, val.int64 = GET_F64_RS2(insn, regs); else if ((insn & MASK_FSW) == MATCH_FSW) - len = 4, val = GET_F32_RS2(insn, regs); + len = 4, val.intx = GET_F32_RS2(insn, regs); else if ((insn & MASK_SH) == MATCH_SH) len = 2; else return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); uintptr_t addr = GET_RS1(insn, regs) + IMM_S(insn); - uintptr_t tmp, tmp2, addr_end = addr + len; - unpriv_mem_access("mv %[tmp], %[val];" - "mv %[tmp2], %[addr];" - "1: sb %[tmp], 0(%[tmp2]);" - "srli %[tmp], %[tmp], 8;" - "addi %[tmp2], %[tmp2], 1;" - "bne %[tmp2], %[addr_end], 1b", - tmp, tmp2, unused1, val, addr, addr_end); + for (size_t i = 0; i < len; i++) + store_uint8_t((void *)(addr + i), val.bytes[i], mepc); write_csr(mepc, mepc + 4); } -DECLARE_EMULATION_FUNC(emulate_float_load) -{ - uintptr_t val_lo, val_hi; - uint64_t val; - uintptr_t addr = GET_RS1(insn, regs) + IMM_I(insn); - - switch (insn & MASK_FUNCT3) - { - case MATCH_FLW & MASK_FUNCT3: - if (addr % 4 != 0) - return misaligned_load_trap(regs, mcause, mepc); - - unpriv_mem_access("lw %[val_lo], (%[addr])", - val_lo, unused1, unused2, addr, mepc/*X*/); - SET_F32_RD(insn, regs, val_lo); - break; - - case MATCH_FLD & MASK_FUNCT3: - if (addr % sizeof(uintptr_t) != 0) - return misaligned_load_trap(regs, mcause, mepc); - -#ifdef __riscv64 - unpriv_mem_access("ld %[val], (%[addr])", - val, val_hi/*X*/, unused1, addr, mepc/*X*/); -#else - unpriv_mem_access("lw %[val_lo], (%[addr]);" - "lw %[val_hi], 4(%[addr])", - val_lo, val_hi, unused1, addr, mepc/*X*/); - val = val_lo | ((uint64_t)val_hi << 32); -#endif - SET_F64_RD(insn, regs, val); - break; - - default: - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - } -} - -DECLARE_EMULATION_FUNC(emulate_float_store) -{ - uintptr_t val_lo, val_hi; - uint64_t val; - uintptr_t addr = GET_RS1(insn, regs) + IMM_S(insn); - - switch (insn & MASK_FUNCT3) - { - case MATCH_FSW & MASK_FUNCT3: - if (addr % 4 != 0) - return misaligned_store_trap(regs, mcause, mepc); - - val_lo = GET_F32_RS2(insn, regs); - unpriv_mem_access("sw %[val_lo], (%[addr])", - unused1, unused2, unused3, val_lo, addr); - break; - - case MATCH_FSD & MASK_FUNCT3: - if (addr % sizeof(uintptr_t) != 0) - return misaligned_store_trap(regs, mcause, mepc); - - val = GET_F64_RS2(insn, regs); -#ifdef __riscv64 - unpriv_mem_access("sd %[val], (%[addr])", - unused1, unused2, unused3, val, addr); -#else - val_lo = val; - val_hi = val >> 32; - unpriv_mem_access("sw %[val_lo], (%[addr]);" - "sw %[val_hi], 4(%[addr])", - unused1, unused2, unused3, val_lo, val_hi, addr); -#endif - break; - - default: - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - } -} - #ifdef __riscv64 typedef __int128 double_int; #else @@ -279,7 +252,7 @@ static inline int emulate_read_csr(int num, uintptr_t mstatus, uintptr_t* result + HLS()->sinstret_delta) >> 32; return 0; #endif -#ifndef __riscv_hard_float +#ifdef PK_ENABLE_FP_EMULATION case CSR_FRM: if ((mstatus & MSTATUS_FS) == 0) break; *result = GET_FRM(); @@ -301,7 +274,7 @@ static inline int emulate_write_csr(int num, uintptr_t value, uintptr_t mstatus) { switch (num) { -#ifndef __riscv_hard_float +#ifndef PK_ENABLE_FP_EMULATION case CSR_FRM: SET_FRM(value); return 0; case CSR_FFLAGS: SET_FFLAGS(value); return 0; case CSR_FCSR: SET_FCSR(value); return 0; @@ -338,384 +311,3 @@ DECLARE_EMULATION_FUNC(emulate_system) SET_RD(insn, regs, csr_val); } - -DECLARE_EMULATION_FUNC(emulate_fp) -{ - asm (".pushsection .rodata\n" - "fp_emulation_table:\n" - " .word emulate_fadd\n" - " .word emulate_fsub\n" - " .word emulate_fmul\n" - " .word emulate_fdiv\n" - " .word emulate_fsgnj\n" - " .word emulate_fmin\n" - " .word truly_illegal_insn\n" - " .word truly_illegal_insn\n" - " .word emulate_fcvt_ff\n" - " .word truly_illegal_insn\n" - " .word truly_illegal_insn\n" - " .word emulate_fsqrt\n" - " .word truly_illegal_insn\n" - " .word truly_illegal_insn\n" - " .word truly_illegal_insn\n" - " .word truly_illegal_insn\n" - " .word truly_illegal_insn\n" - " .word truly_illegal_insn\n" - " .word truly_illegal_insn\n" - " .word truly_illegal_insn\n" - " .word emulate_fcmp\n" - " .word truly_illegal_insn\n" - " .word truly_illegal_insn\n" - " .word truly_illegal_insn\n" - " .word emulate_fcvt_if\n" - " .word truly_illegal_insn\n" - " .word emulate_fcvt_fi\n" - " .word truly_illegal_insn\n" - " .word emulate_fmv_if\n" - " .word truly_illegal_insn\n" - " .word emulate_fmv_fi\n" - " .word truly_illegal_insn\n" - " .popsection"); - - // if FPU is disabled, punt back to the OS - if (unlikely((mstatus & MSTATUS_FS) == 0)) - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - - extern int32_t fp_emulation_table[]; - int32_t* pf = (void*)fp_emulation_table + ((insn >> 25) & 0x7c); - emulation_func f = (emulation_func)(uintptr_t)*pf; - - SETUP_STATIC_ROUNDING(insn); - return f(regs, mcause, mepc, mstatus, insn); -} - -void emulate_any_fadd(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc, uintptr_t mstatus, insn_t insn, int32_t neg_b) -{ - if (GET_PRECISION(insn) == PRECISION_S) { - uint32_t rs1 = GET_F32_RS1(insn, regs); - uint32_t rs2 = GET_F32_RS2(insn, regs) ^ neg_b; - SET_F32_RD(insn, regs, f32_add(rs1, rs2)); - } else if (GET_PRECISION(insn) == PRECISION_D) { - uint64_t rs1 = GET_F64_RS1(insn, regs); - uint64_t rs2 = GET_F64_RS2(insn, regs) ^ ((uint64_t)neg_b << 32); - SET_F64_RD(insn, regs, f64_add(rs1, rs2)); - } else { - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - } -} - -DECLARE_EMULATION_FUNC(emulate_fadd) -{ - return emulate_any_fadd(regs, mcause, mepc, mstatus, insn, 0); -} - -DECLARE_EMULATION_FUNC(emulate_fsub) -{ - return emulate_any_fadd(regs, mcause, mepc, mstatus, insn, INT32_MIN); -} - -DECLARE_EMULATION_FUNC(emulate_fmul) -{ - if (GET_PRECISION(insn) == PRECISION_S) { - uint32_t rs1 = GET_F32_RS1(insn, regs); - uint32_t rs2 = GET_F32_RS2(insn, regs); - SET_F32_RD(insn, regs, f32_mul(rs1, rs2)); - } else if (GET_PRECISION(insn) == PRECISION_D) { - uint64_t rs1 = GET_F64_RS1(insn, regs); - uint64_t rs2 = GET_F64_RS2(insn, regs); - SET_F64_RD(insn, regs, f64_mul(rs1, rs2)); - } else { - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - } -} - -DECLARE_EMULATION_FUNC(emulate_fdiv) -{ - if (GET_PRECISION(insn) == PRECISION_S) { - uint32_t rs1 = GET_F32_RS1(insn, regs); - uint32_t rs2 = GET_F32_RS2(insn, regs); - SET_F32_RD(insn, regs, f32_div(rs1, rs2)); - } else if (GET_PRECISION(insn) == PRECISION_D) { - uint64_t rs1 = GET_F64_RS1(insn, regs); - uint64_t rs2 = GET_F64_RS2(insn, regs); - SET_F64_RD(insn, regs, f64_div(rs1, rs2)); - } else { - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - } -} - -DECLARE_EMULATION_FUNC(emulate_fsqrt) -{ - if ((insn >> 20) & 0x1f) - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - - if (GET_PRECISION(insn) == PRECISION_S) { - SET_F32_RD(insn, regs, f32_sqrt(GET_F32_RS1(insn, regs))); - } else if (GET_PRECISION(insn) == PRECISION_D) { - SET_F64_RD(insn, regs, f64_sqrt(GET_F64_RS1(insn, regs))); - } else { - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - } -} - -DECLARE_EMULATION_FUNC(emulate_fsgnj) -{ - int rm = GET_RM(insn); - if (rm >= 3) - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - - #define DO_FSGNJ(rs1, rs2, rm) ({ \ - typeof(rs1) rs1_sign = (rs1) >> (8*sizeof(rs1)-1); \ - typeof(rs1) rs2_sign = (rs2) >> (8*sizeof(rs1)-1); \ - rs1_sign &= (rm) >> 1; \ - rs1_sign ^= (rm) ^ rs2_sign; \ - ((rs1) << 1 >> 1) | (rs1_sign << (8*sizeof(rs1)-1)); }) - - if (GET_PRECISION(insn) == PRECISION_S) { - uint32_t rs1 = GET_F32_RS1(insn, regs); - uint32_t rs2 = GET_F32_RS2(insn, regs); - SET_F32_RD(insn, regs, DO_FSGNJ(rs1, rs2, rm)); - } else if (GET_PRECISION(insn) == PRECISION_D) { - uint64_t rs1 = GET_F64_RS1(insn, regs); - uint64_t rs2 = GET_F64_RS2(insn, regs); - SET_F64_RD(insn, regs, DO_FSGNJ(rs1, rs2, rm)); - } else { - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - } -} - -DECLARE_EMULATION_FUNC(emulate_fmin) -{ - int rm = GET_RM(insn); - if (rm >= 2) - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - - if (GET_PRECISION(insn) == PRECISION_S) { - uint32_t rs1 = GET_F32_RS1(insn, regs); - uint32_t rs2 = GET_F32_RS2(insn, regs); - uint32_t arg1 = rm ? rs2 : rs1; - uint32_t arg2 = rm ? rs1 : rs2; - int use_rs1 = f32_lt_quiet(arg1, arg2) || isNaNF32UI(rs2); - SET_F32_RD(insn, regs, use_rs1 ? rs1 : rs2); - } else if (GET_PRECISION(insn) == PRECISION_D) { - uint64_t rs1 = GET_F64_RS1(insn, regs); - uint64_t rs2 = GET_F64_RS2(insn, regs); - uint64_t arg1 = rm ? rs2 : rs1; - uint64_t arg2 = rm ? rs1 : rs2; - int use_rs1 = f64_lt_quiet(arg1, arg2) || isNaNF64UI(rs2); - SET_F64_RD(insn, regs, use_rs1 ? rs1 : rs2); - } else { - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - } -} - -DECLARE_EMULATION_FUNC(emulate_fcvt_ff) -{ - int rs2_num = (insn >> 20) & 0x1f; - if (GET_PRECISION(insn) == PRECISION_S) { - if (rs2_num != 1) - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - SET_F32_RD(insn, regs, f64_to_f32(GET_F64_RS1(insn, regs))); - } else if (GET_PRECISION(insn) == PRECISION_D) { - if (rs2_num != 0) - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - SET_F64_RD(insn, regs, f32_to_f64(GET_F32_RS1(insn, regs))); - } else { - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - } -} - -DECLARE_EMULATION_FUNC(emulate_fcvt_fi) -{ - if (GET_PRECISION(insn) != PRECISION_S && GET_PRECISION(insn) != PRECISION_D) - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - - int negative = 0; - uint64_t uint_val = GET_RS1(insn, regs); - - switch ((insn >> 20) & 0x1f) - { - case 0: // int32 - negative = (int32_t)uint_val < 0; - uint_val = negative ? -(int32_t)uint_val : (int32_t)uint_val; - break; - case 1: // uint32 - uint_val = (uint32_t)uint_val; - break; -#ifdef __riscv64 - case 2: // int64 - negative = (int64_t)uint_val < 0; - uint_val = negative ? -uint_val : uint_val; - case 3: // uint64 - break; -#endif - default: - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - } - - uint64_t float64 = ui64_to_f64(uint_val); - if (negative) - float64 ^= INT64_MIN; - - if (GET_PRECISION(insn) == PRECISION_S) - SET_F32_RD(insn, regs, f64_to_f32(float64)); - else - SET_F64_RD(insn, regs, float64); -} - -DECLARE_EMULATION_FUNC(emulate_fcvt_if) -{ - int rs2_num = (insn >> 20) & 0x1f; -#ifdef __riscv64 - if (rs2_num >= 4) - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); -#else - if (rs2_num >= 2) - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); -#endif - - int64_t float64; - if (GET_PRECISION(insn) == PRECISION_S) - float64 = f32_to_f64(GET_F32_RS1(insn, regs)); - else if (GET_PRECISION(insn) == PRECISION_D) - float64 = GET_F64_RS1(insn, regs); - else - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - - int negative = 0; - if (float64 < 0) { - negative = 1; - float64 ^= INT64_MIN; - } - uint64_t uint_val = f64_to_ui64(float64, softfloat_roundingMode, true); - uint64_t result, limit, limit_result; - - switch (rs2_num) - { - case 0: // int32 - if (negative) { - result = (int32_t)-uint_val; - limit_result = limit = (uint32_t)INT32_MIN; - } else { - result = (int32_t)uint_val; - limit_result = limit = INT32_MAX; - } - break; - - case 1: // uint32 - limit = limit_result = UINT32_MAX; - if (negative) - result = limit = 0; - else - result = (uint32_t)uint_val; - break; - - case 2: // int32 - if (negative) { - result = (int64_t)-uint_val; - limit_result = limit = (uint64_t)INT64_MIN; - } else { - result = (int64_t)uint_val; - limit_result = limit = INT64_MAX; - } - break; - - case 3: // uint64 - limit = limit_result = UINT64_MAX; - if (negative) - result = limit = 0; - else - result = (uint64_t)uint_val; - break; - - default: - __builtin_unreachable(); - } - - if (uint_val > limit) { - result = limit_result; - softfloat_raiseFlags(softfloat_flag_invalid); - } - - SET_FS_DIRTY(); - SET_RD(insn, regs, result); -} - -DECLARE_EMULATION_FUNC(emulate_fcmp) -{ - int rm = GET_RM(insn); - if (rm >= 3) - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - - uintptr_t result; - if (GET_PRECISION(insn) == PRECISION_S) { - uint32_t rs1 = GET_F32_RS1(insn, regs); - uint32_t rs2 = GET_F32_RS2(insn, regs); - if (rm != 1) - result = f32_eq(rs1, rs2); - if (rm == 1 || (rm == 0 && !result)) - result = f32_lt(rs1, rs2); - goto success; - } else if (GET_PRECISION(insn) == PRECISION_D) { - uint64_t rs1 = GET_F64_RS1(insn, regs); - uint64_t rs2 = GET_F64_RS2(insn, regs); - if (rm != 1) - result = f64_eq(rs1, rs2); - if (rm == 1 || (rm == 0 && !result)) - result = f64_lt(rs1, rs2); - goto success; - } - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); -success: - SET_RD(insn, regs, result); -} - -DECLARE_EMULATION_FUNC(emulate_fmv_if) -{ - uintptr_t result; - if ((insn & MASK_FMV_X_S) == MATCH_FMV_X_S) - result = GET_F32_RS1(insn, regs); -#ifdef __riscv64 - else if ((insn & MASK_FMV_X_D) == MATCH_FMV_X_D) - result = GET_F64_RS1(insn, regs); -#endif - else - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - - SET_RD(insn, regs, result); -} - -DECLARE_EMULATION_FUNC(emulate_fmv_fi) -{ - uintptr_t rs1 = GET_RS1(insn, regs); - - if ((insn & MASK_FMV_S_X) == MATCH_FMV_S_X) - SET_F32_RD(insn, regs, rs1); - else if ((insn & MASK_FMV_D_X) == MATCH_FMV_D_X) - SET_F64_RD(insn, regs, rs1); - else - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); -} - -DECLARE_EMULATION_FUNC(emulate_fmadd) -{ - // if FPU is disabled, punt back to the OS - if (unlikely((mstatus & MSTATUS_FS) == 0)) - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - - int op = (insn >> 2) & 3; - SETUP_STATIC_ROUNDING(insn); - if (GET_PRECISION(insn) == PRECISION_S) { - uint32_t rs1 = GET_F32_RS1(insn, regs); - uint32_t rs2 = GET_F32_RS2(insn, regs); - uint32_t rs3 = GET_F32_RS3(insn, regs); - SET_F32_RD(insn, regs, softfloat_mulAddF32(op, rs1, rs2, rs3)); - } else if (GET_PRECISION(insn) == PRECISION_D) { - uint64_t rs1 = GET_F64_RS1(insn, regs); - uint64_t rs2 = GET_F64_RS2(insn, regs); - uint64_t rs3 = GET_F64_RS3(insn, regs); - SET_F64_RD(insn, regs, softfloat_mulAddF64(op, rs1, rs2, rs3)); - } else { - return truly_illegal_insn(regs, mcause, mepc, mstatus, insn); - } -} |