Along with FP helper infrastructure, changes to softfloat-specialize Signed-off-by: Sagar Karandikar <sag...@eecs.berkeley.edu> --- fpu/softfloat-specialize.h | 7 ++- target-riscv/Makefile.objs | 2 +- target-riscv/fpu_helper.c | 151 +++++++++++++++++++++++++++++++++++++++++++++ target-riscv/helper.h | 10 +++ target-riscv/translate.c | 105 +++++++++++++++++++++++++++++++ 5 files changed, 271 insertions(+), 4 deletions(-) create mode 100644 target-riscv/fpu_helper.c
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index f5aed72..fa5986d 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) 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); #else if (status->snan_bit_is_one) { @@ -137,7 +138,7 @@ float64 float64_default_nan(float_status *status) #if defined(TARGET_SPARC) 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)); #else if (status->snan_bit_is_one) { @@ -181,7 +182,7 @@ float128 float128_default_nan(float_status *status) r.high = LIT64(0x7FFF7FFFFFFFFFFF); } else { r.low = LIT64(0x0000000000000000); -#if defined(TARGET_S390X) +#if defined(TARGET_S390X) || defined(TARGET_RISCV) r.high = LIT64(0x7FFF800000000000); #else r.high = LIT64(0xFFFF800000000000); diff --git a/target-riscv/Makefile.objs b/target-riscv/Makefile.objs index cb448a8..0149732 100644 --- a/target-riscv/Makefile.objs +++ b/target-riscv/Makefile.objs @@ -1 +1 @@ -obj-y += translate.o op_helper.o helper.o cpu.o +obj-y += translate.o op_helper.o helper.o cpu.o fpu_helper.o diff --git a/target-riscv/fpu_helper.c b/target-riscv/fpu_helper.c new file mode 100644 index 0000000..9023d10 --- /dev/null +++ b/target-riscv/fpu_helper.c @@ -0,0 +1,151 @@ +/* + * 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->csr[CSR_FRM]; \ +} \ +if (rm > 4) { \ + helper_raise_exception(env, RISCV_EXCP_ILLEGAL_INST); \ +} \ +ieee_rm[rm]; }) + +/* convert softfloat library flag numbers to RISC-V */ +unsigned int softfloat_flags_to_riscv(unsigned int flag) +{ + switch (flag) { + case float_flag_inexact: + return 1; + case float_flag_underflow: + return 2; + case float_flag_overflow: + return 4; + case float_flag_divbyzero: + return 8; + case float_flag_invalid: + return 16; + default: + return 0; + } +} + +/* adapted from Spike's decode.h:set_fp_exceptions */ +#define set_fp_exceptions() do { \ + env->csr[CSR_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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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) +{ + 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; +} diff --git a/target-riscv/helper.h b/target-riscv/helper.h index c489222..586abae5 100644 --- a/target-riscv/helper.h +++ b/target-riscv/helper.h @@ -6,3 +6,13 @@ DEF_HELPER_3(raise_exception_mbadaddr, noreturn, env, i32, tl) #if defined(TARGET_RISCV64) DEF_HELPER_FLAGS_3(mulhsu, TCG_CALL_NO_RWG_SE, tl, env, tl, tl) #endif + +/* Floating Point - fused */ +DEF_HELPER_FLAGS_5(fmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_5(fmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_5(fmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_5(fmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_5(fnmsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_5(fnmsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_5(fnmadd_s, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64) +DEF_HELPER_FLAGS_5(fnmadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64, i64) diff --git a/target-riscv/translate.c b/target-riscv/translate.c index af82eab..07f24e8 100644 --- a/target-riscv/translate.c +++ b/target-riscv/translate.c @@ -792,6 +792,95 @@ static inline void gen_atomic(DisasContext *ctx, uint32_t opc, tcg_temp_free(dat); } +static inline void gen_fp_fmadd(DisasContext *ctx, uint32_t opc, int rd, + int rs1, int rs2, int rs3, int rm) +{ + TCGv_i64 rm_reg = tcg_temp_new_i64(); + tcg_gen_movi_i64(rm_reg, rm); + + switch (opc) { + case OPC_RISC_FMADD_S: + gen_helper_fmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + cpu_fpr[rs3], rm_reg); + break; + case OPC_RISC_FMADD_D: + gen_helper_fmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + cpu_fpr[rs3], rm_reg); + break; + default: + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + break; + } + tcg_temp_free_i64(rm_reg); + +} + +static inline void gen_fp_fmsub(DisasContext *ctx, uint32_t opc, int rd, + int rs1, int rs2, int rs3, int rm) +{ + TCGv_i64 rm_reg = tcg_temp_new_i64(); + tcg_gen_movi_i64(rm_reg, rm); + + switch (opc) { + case OPC_RISC_FMSUB_S: + gen_helper_fmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + cpu_fpr[rs3], rm_reg); + break; + case OPC_RISC_FMSUB_D: + gen_helper_fmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + cpu_fpr[rs3], rm_reg); + break; + default: + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + break; + } + tcg_temp_free_i64(rm_reg); +} + +static inline void gen_fp_fnmsub(DisasContext *ctx, uint32_t opc, int rd, + int rs1, int rs2, int rs3, int rm) +{ + TCGv_i64 rm_reg = tcg_temp_new_i64(); + tcg_gen_movi_i64(rm_reg, rm); + + switch (opc) { + case OPC_RISC_FNMSUB_S: + gen_helper_fnmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + cpu_fpr[rs3], rm_reg); + break; + case OPC_RISC_FNMSUB_D: + gen_helper_fnmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + cpu_fpr[rs3], rm_reg); + break; + default: + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + break; + } + tcg_temp_free_i64(rm_reg); +} + +static inline void gen_fp_fnmadd(DisasContext *ctx, uint32_t opc, int rd, + int rs1, int rs2, int rs3, int rm) +{ + TCGv_i64 rm_reg = tcg_temp_new_i64(); + tcg_gen_movi_i64(rm_reg, rm); + + switch (opc) { + case OPC_RISC_FNMADD_S: + gen_helper_fnmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + cpu_fpr[rs3], rm_reg); + break; + case OPC_RISC_FNMADD_D: + gen_helper_fnmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2], + cpu_fpr[rs3], rm_reg); + break; + default: + kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); + break; + } + tcg_temp_free_i64(rm_reg); +} + static void decode_opc(CPURISCVState *env, DisasContext *ctx) { int rs1; @@ -900,6 +989,22 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx) case OPC_RISC_ATOMIC: gen_atomic(ctx, MASK_OP_ATOMIC(ctx->opcode), rd, rs1, rs2); break; + case OPC_RISC_FMADD: + gen_fp_fmadd(ctx, MASK_OP_FP_FMADD(ctx->opcode), rd, rs1, rs2, + GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); + break; + case OPC_RISC_FMSUB: + gen_fp_fmsub(ctx, MASK_OP_FP_FMSUB(ctx->opcode), rd, rs1, rs2, + GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); + break; + case OPC_RISC_FNMSUB: + gen_fp_fnmsub(ctx, MASK_OP_FP_FNMSUB(ctx->opcode), rd, rs1, rs2, + GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); + break; + case OPC_RISC_FNMADD: + gen_fp_fnmadd(ctx, MASK_OP_FP_FNMADD(ctx->opcode), rd, rs1, rs2, + GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); + break; default: kill_unknown(ctx, RISCV_EXCP_ILLEGAL_INST); break; -- 2.9.3