Signed-off-by: Sagar Karandikar <sag...@eecs.berkeley.edu> --- target-riscv/fpu_helper.c | 225 ++++++++++++++++++++++++++++++++++++++++++++++ target-riscv/helper.h | 30 +++++++ target-riscv/translate.c | 135 ++++++++++++++++++++++++++++ 3 files changed, 390 insertions(+)
diff --git a/target-riscv/fpu_helper.c b/target-riscv/fpu_helper.c index 8d33fa1..b3d443e 100644 --- a/target-riscv/fpu_helper.c +++ b/target-riscv/fpu_helper.c @@ -355,3 +355,228 @@ target_ulong helper_fclass_s(CPURISCVState *env, uint64_t frs1) frs1 = float32_classify(frs1, &env->fp_status); return frs1; } + +uint64_t helper_fadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_add(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_sub(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fmul_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_mul(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fdiv_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_div(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fsgnj_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + frs1 = (frs1 & ~INT64_MIN) | (frs2 & INT64_MIN); + return frs1; +} + +uint64_t helper_fsgnjn_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + frs1 = (frs1 & ~INT64_MIN) | ((~frs2) & INT64_MIN); + return frs1; +} + +uint64_t helper_fsgnjx_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + frs1 = frs1 ^ (frs2 & INT64_MIN); + return frs1; +} + +uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + frs1 = float64_is_any_nan(frs2) || + float64_lt_quiet(frs1, frs2, &env->fp_status) ? frs1 : frs2; + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + frs1 = float64_is_any_nan(frs2) || + float64_le_quiet(frs2, frs1, &env->fp_status) ? frs1 : frs2; + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1, uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + rs1 = float64_to_float32(rs1, &env->fp_status); + set_fp_exceptions(); + return rs1; +} + +uint64_t helper_fcvt_d_s(CPURISCVState *env, uint64_t rs1, uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + rs1 = float32_to_float64(rs1, &env->fp_status); + set_fp_exceptions(); + return rs1; +} + +uint64_t helper_fsqrt_d(CPURISCVState *env, uint64_t frs1, uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_sqrt(frs1, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + frs1 = float64_le(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + frs1 = float64_lt(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + frs1 = float64_eq(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1, uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + frs1 = (int64_t)((int32_t)float64_to_int32(frs1, &env->fp_status)); + set_fp_exceptions(); + return frs1; +} + +target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1, uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + frs1 = (int64_t)((int32_t)float64_to_uint32(frs1, &env->fp_status)); + set_fp_exceptions(); + return frs1; +} + +#if defined(TARGET_RISCV64) +uint64_t helper_fcvt_l_d(CPURISCVState *env, uint64_t frs1, uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_to_int64(frs1, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fcvt_lu_d(CPURISCVState *env, uint64_t frs1, uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_to_uint64(frs1, &env->fp_status); + set_fp_exceptions(); + return frs1; +} +#endif + +uint64_t helper_fcvt_d_w(CPURISCVState *env, target_ulong rs1, uint64_t rm) +{ + uint64_t res; + set_float_rounding_mode(RM, &env->fp_status); + res = int32_to_float64((int32_t)rs1, &env->fp_status); + set_fp_exceptions(); + return res; +} + +uint64_t helper_fcvt_d_wu(CPURISCVState *env, target_ulong rs1, uint64_t rm) +{ + uint64_t res; + set_float_rounding_mode(RM, &env->fp_status); + res = uint32_to_float64((uint32_t)rs1, &env->fp_status); + set_fp_exceptions(); + return res; +} + +#if defined(TARGET_RISCV64) +uint64_t helper_fcvt_d_l(CPURISCVState *env, uint64_t rs1, uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + rs1 = int64_to_float64(rs1, &env->fp_status); + set_fp_exceptions(); + return rs1; +} + +uint64_t helper_fcvt_d_lu(CPURISCVState *env, uint64_t rs1, uint64_t rm) +{ + set_float_rounding_mode(RM, &env->fp_status); + rs1 = uint64_to_float64(rs1, &env->fp_status); + set_fp_exceptions(); + return rs1; +} +#endif + +/* adapted from spike */ +#define isNaNF64UI(ui) (UINT64_C(0xFFE0000000000000) \ + < (uint64_t)((uint_fast64_t)ui << 1)) +#define signF64UI(a) ((bool)((uint64_t) a >> 63)) +#define expF64UI(a) ((int_fast16_t)(a >> 52) & 0x7FF) +#define fracF64UI(a) (a & UINT64_C(0x000FFFFFFFFFFFFF)) + +union ui64_f64 { uint64_t ui; uint64_t f; }; + +uint_fast16_t float64_classify(uint64_t a, float_status *status) +{ + union ui64_f64 uA; + uint_fast64_t uiA; + + uA.f = a; + uiA = uA.ui; + + uint_fast16_t infOrNaN = expF64UI(uiA) == 0x7FF; + uint_fast16_t subnormalOrZero = expF64UI(uiA) == 0; + bool sign = signF64UI(uiA); + + return + (sign && infOrNaN && fracF64UI(uiA) == 0) << 0 | + (sign && !infOrNaN && !subnormalOrZero) << 1 | + (sign && subnormalOrZero && fracF64UI(uiA)) << 2 | + (sign && subnormalOrZero && fracF64UI(uiA) == 0) << 3 | + (!sign && infOrNaN && fracF64UI(uiA) == 0) << 7 | + (!sign && !infOrNaN && !subnormalOrZero) << 6 | + (!sign && subnormalOrZero && fracF64UI(uiA)) << 5 | + (!sign && subnormalOrZero && fracF64UI(uiA) == 0) << 4 | + (isNaNF64UI(uiA) && float64_is_signaling_nan(uiA, status)) << 8 | + (isNaNF64UI(uiA) && !float64_is_signaling_nan(uiA, status)) << 9; +} + +target_ulong helper_fclass_d(CPURISCVState *env, uint64_t frs1) +{ + frs1 = float64_classify(frs1, &env->fp_status); + return frs1; +} diff --git a/target-riscv/helper.h b/target-riscv/helper.h index 85b505a..eeb1caf 100644 --- a/target-riscv/helper.h +++ b/target-riscv/helper.h @@ -44,3 +44,33 @@ DEF_HELPER_FLAGS_3(fcvt_s_l, TCG_CALL_NO_RWG, i64, env, i64, i64) DEF_HELPER_FLAGS_3(fcvt_s_lu, TCG_CALL_NO_RWG, i64, env, i64, i64) #endif DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG, tl, env, i64) + +/* Floating Point - Double Precision */ +DEF_HELPER_FLAGS_4(fadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fmul_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fdiv_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_3(fsgnj_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fsgnjn_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fsgnjx_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fcvt_s_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fcvt_d_s, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fsqrt_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fle_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(flt_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(feq_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fcvt_w_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +DEF_HELPER_FLAGS_3(fcvt_wu_d, TCG_CALL_NO_RWG, tl, env, i64, i64) +#if defined(TARGET_RISCV64) +DEF_HELPER_FLAGS_3(fcvt_l_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fcvt_lu_d, TCG_CALL_NO_RWG, i64, env, i64, i64) +#endif +DEF_HELPER_FLAGS_3(fcvt_d_w, TCG_CALL_NO_RWG, i64, env, tl, i64) +DEF_HELPER_FLAGS_3(fcvt_d_wu, TCG_CALL_NO_RWG, i64, env, tl, i64) +#if defined(TARGET_RISCV64) +DEF_HELPER_FLAGS_3(fcvt_d_l, TCG_CALL_NO_RWG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fcvt_d_lu, TCG_CALL_NO_RWG, i64, env, i64, i64) +#endif +DEF_HELPER_FLAGS_2(fclass_d, TCG_CALL_NO_RWG, tl, env, i64) diff --git a/target-riscv/translate.c b/target-riscv/translate.c index 4140769..de39276 100644 --- a/target-riscv/translate.c +++ b/target-riscv/translate.c @@ -1015,6 +1015,141 @@ static inline void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, tcg_gen_extu_i32_i64(cpu_fpr[rd], write_int_rd); #endif break; + /* double */ + case OPC_RISC_FADD_D: + gen_helper_fadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + rm_reg); + break; + case OPC_RISC_FSUB_D: + gen_helper_fsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + rm_reg); + break; + case OPC_RISC_FMUL_D: + gen_helper_fmul_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + rm_reg); + break; + case OPC_RISC_FDIV_D: + gen_helper_fdiv_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + rm_reg); + break; + case OPC_RISC_FSGNJ_D: + /* also OPC_RISC_FSGNJN_D, OPC_RISC_FSGNJX_D */ + if (rm == 0x0) { + gen_helper_fsgnj_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], + cpu_fpr[rs2]); + } else if (rm == 0x1) { + gen_helper_fsgnjn_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], + cpu_fpr[rs2]); + } else if (rm == 0x2) { + gen_helper_fsgnjx_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], + cpu_fpr[rs2]); + } else { + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + } + break; + case OPC_RISC_FMIN_D: + /* also OPC_RISC_FMAX_D */ + if (rm == 0x0) { + gen_helper_fmin_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + } else if (rm == 0x1) { + gen_helper_fmax_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + } else { + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + } + break; + case OPC_RISC_FCVT_S_D: + if (rs2 == 0x1) { + gen_helper_fcvt_s_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], rm_reg); + } else { + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + } + break; + case OPC_RISC_FCVT_D_S: + if (rs2 == 0x0) { + gen_helper_fcvt_d_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], rm_reg); + } else { + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + } + break; + case OPC_RISC_FSQRT_D: + gen_helper_fsqrt_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], rm_reg); + break; + case OPC_RISC_FEQ_D: + /* also OPC_RISC_FLT_D, OPC_RISC_FLE_D */ + if (rm == 0x0) { + gen_helper_fle_d(write_int_rd, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + } else if (rm == 0x1) { + gen_helper_flt_d(write_int_rd, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + } else if (rm == 0x2) { + gen_helper_feq_d(write_int_rd, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); + } else { + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + } + gen_set_gpr(rd, write_int_rd); + break; + case OPC_RISC_FCVT_W_D: + /* also OPC_RISC_FCVT_WU_D, OPC_RISC_FCVT_L_D, OPC_RISC_FCVT_LU_D */ + if (rs2 == 0x0) { + gen_helper_fcvt_w_d(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg); + } else if (rs2 == 0x1) { + gen_helper_fcvt_wu_d(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg); + } else if (rs2 == 0x2) { +#if defined(TARGET_RISCV64) + gen_helper_fcvt_l_d(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg); +#else + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); +#endif + } else if (rs2 == 0x3) { +#if defined(TARGET_RISCV64) + gen_helper_fcvt_lu_d(write_int_rd, cpu_env, cpu_fpr[rs1], rm_reg); +#else + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); +#endif + } else { + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + } + gen_set_gpr(rd, write_int_rd); + break; + case OPC_RISC_FCVT_D_W: + /* also OPC_RISC_FCVT_D_WU, OPC_RISC_FCVT_D_L, OPC_RISC_FCVT_D_LU */ + gen_get_gpr(write_int_rd, rs1); + if (rs2 == 0x0) { + gen_helper_fcvt_d_w(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg); + } else if (rs2 == 0x1) { + gen_helper_fcvt_d_wu(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg); + } else if (rs2 == 0x2) { +#if defined(TARGET_RISCV64) + gen_helper_fcvt_d_l(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg); +#else + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); +#endif + } else if (rs2 == 0x3) { +#if defined(TARGET_RISCV64) + gen_helper_fcvt_d_lu(cpu_fpr[rd], cpu_env, write_int_rd, rm_reg); +#else + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); +#endif + } else { + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + } + break; +#if defined(TARGET_RISCV64) + case OPC_RISC_FMV_X_D: + /* also OPC_RISC_FCLASS_D */ + if (rm == 0x0) { /* FMV */ + tcg_gen_mov_tl(write_int_rd, cpu_fpr[rs1]); + } else if (rm == 0x1) { + gen_helper_fclass_d(write_int_rd, cpu_env, cpu_fpr[rs1]); + } else { + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + } + gen_set_gpr(rd, write_int_rd); + break; + case OPC_RISC_FMV_D_X: + gen_get_gpr(write_int_rd, rs1); + tcg_gen_mov_tl(cpu_fpr[rd], write_int_rd); + break; +#endif default: kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); break; -- 2.9.3