xemu/target/riscv/insn_trans/trans_rvf.c.inc
Deepak Gupta f21b36a022 target/riscv: update decode_save_opc to store extra word2
Extra word 2 is stored during tcg compile and `decode_save_opc` needs
additional argument in order to pass the value. This will be used during
unwind to get extra information about instruction like how to massage
exceptions. Updated all callsites as well.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/594

Signed-off-by: Deepak Gupta <debug@rivosinc.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Alistair Francis <alistair.francis@wdc.com>
Message-ID: <20241008225010.1861630-16-debug@rivosinc.com>
Signed-off-by: Alistair Francis <alistair.francis@wdc.com>
2024-10-30 11:22:08 +10:00

605 lines
16 KiB
C++

/*
* RISC-V translation routines for the RV64F Standard Extension.
*
* Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu
* Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de
* Bastian Koppelmann, kbastian@mail.uni-paderborn.de
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2 or later, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define REQUIRE_FPU do {\
if (ctx->mstatus_fs == EXT_STATUS_DISABLED) { \
ctx->virt_inst_excp = ctx->virt_enabled && ctx->cfg_ptr->ext_zfinx; \
return false; \
} \
} while (0)
#define REQUIRE_ZFINX_OR_F(ctx) do {\
if (!ctx->cfg_ptr->ext_zfinx) { \
REQUIRE_EXT(ctx, RVF); \
} \
} while (0)
#define REQUIRE_ZCF_OR_FC(ctx) do { \
if (!ctx->cfg_ptr->ext_zcf) { \
if (!has_ext(ctx, RVF) || !has_ext(ctx, RVC)) { \
return false; \
} \
} \
} while (0)
static bool trans_flw(DisasContext *ctx, arg_flw *a)
{
TCGv_i64 dest;
TCGv addr;
MemOp memop = MO_TEUL;
REQUIRE_FPU;
REQUIRE_EXT(ctx, RVF);
if (ctx->cfg_ptr->ext_zama16b) {
memop |= MO_ATOM_WITHIN16;
}
decode_save_opc(ctx, 0);
addr = get_address(ctx, a->rs1, a->imm);
dest = cpu_fpr[a->rd];
tcg_gen_qemu_ld_i64(dest, addr, ctx->mem_idx, memop);
gen_nanbox_s(dest, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fsw(DisasContext *ctx, arg_fsw *a)
{
TCGv addr;
MemOp memop = MO_TEUL;
REQUIRE_FPU;
REQUIRE_EXT(ctx, RVF);
if (ctx->cfg_ptr->ext_zama16b) {
memop |= MO_ATOM_WITHIN16;
}
decode_save_opc(ctx, 0);
addr = get_address(ctx, a->rs1, a->imm);
tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], addr, ctx->mem_idx, memop);
return true;
}
static bool trans_c_flw(DisasContext *ctx, arg_flw *a)
{
REQUIRE_ZCF_OR_FC(ctx);
return trans_flw(ctx, a);
}
static bool trans_c_fsw(DisasContext *ctx, arg_fsw *a)
{
REQUIRE_ZCF_OR_FC(ctx);
return trans_fsw(ctx, a);
}
static bool trans_fmadd_s(DisasContext *ctx, arg_fmadd_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3);
gen_set_rm(ctx, a->rm);
gen_helper_fmadd_s(dest, tcg_env, src1, src2, src3);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fmsub_s(DisasContext *ctx, arg_fmsub_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3);
gen_set_rm(ctx, a->rm);
gen_helper_fmsub_s(dest, tcg_env, src1, src2, src3);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fnmsub_s(DisasContext *ctx, arg_fnmsub_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3);
gen_set_rm(ctx, a->rm);
gen_helper_fnmsub_s(dest, tcg_env, src1, src2, src3);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fnmadd_s(DisasContext *ctx, arg_fnmadd_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
TCGv_i64 src3 = get_fpr_hs(ctx, a->rs3);
gen_set_rm(ctx, a->rm);
gen_helper_fnmadd_s(dest, tcg_env, src1, src2, src3);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fadd_s(DisasContext *ctx, arg_fadd_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
gen_set_rm(ctx, a->rm);
gen_helper_fadd_s(dest, tcg_env, src1, src2);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fsub_s(DisasContext *ctx, arg_fsub_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
gen_set_rm(ctx, a->rm);
gen_helper_fsub_s(dest, tcg_env, src1, src2);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fmul_s(DisasContext *ctx, arg_fmul_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
gen_set_rm(ctx, a->rm);
gen_helper_fmul_s(dest, tcg_env, src1, src2);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fdiv_s(DisasContext *ctx, arg_fdiv_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
gen_set_rm(ctx, a->rm);
gen_helper_fdiv_s(dest, tcg_env, src1, src2);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fsqrt_s(DisasContext *ctx, arg_fsqrt_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
gen_set_rm(ctx, a->rm);
gen_helper_fsqrt_s(dest, tcg_env, src1);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fsgnj_s(DisasContext *ctx, arg_fsgnj_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
if (a->rs1 == a->rs2) { /* FMOV */
if (!ctx->cfg_ptr->ext_zfinx) {
gen_check_nanbox_s(dest, src1);
} else {
tcg_gen_ext32s_i64(dest, src1);
}
} else { /* FSGNJ */
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
if (!ctx->cfg_ptr->ext_zfinx) {
TCGv_i64 rs1 = tcg_temp_new_i64();
TCGv_i64 rs2 = tcg_temp_new_i64();
gen_check_nanbox_s(rs1, src1);
gen_check_nanbox_s(rs2, src2);
/* This formulation retains the nanboxing of rs2 in normal 'F'. */
tcg_gen_deposit_i64(dest, rs2, rs1, 0, 31);
} else {
tcg_gen_deposit_i64(dest, src2, src1, 0, 31);
tcg_gen_ext32s_i64(dest, dest);
}
}
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fsgnjn_s(DisasContext *ctx, arg_fsgnjn_s *a)
{
TCGv_i64 rs1, rs2, mask;
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
rs1 = tcg_temp_new_i64();
if (!ctx->cfg_ptr->ext_zfinx) {
gen_check_nanbox_s(rs1, src1);
} else {
tcg_gen_mov_i64(rs1, src1);
}
if (a->rs1 == a->rs2) { /* FNEG */
tcg_gen_xori_i64(dest, rs1, MAKE_64BIT_MASK(31, 1));
} else {
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
rs2 = tcg_temp_new_i64();
if (!ctx->cfg_ptr->ext_zfinx) {
gen_check_nanbox_s(rs2, src2);
} else {
tcg_gen_mov_i64(rs2, src2);
}
/*
* Replace bit 31 in rs1 with inverse in rs2.
* This formulation retains the nanboxing of rs1.
*/
mask = tcg_constant_i64(~MAKE_64BIT_MASK(31, 1));
tcg_gen_nor_i64(rs2, rs2, mask);
tcg_gen_and_i64(dest, mask, rs1);
tcg_gen_or_i64(dest, dest, rs2);
}
/* signed-extended instead of nanboxing for result if enable zfinx */
if (ctx->cfg_ptr->ext_zfinx) {
tcg_gen_ext32s_i64(dest, dest);
}
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fsgnjx_s(DisasContext *ctx, arg_fsgnjx_s *a)
{
TCGv_i64 rs1, rs2;
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
rs1 = tcg_temp_new_i64();
if (!ctx->cfg_ptr->ext_zfinx) {
gen_check_nanbox_s(rs1, src1);
} else {
tcg_gen_mov_i64(rs1, src1);
}
if (a->rs1 == a->rs2) { /* FABS */
tcg_gen_andi_i64(dest, rs1, ~MAKE_64BIT_MASK(31, 1));
} else {
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
rs2 = tcg_temp_new_i64();
if (!ctx->cfg_ptr->ext_zfinx) {
gen_check_nanbox_s(rs2, src2);
} else {
tcg_gen_mov_i64(rs2, src2);
}
/*
* Xor bit 31 in rs1 with that in rs2.
* This formulation retains the nanboxing of rs1.
*/
tcg_gen_andi_i64(dest, rs2, MAKE_64BIT_MASK(31, 1));
tcg_gen_xor_i64(dest, rs1, dest);
}
/* signed-extended instead of nanboxing for result if enable zfinx */
if (ctx->cfg_ptr->ext_zfinx) {
tcg_gen_ext32s_i64(dest, dest);
}
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fmin_s(DisasContext *ctx, arg_fmin_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
gen_helper_fmin_s(dest, tcg_env, src1, src2);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fmax_s(DisasContext *ctx, arg_fmax_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
gen_helper_fmax_s(dest, tcg_env, src1, src2);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fcvt_w_s(DisasContext *ctx, arg_fcvt_w_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv dest = dest_gpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
gen_set_rm(ctx, a->rm);
gen_helper_fcvt_w_s(dest, tcg_env, src1);
gen_set_gpr(ctx, a->rd, dest);
return true;
}
static bool trans_fcvt_wu_s(DisasContext *ctx, arg_fcvt_wu_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv dest = dest_gpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
gen_set_rm(ctx, a->rm);
gen_helper_fcvt_wu_s(dest, tcg_env, src1);
gen_set_gpr(ctx, a->rd, dest);
return true;
}
static bool trans_fmv_x_w(DisasContext *ctx, arg_fmv_x_w *a)
{
/* NOTE: This was FMV.X.S in an earlier version of the ISA spec! */
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv dest = dest_gpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
#if defined(TARGET_RISCV64)
tcg_gen_ext32s_tl(dest, src1);
#else
tcg_gen_extrl_i64_i32(dest, src1);
#endif
gen_set_gpr(ctx, a->rd, dest);
return true;
}
static bool trans_feq_s(DisasContext *ctx, arg_feq_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv dest = dest_gpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
gen_helper_feq_s(dest, tcg_env, src1, src2);
gen_set_gpr(ctx, a->rd, dest);
return true;
}
static bool trans_flt_s(DisasContext *ctx, arg_flt_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv dest = dest_gpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
gen_helper_flt_s(dest, tcg_env, src1, src2);
gen_set_gpr(ctx, a->rd, dest);
return true;
}
static bool trans_fle_s(DisasContext *ctx, arg_fle_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv dest = dest_gpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
gen_helper_fle_s(dest, tcg_env, src1, src2);
gen_set_gpr(ctx, a->rd, dest);
return true;
}
static bool trans_fclass_s(DisasContext *ctx, arg_fclass_s *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv dest = dest_gpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
gen_helper_fclass_s(dest, tcg_env, src1);
gen_set_gpr(ctx, a->rd, dest);
return true;
}
static bool trans_fcvt_s_w(DisasContext *ctx, arg_fcvt_s_w *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN);
gen_set_rm(ctx, a->rm);
gen_helper_fcvt_s_w(dest, tcg_env, src);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fcvt_s_wu(DisasContext *ctx, arg_fcvt_s_wu *a)
{
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
gen_set_rm(ctx, a->rm);
gen_helper_fcvt_s_wu(dest, tcg_env, src);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fmv_w_x(DisasContext *ctx, arg_fmv_w_x *a)
{
/* NOTE: This was FMV.S.X in an earlier version of the ISA spec! */
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
tcg_gen_extu_tl_i64(dest, src);
gen_nanbox_s(dest, dest);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fcvt_l_s(DisasContext *ctx, arg_fcvt_l_s *a)
{
REQUIRE_64BIT(ctx);
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv dest = dest_gpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
gen_set_rm(ctx, a->rm);
gen_helper_fcvt_l_s(dest, tcg_env, src1);
gen_set_gpr(ctx, a->rd, dest);
return true;
}
static bool trans_fcvt_lu_s(DisasContext *ctx, arg_fcvt_lu_s *a)
{
REQUIRE_64BIT(ctx);
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv dest = dest_gpr(ctx, a->rd);
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
gen_set_rm(ctx, a->rm);
gen_helper_fcvt_lu_s(dest, tcg_env, src1);
gen_set_gpr(ctx, a->rd, dest);
return true;
}
static bool trans_fcvt_s_l(DisasContext *ctx, arg_fcvt_s_l *a)
{
REQUIRE_64BIT(ctx);
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv src = get_gpr(ctx, a->rs1, EXT_SIGN);
gen_set_rm(ctx, a->rm);
gen_helper_fcvt_s_l(dest, tcg_env, src);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}
static bool trans_fcvt_s_lu(DisasContext *ctx, arg_fcvt_s_lu *a)
{
REQUIRE_64BIT(ctx);
REQUIRE_FPU;
REQUIRE_ZFINX_OR_F(ctx);
TCGv_i64 dest = dest_fpr(ctx, a->rd);
TCGv src = get_gpr(ctx, a->rs1, EXT_ZERO);
gen_set_rm(ctx, a->rm);
gen_helper_fcvt_s_lu(dest, tcg_env, src);
gen_set_fpr_hs(ctx, a->rd, dest);
mark_fs_dirty(ctx);
return true;
}