Helper routines for FPU instructions and NaN definitions. Signed-off-by: Michael Clark <m...@sifive.com> --- fpu/softfloat-specialize.h | 7 +- target/riscv/fpu_helper.c | 591 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 595 insertions(+), 3 deletions(-) create mode 100644 target/riscv/fpu_helper.c
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index de2c5d5..49ee578 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -114,7 +114,8 @@ float32 float32_default_nan(float_status *status) #if defined(TARGET_SPARC) || defined(TARGET_M68K) return const_float32(0x7FFFFFFF); #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_TRICORE) + defined(TARGET_XTENSA) || defined(TARGET_S390X) || \ + defined(TARGET_TRICORE) || defined(TARGET_RISCV) return const_float32(0x7FC00000); #elif defined(TARGET_HPPA) return const_float32(0x7FA00000); @@ -139,7 +140,7 @@ float64 float64_default_nan(float_status *status) #if defined(TARGET_SPARC) || defined(TARGET_M68K) return const_float64(LIT64(0x7FFFFFFFFFFFFFFF)); #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_S390X) + defined(TARGET_S390X) || defined(TARGET_RISCV) return const_float64(LIT64(0x7FF8000000000000)); #elif defined(TARGET_HPPA) return const_float64(LIT64(0x7FF4000000000000)); @@ -189,7 +190,7 @@ float128 float128_default_nan(float_status *status) r.high = LIT64(0x7FFF7FFFFFFFFFFF); } else { r.low = LIT64(0x0000000000000000); -#if defined(TARGET_S390X) || defined(TARGET_PPC) +#if defined(TARGET_S390X) || defined(TARGET_PPC) || defined(TARGET_RISCV) r.high = LIT64(0x7FFF800000000000); #else r.high = LIT64(0xFFFF800000000000); diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c new file mode 100644 index 0000000..ada985f --- /dev/null +++ b/target/riscv/fpu_helper.c @@ -0,0 +1,591 @@ +/* + * RISC-V FPU Emulation Helpers for QEMU. + * + * Author: Sagar Karandikar, sag...@eecs.berkeley.edu + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include <stdlib.h> +#include "cpu.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" + +/* convert RISC-V rounding mode to IEEE library numbers */ +unsigned int ieee_rm[] = { + float_round_nearest_even, + float_round_to_zero, + float_round_down, + float_round_up, + float_round_ties_away +}; + +/* obtain rm value to use in computation + * as the last step, convert rm codes to what the softfloat library expects + * Adapted from Spike's decode.h:RM + */ +#define RM ({ \ +if (rm == 7) { \ + rm = env->frm; \ +} \ +if (rm > 4) { \ + helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \ +} \ +ieee_rm[rm]; }) + +#ifndef CONFIG_USER_ONLY +#define require_fp if (!(env->mstatus & MSTATUS_FS)) { \ + helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \ +} +#else +#define require_fp /* nop */ +#endif + +/* convert softfloat library flag numbers to RISC-V */ +unsigned int softfloat_flags_to_riscv(unsigned int flags) +{ + int rv_flags = 0; + rv_flags |= (flags & float_flag_inexact) ? 1 : 0; + rv_flags |= (flags & float_flag_underflow) ? 2 : 0; + rv_flags |= (flags & float_flag_overflow) ? 4 : 0; + rv_flags |= (flags & float_flag_divbyzero) ? 8 : 0; + rv_flags |= (flags & float_flag_invalid) ? 16 : 0; + return rv_flags; +} + +/* adapted from Spike's decode.h:set_fp_exceptions */ +#define set_fp_exceptions() do { \ + env->fflags |= softfloat_flags_to_riscv(get_float_exception_flags(\ + &env->fp_status)); \ + set_float_exception_flags(0, &env->fp_status); \ +} while (0) + +uint64_t helper_fmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_muladd(frs1, frs2, frs3, 0, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_muladd(frs1, frs2, frs3, 0, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_muladd(frs1, frs2, frs3 ^ (uint32_t)INT32_MIN, 0, + &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_muladd(frs1, frs2, frs3 ^ (uint64_t)INT64_MIN, 0, + &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fnmsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2, frs3, 0, + &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fnmsub_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_muladd(frs1 ^ (uint64_t)INT64_MIN, frs2, frs3, 0, + &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fnmadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_muladd(frs1 ^ (uint32_t)INT32_MIN, frs2, + frs3 ^ (uint32_t)INT32_MIN, 0, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fnmadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t frs3, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_muladd(frs1 ^ (uint64_t)INT64_MIN, frs2, + frs3 ^ (uint64_t)INT64_MIN, 0, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fadd_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_add(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fsub_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_sub(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fmul_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_mul(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fdiv_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2, + uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_div(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fmin_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + require_fp; + frs1 = float32_minnum(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fmax_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + require_fp; + frs1 = float32_maxnum(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t frs1, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_sqrt(frs1, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +target_ulong helper_fle_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + require_fp; + frs1 = float32_le(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +target_ulong helper_flt_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + require_fp; + frs1 = float32_lt(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +target_ulong helper_feq_s(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + require_fp; + frs1 = float32_eq_quiet(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +target_ulong helper_fcvt_w_s(CPURISCVState *env, uint64_t frs1, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_to_int32(frs1, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +target_ulong helper_fcvt_wu_s(CPURISCVState *env, uint64_t frs1, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = (int32_t)float32_to_uint32(frs1, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +#if defined(TARGET_RISCV64) +uint64_t helper_fcvt_l_s(CPURISCVState *env, uint64_t frs1, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_to_int64(frs1, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fcvt_lu_s(CPURISCVState *env, uint64_t frs1, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float32_to_uint64(frs1, &env->fp_status); + set_fp_exceptions(); + return frs1; +} +#endif + +uint64_t helper_fcvt_s_w(CPURISCVState *env, target_ulong rs1, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + rs1 = int32_to_float32((int32_t)rs1, &env->fp_status); + set_fp_exceptions(); + return rs1; +} + +uint64_t helper_fcvt_s_wu(CPURISCVState *env, target_ulong rs1, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + rs1 = uint32_to_float32((uint32_t)rs1, &env->fp_status); + set_fp_exceptions(); + return rs1; +} + +#if defined(TARGET_RISCV64) +uint64_t helper_fcvt_s_l(CPURISCVState *env, uint64_t rs1, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + rs1 = int64_to_float32(rs1, &env->fp_status); + set_fp_exceptions(); + return rs1; +} + +uint64_t helper_fcvt_s_lu(CPURISCVState *env, uint64_t rs1, uint64_t rm) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + rs1 = uint64_to_float32(rs1, &env->fp_status); + set_fp_exceptions(); + return rs1; +} +#endif + +/* adapted from spike */ +#define isNaNF32UI(ui) (0xFF000000 < (uint32_t)((uint_fast32_t)ui << 1)) +#define signF32UI(a) ((bool)((uint32_t)a >> 31)) +#define expF32UI(a) ((int_fast16_t)(a >> 23) & 0xFF) +#define fracF32UI(a) (a & 0x007FFFFF) + +union ui32_f32 { uint32_t ui; uint32_t f; }; + +uint_fast16_t float32_classify(uint32_t a, float_status *status) +{ + union ui32_f32 uA; + uint_fast32_t uiA; + + uA.f = a; + uiA = uA.ui; + + uint_fast16_t infOrNaN = expF32UI(uiA) == 0xFF; + uint_fast16_t subnormalOrZero = expF32UI(uiA) == 0; + bool sign = signF32UI(uiA); + + return + (sign && infOrNaN && fracF32UI(uiA) == 0) << 0 | + (sign && !infOrNaN && !subnormalOrZero) << 1 | + (sign && subnormalOrZero && fracF32UI(uiA)) << 2 | + (sign && subnormalOrZero && fracF32UI(uiA) == 0) << 3 | + (!sign && infOrNaN && fracF32UI(uiA) == 0) << 7 | + (!sign && !infOrNaN && !subnormalOrZero) << 6 | + (!sign && subnormalOrZero && fracF32UI(uiA)) << 5 | + (!sign && subnormalOrZero && fracF32UI(uiA) == 0) << 4 | + (isNaNF32UI(uiA) && float32_is_signaling_nan(uiA, status)) << 8 | + (isNaNF32UI(uiA) && !float32_is_signaling_nan(uiA, status)) << 9; +} + +target_ulong helper_fclass_s(CPURISCVState *env, uint64_t frs1) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + set_float_rounding_mode(RM, &env->fp_status); + frs1 = float64_div(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + require_fp; + frs1 = float64_minnum(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2) +{ + require_fp; + frs1 = float64_maxnum(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1, uint64_t rm) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + frs1 = float64_eq_quiet(frs1, frs2, &env->fp_status); + set_fp_exceptions(); + return frs1; +} + +target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1, uint64_t rm) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + 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) +{ + require_fp; + frs1 = float64_classify(frs1, &env->fp_status); + return frs1; +} -- 2.7.0