In gen_mcrfs() the FPSCR nibble mask is computed as:
`~((0xF << shift) & FP_EX_CLEAR_BITS)`
Here, 0xF is of type int, so the left shift is performed in
32-bit signed arithmetic. For bfa=0 we get shift=28,
and (0xF << 28) = 0xF0000000, which is not representable as a 32-bit
signed int. Static analyzers flag this as a potential integer
overflow.
Found by Linux Verification Center (linuxtesting.org) with SVACE.
Signed-off-by: Denis Sergeev <zeff@altlinux.org>
Reviewed-by: Chinmay Rath <rathc@linux.ibm.com>
Signed-off-by: Harsh Prateek Bora <harshpb@linux.ibm.com>
Link: https://lore.kernel.org/r/20250915080118.29898-1-zeff@altlinux.org
Message-ID: <20250915080118.29898-1-zeff@altlinux.org>
981 lines
28 KiB
C++
981 lines
28 KiB
C++
/*
|
|
* translate-fp.c
|
|
*
|
|
* Standard FPU translation
|
|
*/
|
|
|
|
static inline void gen_reset_fpstatus(void)
|
|
{
|
|
gen_helper_reset_fpstatus(tcg_env);
|
|
}
|
|
|
|
static inline void gen_compute_fprf_float64(TCGv_i64 arg)
|
|
{
|
|
gen_helper_compute_fprf_float64(tcg_env, arg);
|
|
gen_helper_float_check_status(tcg_env);
|
|
}
|
|
|
|
#if defined(TARGET_PPC64)
|
|
static void gen_set_cr1_from_fpscr(DisasContext *ctx)
|
|
{
|
|
TCGv_i32 tmp = tcg_temp_new_i32();
|
|
tcg_gen_trunc_tl_i32(tmp, cpu_fpscr);
|
|
tcg_gen_shri_i32(cpu_crf[1], tmp, 28);
|
|
}
|
|
#else
|
|
static void gen_set_cr1_from_fpscr(DisasContext *ctx)
|
|
{
|
|
tcg_gen_shri_tl(cpu_crf[1], cpu_fpscr, 28);
|
|
}
|
|
#endif
|
|
|
|
/*** Floating-Point arithmetic ***/
|
|
static bool do_helper_acb(DisasContext *ctx, arg_A *a,
|
|
void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64,
|
|
TCGv_i64, TCGv_i64))
|
|
{
|
|
TCGv_i64 t0, t1, t2, t3;
|
|
REQUIRE_INSNS_FLAGS(ctx, FLOAT);
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
t2 = tcg_temp_new_i64();
|
|
t3 = tcg_temp_new_i64();
|
|
gen_reset_fpstatus();
|
|
get_fpr(t0, a->fra);
|
|
get_fpr(t1, a->frc);
|
|
get_fpr(t2, a->frb);
|
|
helper(t3, tcg_env, t0, t1, t2);
|
|
set_fpr(a->frt, t3);
|
|
gen_compute_fprf_float64(t3);
|
|
if (unlikely(a->rc)) {
|
|
gen_set_cr1_from_fpscr(ctx);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool do_helper_ab(DisasContext *ctx, arg_A_tab *a,
|
|
void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64,
|
|
TCGv_i64))
|
|
{
|
|
TCGv_i64 t0, t1, t2;
|
|
REQUIRE_INSNS_FLAGS(ctx, FLOAT);
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
t2 = tcg_temp_new_i64();
|
|
gen_reset_fpstatus();
|
|
get_fpr(t0, a->fra);
|
|
get_fpr(t1, a->frb);
|
|
helper(t2, tcg_env, t0, t1);
|
|
set_fpr(a->frt, t2);
|
|
gen_compute_fprf_float64(t2);
|
|
if (unlikely(a->rc)) {
|
|
gen_set_cr1_from_fpscr(ctx);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool do_helper_ac(DisasContext *ctx, arg_A_tac *a,
|
|
void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64,
|
|
TCGv_i64))
|
|
{
|
|
TCGv_i64 t0, t1, t2;
|
|
REQUIRE_INSNS_FLAGS(ctx, FLOAT);
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
t2 = tcg_temp_new_i64();
|
|
gen_reset_fpstatus();
|
|
get_fpr(t0, a->fra);
|
|
get_fpr(t1, a->frc);
|
|
helper(t2, tcg_env, t0, t1);
|
|
set_fpr(a->frt, t2);
|
|
gen_compute_fprf_float64(t2);
|
|
if (unlikely(a->rc)) {
|
|
gen_set_cr1_from_fpscr(ctx);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool do_round_convert(DisasContext *ctx, arg_X_tb_rc *a,
|
|
void (*helper)(TCGv_i64, TCGv_env, TCGv_i64),
|
|
bool set_fprf)
|
|
{
|
|
TCGv_i64 t0, t1;
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
gen_reset_fpstatus();
|
|
get_fpr(t0, a->rb);
|
|
helper(t1, tcg_env, t0);
|
|
set_fpr(a->rt, t1);
|
|
if (set_fprf) {
|
|
gen_helper_compute_fprf_float64(tcg_env, t1);
|
|
}
|
|
gen_helper_float_check_status(tcg_env);
|
|
if (unlikely(a->rc)) {
|
|
gen_set_cr1_from_fpscr(ctx);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool do_helper_bs(DisasContext *ctx, arg_A_tb *a,
|
|
void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64))
|
|
{
|
|
TCGv_i64 t0, t1;
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
gen_reset_fpstatus();
|
|
get_fpr(t0, a->frb);
|
|
helper(t1, tcg_env, t0);
|
|
set_fpr(a->frt, t1);
|
|
gen_compute_fprf_float64(t1);
|
|
if (unlikely(a->rc)) {
|
|
gen_set_cr1_from_fpscr(ctx);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool trans_FSEL(DisasContext *ctx, arg_A *a)
|
|
{
|
|
TCGv_i64 t0, t1, t2;
|
|
|
|
REQUIRE_INSNS_FLAGS(ctx, FLOAT_FSEL);
|
|
REQUIRE_FPU(ctx);
|
|
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
t2 = tcg_temp_new_i64();
|
|
|
|
get_fpr(t0, a->fra);
|
|
get_fpr(t1, a->frb);
|
|
get_fpr(t2, a->frc);
|
|
|
|
gen_helper_FSEL(t0, t0, t1, t2);
|
|
set_fpr(a->frt, t0);
|
|
if (a->rc) {
|
|
gen_set_cr1_from_fpscr(ctx);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool do_helper_fsqrt(DisasContext *ctx, arg_A_tb *a,
|
|
void (*helper)(TCGv_i64, TCGv_ptr, TCGv_i64))
|
|
{
|
|
TCGv_i64 t0, t1;
|
|
|
|
REQUIRE_INSNS_FLAGS(ctx, FLOAT_FSQRT);
|
|
REQUIRE_FPU(ctx);
|
|
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
|
|
gen_reset_fpstatus();
|
|
get_fpr(t0, a->frb);
|
|
helper(t1, tcg_env, t0);
|
|
set_fpr(a->frt, t1);
|
|
gen_compute_fprf_float64(t1);
|
|
if (unlikely(a->rc != 0)) {
|
|
gen_set_cr1_from_fpscr(ctx);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
TRANS(FADD, do_helper_ab, gen_helper_FADD);
|
|
TRANS(FADDS, do_helper_ab, gen_helper_FADDS);
|
|
TRANS(FSUB, do_helper_ab, gen_helper_FSUB);
|
|
TRANS(FSUBS, do_helper_ab, gen_helper_FSUBS);
|
|
TRANS(FDIV, do_helper_ab, gen_helper_FDIV);
|
|
TRANS(FDIVS, do_helper_ab, gen_helper_FDIVS);
|
|
TRANS(FMUL, do_helper_ac, gen_helper_FMUL);
|
|
TRANS(FMULS, do_helper_ac, gen_helper_FMULS);
|
|
|
|
TRANS(FMADD, do_helper_acb, gen_helper_FMADD);
|
|
TRANS(FMADDS, do_helper_acb, gen_helper_FMADDS);
|
|
TRANS(FMSUB, do_helper_acb, gen_helper_FMSUB);
|
|
TRANS(FMSUBS, do_helper_acb, gen_helper_FMSUBS);
|
|
|
|
TRANS(FNMADD, do_helper_acb, gen_helper_FNMADD);
|
|
TRANS(FNMADDS, do_helper_acb, gen_helper_FNMADDS);
|
|
TRANS(FNMSUB, do_helper_acb, gen_helper_FNMSUB);
|
|
TRANS(FNMSUBS, do_helper_acb, gen_helper_FNMSUBS);
|
|
|
|
TRANS_FLAGS(FLOAT_EXT, FRE, do_helper_bs, gen_helper_FRE);
|
|
TRANS_FLAGS(FLOAT_FRES, FRES, do_helper_bs, gen_helper_FRES);
|
|
TRANS_FLAGS(FLOAT_FRSQRTE, FRSQRTE, do_helper_bs, gen_helper_FRSQRTE);
|
|
TRANS_FLAGS(FLOAT_FRSQRTES, FRSQRTES, do_helper_bs, gen_helper_FRSQRTES);
|
|
|
|
TRANS(FSQRT, do_helper_fsqrt, gen_helper_FSQRT);
|
|
TRANS(FSQRTS, do_helper_fsqrt, gen_helper_FSQRTS);
|
|
|
|
/*** Floating-Point round & convert ***/
|
|
TRANS_FLAGS(FLOAT, FRSP, do_round_convert, gen_helper_FRSP, true);
|
|
TRANS_FLAGS(FLOAT_EXT, FRIN, do_round_convert, gen_helper_FRIN, true);
|
|
TRANS_FLAGS(FLOAT_EXT, FRIZ, do_round_convert, gen_helper_FRIZ, true);
|
|
TRANS_FLAGS(FLOAT_EXT, FRIP, do_round_convert, gen_helper_FRIP, true);
|
|
TRANS_FLAGS(FLOAT_EXT, FRIM, do_round_convert, gen_helper_FRIM, true);
|
|
|
|
TRANS_FLAGS(FLOAT, FCTIW, do_round_convert, gen_helper_FCTIW, false);
|
|
TRANS_FLAGS2(FP_CVT_ISA206, FCTIWU, do_round_convert, gen_helper_FCTIWU, false);
|
|
TRANS_FLAGS(FLOAT, FCTIWZ, do_round_convert, gen_helper_FCTIWZ, false);
|
|
TRANS_FLAGS2(FP_CVT_ISA206, FCTIWUZ, do_round_convert, gen_helper_FCTIWUZ, false);
|
|
|
|
TRANS_FLAGS2(FP_CVT_S64, FCTID, do_round_convert, gen_helper_FCTID, false);
|
|
TRANS_FLAGS2(FP_CVT_ISA206, FCTIDU, do_round_convert, gen_helper_FCTIDU, false);
|
|
TRANS_FLAGS2(FP_CVT_S64, FCTIDZ, do_round_convert, gen_helper_FCTIDZ, false);
|
|
TRANS_FLAGS2(FP_CVT_ISA206, FCTIDUZ, do_round_convert, gen_helper_FCTIDUZ, false);
|
|
|
|
TRANS_FLAGS2(FP_CVT_S64, FCFID, do_round_convert, gen_helper_FCFID, true);
|
|
TRANS_FLAGS2(FP_CVT_ISA206, FCFIDS, do_round_convert, gen_helper_FCFIDS, false);
|
|
TRANS_FLAGS2(FP_CVT_ISA206, FCFIDU, do_round_convert, gen_helper_FCFIDU, false);
|
|
TRANS_FLAGS2(FP_CVT_ISA206, FCFIDUS, do_round_convert, gen_helper_FCFIDUS, false);
|
|
|
|
static bool trans_FTDIV(DisasContext *ctx, arg_X_bf *a)
|
|
{
|
|
TCGv_i64 t0, t1;
|
|
REQUIRE_INSNS_FLAGS2(ctx, FP_TST_ISA206);
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
get_fpr(t0, a->ra);
|
|
get_fpr(t1, a->rb);
|
|
gen_helper_FTDIV(cpu_crf[a->bf], t0, t1);
|
|
return true;
|
|
}
|
|
|
|
static bool trans_FTSQRT(DisasContext *ctx, arg_X_bf_b *a)
|
|
{
|
|
TCGv_i64 t0;
|
|
REQUIRE_INSNS_FLAGS2(ctx, FP_TST_ISA206);
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
get_fpr(t0, a->rb);
|
|
gen_helper_FTSQRT(cpu_crf[a->bf], t0);
|
|
return true;
|
|
}
|
|
|
|
/*** Floating-Point compare ***/
|
|
static bool do_helper_cmp(DisasContext *ctx, arg_X_bf *a,
|
|
void (*helper)(TCGv_env, TCGv_i64, TCGv_i64,
|
|
TCGv_i32))
|
|
{
|
|
TCGv_i32 crf;
|
|
TCGv_i64 t0, t1;
|
|
REQUIRE_INSNS_FLAGS(ctx, FLOAT);
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
gen_reset_fpstatus();
|
|
crf = tcg_constant_i32(a->bf);
|
|
get_fpr(t0, a->ra);
|
|
get_fpr(t1, a->rb);
|
|
helper(tcg_env, t0, t1, crf);
|
|
gen_helper_float_check_status(tcg_env);
|
|
return true;
|
|
}
|
|
|
|
TRANS(FCMPU, do_helper_cmp, gen_helper_FCMPU);
|
|
TRANS(FCMPO, do_helper_cmp, gen_helper_FCMPO);
|
|
|
|
/*** Floating-point move ***/
|
|
|
|
/* fmr - fmr. */
|
|
/* XXX: beware that fmr never checks for NaNs nor update FPSCR */
|
|
static bool trans_FMR(DisasContext *ctx, arg_FMR *a)
|
|
{
|
|
TCGv_i64 t0;
|
|
REQUIRE_INSNS_FLAGS(ctx, FLOAT);
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
get_fpr(t0, a->rb);
|
|
set_fpr(a->rt, t0);
|
|
if (unlikely(a->rc)) {
|
|
gen_set_cr1_from_fpscr(ctx);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* XXX: beware that f{neg, abs, nabs} never checks for NaNs nor update FPSCR */
|
|
static bool do_move_b(DisasContext *ctx, arg_X_tb_rc *a, int64_t val,
|
|
void (*tcg_op)(TCGv_i64, TCGv_i64, int64_t))
|
|
{
|
|
TCGv_i64 t0, t1;
|
|
REQUIRE_INSNS_FLAGS(ctx, FLOAT);
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
get_fpr(t0, a->rb);
|
|
tcg_op(t1, t0, val);
|
|
set_fpr(a->rt, t1);
|
|
if (unlikely(a->rc)) {
|
|
gen_set_cr1_from_fpscr(ctx);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
TRANS(FNEG, do_move_b, 1ULL << 63, tcg_gen_xori_i64);
|
|
TRANS(FABS, do_move_b, ~(1ULL << 63), tcg_gen_andi_i64);
|
|
TRANS(FNABS, do_move_b, 1ULL << 63, tcg_gen_ori_i64);
|
|
|
|
/* fcpsgn: PowerPC 2.05 specification */
|
|
/* XXX: beware that fcpsgn never checks for NaNs nor update FPSCR */
|
|
static bool trans_FCPSGN(DisasContext *ctx, arg_FCPSGN *a)
|
|
{
|
|
TCGv_i64 t0, t1, t2;
|
|
REQUIRE_INSNS_FLAGS2(ctx, ISA205);
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
t2 = tcg_temp_new_i64();
|
|
get_fpr(t0, a->ra);
|
|
get_fpr(t1, a->rb);
|
|
tcg_gen_deposit_i64(t2, t0, t1, 0, 63);
|
|
set_fpr(a->rt, t2);
|
|
if (unlikely(a->rc)) {
|
|
gen_set_cr1_from_fpscr(ctx);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool trans_FMRGEW(DisasContext *ctx, arg_FMRGEW *a)
|
|
{
|
|
TCGv_i64 t0, t1, t2;
|
|
REQUIRE_INSNS_FLAGS2(ctx, VSX207);
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
t2 = tcg_temp_new_i64();
|
|
get_fpr(t1, a->rb);
|
|
tcg_gen_shri_i64(t0, t1, 32);
|
|
get_fpr(t1, a->ra);
|
|
tcg_gen_deposit_i64(t2, t1, t0, 0, 32);
|
|
set_fpr(a->rt, t2);
|
|
return true;
|
|
}
|
|
|
|
static bool trans_FMRGOW(DisasContext *ctx, arg_FMRGOW *a)
|
|
{
|
|
TCGv_i64 t0, t1, t2;
|
|
REQUIRE_INSNS_FLAGS2(ctx, VSX207);
|
|
REQUIRE_FPU(ctx);
|
|
t0 = tcg_temp_new_i64();
|
|
t1 = tcg_temp_new_i64();
|
|
t2 = tcg_temp_new_i64();
|
|
get_fpr(t0, a->rb);
|
|
get_fpr(t1, a->ra);
|
|
tcg_gen_deposit_i64(t2, t0, t1, 32, 32);
|
|
set_fpr(a->rt, t2);
|
|
return true;
|
|
}
|
|
|
|
/*** Floating-Point status & ctrl register ***/
|
|
|
|
/* mcrfs */
|
|
static void gen_mcrfs(DisasContext *ctx)
|
|
{
|
|
TCGv tmp = tcg_temp_new();
|
|
TCGv_i32 tmask;
|
|
TCGv_i64 tnew_fpscr = tcg_temp_new_i64();
|
|
int bfa;
|
|
int nibble;
|
|
int shift;
|
|
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
bfa = crfS(ctx->opcode);
|
|
nibble = 7 - bfa;
|
|
shift = 4 * nibble;
|
|
tcg_gen_shri_tl(tmp, cpu_fpscr, shift);
|
|
tcg_gen_trunc_tl_i32(cpu_crf[crfD(ctx->opcode)], tmp);
|
|
tcg_gen_andi_i32(cpu_crf[crfD(ctx->opcode)], cpu_crf[crfD(ctx->opcode)],
|
|
0xf);
|
|
tcg_gen_extu_tl_i64(tnew_fpscr, cpu_fpscr);
|
|
/* Only the exception bits (including FX) should be cleared if read */
|
|
tcg_gen_andi_i64(tnew_fpscr, tnew_fpscr,
|
|
~(MAKE_64BIT_MASK(shift, 4) & FP_EX_CLEAR_BITS));
|
|
/* FEX and VX need to be updated, so don't set fpscr directly */
|
|
tmask = tcg_constant_i32(1 << nibble);
|
|
gen_helper_store_fpscr(tcg_env, tnew_fpscr, tmask);
|
|
}
|
|
|
|
static TCGv_i64 place_from_fpscr(int rt, uint64_t mask)
|
|
{
|
|
TCGv_i64 fpscr = tcg_temp_new_i64();
|
|
TCGv_i64 fpscr_masked = tcg_temp_new_i64();
|
|
|
|
tcg_gen_extu_tl_i64(fpscr, cpu_fpscr);
|
|
tcg_gen_andi_i64(fpscr_masked, fpscr, mask);
|
|
set_fpr(rt, fpscr_masked);
|
|
|
|
return fpscr;
|
|
}
|
|
|
|
static void store_fpscr_masked(TCGv_i64 fpscr, uint64_t clear_mask,
|
|
TCGv_i64 set_mask, uint32_t store_mask)
|
|
{
|
|
TCGv_i64 fpscr_masked = tcg_temp_new_i64();
|
|
TCGv_i32 st_mask = tcg_constant_i32(store_mask);
|
|
|
|
tcg_gen_andi_i64(fpscr_masked, fpscr, ~clear_mask);
|
|
tcg_gen_or_i64(fpscr_masked, fpscr_masked, set_mask);
|
|
gen_helper_store_fpscr(tcg_env, fpscr_masked, st_mask);
|
|
}
|
|
|
|
static bool trans_MFFS_ISA207(DisasContext *ctx, arg_X_t_rc *a)
|
|
{
|
|
if (!(ctx->insns_flags2 & PPC2_ISA300)) {
|
|
/*
|
|
* Before Power ISA v3.0, MFFS bits 11~15 were reserved, any instruction
|
|
* with OPCD=63 and XO=583 should be decoded as MFFS.
|
|
*/
|
|
return trans_MFFS(ctx, a);
|
|
}
|
|
/*
|
|
* For Power ISA v3.0+, return false and let the pattern group
|
|
* select the correct instruction.
|
|
*/
|
|
return false;
|
|
}
|
|
|
|
static bool trans_MFFS(DisasContext *ctx, arg_X_t_rc *a)
|
|
{
|
|
REQUIRE_FPU(ctx);
|
|
|
|
gen_reset_fpstatus();
|
|
place_from_fpscr(a->rt, UINT64_MAX);
|
|
if (a->rc) {
|
|
gen_set_cr1_from_fpscr(ctx);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool trans_MFFSCE(DisasContext *ctx, arg_X_t *a)
|
|
{
|
|
TCGv_i64 fpscr;
|
|
|
|
REQUIRE_FPU(ctx);
|
|
|
|
gen_reset_fpstatus();
|
|
fpscr = place_from_fpscr(a->rt, UINT64_MAX);
|
|
store_fpscr_masked(fpscr, FP_ENABLES, tcg_constant_i64(0), 0x0003);
|
|
return true;
|
|
}
|
|
|
|
static bool trans_MFFSCRN(DisasContext *ctx, arg_X_tb *a)
|
|
{
|
|
TCGv_i64 t1, fpscr;
|
|
|
|
REQUIRE_FPU(ctx);
|
|
|
|
t1 = tcg_temp_new_i64();
|
|
get_fpr(t1, a->rb);
|
|
tcg_gen_andi_i64(t1, t1, FP_RN);
|
|
|
|
gen_reset_fpstatus();
|
|
fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN);
|
|
store_fpscr_masked(fpscr, FP_RN, t1, 0x0001);
|
|
return true;
|
|
}
|
|
|
|
static bool trans_MFFSCDRN(DisasContext *ctx, arg_X_tb *a)
|
|
{
|
|
TCGv_i64 t1, fpscr;
|
|
|
|
REQUIRE_FPU(ctx);
|
|
|
|
t1 = tcg_temp_new_i64();
|
|
get_fpr(t1, a->rb);
|
|
tcg_gen_andi_i64(t1, t1, FP_DRN);
|
|
|
|
gen_reset_fpstatus();
|
|
fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN);
|
|
store_fpscr_masked(fpscr, FP_DRN, t1, 0x0100);
|
|
return true;
|
|
}
|
|
|
|
static bool trans_MFFSCRNI(DisasContext *ctx, arg_X_imm2 *a)
|
|
{
|
|
TCGv_i64 t1, fpscr;
|
|
|
|
REQUIRE_FPU(ctx);
|
|
|
|
t1 = tcg_temp_new_i64();
|
|
tcg_gen_movi_i64(t1, a->imm);
|
|
|
|
gen_reset_fpstatus();
|
|
fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN);
|
|
store_fpscr_masked(fpscr, FP_RN, t1, 0x0001);
|
|
return true;
|
|
}
|
|
|
|
static bool trans_MFFSCDRNI(DisasContext *ctx, arg_X_imm3 *a)
|
|
{
|
|
TCGv_i64 t1, fpscr;
|
|
|
|
REQUIRE_FPU(ctx);
|
|
|
|
t1 = tcg_temp_new_i64();
|
|
tcg_gen_movi_i64(t1, (uint64_t)a->imm << FPSCR_DRN0);
|
|
|
|
gen_reset_fpstatus();
|
|
fpscr = place_from_fpscr(a->rt, FP_DRN | FP_ENABLES | FP_NI | FP_RN);
|
|
store_fpscr_masked(fpscr, FP_DRN, t1, 0x0100);
|
|
return true;
|
|
}
|
|
|
|
static bool trans_MFFSL(DisasContext *ctx, arg_X_t *a)
|
|
{
|
|
REQUIRE_FPU(ctx);
|
|
|
|
gen_reset_fpstatus();
|
|
place_from_fpscr(a->rt, FP_DRN | FP_STATUS | FP_ENABLES | FP_NI | FP_RN);
|
|
return true;
|
|
}
|
|
|
|
/* mtfsb0 */
|
|
static void gen_mtfsb0(DisasContext *ctx)
|
|
{
|
|
uint8_t crb;
|
|
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
crb = 31 - crbD(ctx->opcode);
|
|
gen_reset_fpstatus();
|
|
if (likely(crb != FPSCR_FEX && crb != FPSCR_VX)) {
|
|
gen_helper_fpscr_clrbit(tcg_env, tcg_constant_i32(crb));
|
|
}
|
|
if (unlikely(Rc(ctx->opcode) != 0)) {
|
|
tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr);
|
|
tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
|
|
}
|
|
}
|
|
|
|
/* mtfsb1 */
|
|
static void gen_mtfsb1(DisasContext *ctx)
|
|
{
|
|
uint8_t crb;
|
|
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
crb = 31 - crbD(ctx->opcode);
|
|
/* XXX: we pretend we can only do IEEE floating-point computations */
|
|
if (likely(crb != FPSCR_FEX && crb != FPSCR_VX && crb != FPSCR_NI)) {
|
|
gen_helper_fpscr_setbit(tcg_env, tcg_constant_i32(crb));
|
|
}
|
|
if (unlikely(Rc(ctx->opcode) != 0)) {
|
|
tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr);
|
|
tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
|
|
}
|
|
/* We can raise a deferred exception */
|
|
gen_helper_fpscr_check_status(tcg_env);
|
|
}
|
|
|
|
/* mtfsf */
|
|
static void gen_mtfsf(DisasContext *ctx)
|
|
{
|
|
TCGv_i32 t0;
|
|
TCGv_i64 t1;
|
|
int flm, l, w;
|
|
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
flm = FPFLM(ctx->opcode);
|
|
l = FPL(ctx->opcode);
|
|
w = FPW(ctx->opcode);
|
|
if (unlikely(w & !(ctx->insns_flags2 & PPC2_ISA205))) {
|
|
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
|
|
return;
|
|
}
|
|
if (!l) {
|
|
t0 = tcg_constant_i32(flm << (w * 8));
|
|
} else if (ctx->insns_flags2 & PPC2_ISA205) {
|
|
t0 = tcg_constant_i32(0xffff);
|
|
} else {
|
|
t0 = tcg_constant_i32(0xff);
|
|
}
|
|
t1 = tcg_temp_new_i64();
|
|
get_fpr(t1, rB(ctx->opcode));
|
|
gen_helper_store_fpscr(tcg_env, t1, t0);
|
|
if (unlikely(Rc(ctx->opcode) != 0)) {
|
|
tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr);
|
|
tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
|
|
}
|
|
/* We can raise a deferred exception */
|
|
gen_helper_fpscr_check_status(tcg_env);
|
|
}
|
|
|
|
/* mtfsfi */
|
|
static void gen_mtfsfi(DisasContext *ctx)
|
|
{
|
|
int bf, sh, w;
|
|
TCGv_i64 t0;
|
|
TCGv_i32 t1;
|
|
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
w = FPW(ctx->opcode);
|
|
bf = FPBF(ctx->opcode);
|
|
if (unlikely(w & !(ctx->insns_flags2 & PPC2_ISA205))) {
|
|
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
|
|
return;
|
|
}
|
|
sh = (8 * w) + 7 - bf;
|
|
t0 = tcg_constant_i64(((uint64_t)FPIMM(ctx->opcode)) << (4 * sh));
|
|
t1 = tcg_constant_i32(1 << sh);
|
|
gen_helper_store_fpscr(tcg_env, t0, t1);
|
|
if (unlikely(Rc(ctx->opcode) != 0)) {
|
|
tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr);
|
|
tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
|
|
}
|
|
/* We can raise a deferred exception */
|
|
gen_helper_fpscr_check_status(tcg_env);
|
|
}
|
|
|
|
static void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 dest, TCGv addr)
|
|
{
|
|
TCGv_i32 tmp = tcg_temp_new_i32();
|
|
tcg_gen_qemu_ld_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL));
|
|
gen_helper_todouble(dest, tmp);
|
|
}
|
|
|
|
/* lfdepx (external PID lfdx) */
|
|
static void gen_lfdepx(DisasContext *ctx)
|
|
{
|
|
TCGv EA;
|
|
TCGv_i64 t0;
|
|
CHK_SV(ctx);
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
gen_set_access_type(ctx, ACCESS_FLOAT);
|
|
EA = tcg_temp_new();
|
|
t0 = tcg_temp_new_i64();
|
|
gen_addr_reg_index(ctx, EA);
|
|
tcg_gen_qemu_ld_i64(t0, EA, PPC_TLB_EPID_LOAD, DEF_MEMOP(MO_UQ));
|
|
set_fpr(rD(ctx->opcode), t0);
|
|
}
|
|
|
|
/* lfdp */
|
|
static void gen_lfdp(DisasContext *ctx)
|
|
{
|
|
TCGv EA;
|
|
TCGv_i64 t0;
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
gen_set_access_type(ctx, ACCESS_FLOAT);
|
|
EA = tcg_temp_new();
|
|
gen_addr_imm_index(ctx, EA, 0);
|
|
t0 = tcg_temp_new_i64();
|
|
/*
|
|
* We only need to swap high and low halves. gen_qemu_ld64_i64
|
|
* does necessary 64-bit byteswap already.
|
|
*/
|
|
if (unlikely(ctx->le_mode)) {
|
|
gen_qemu_ld64_i64(ctx, t0, EA);
|
|
set_fpr(rD(ctx->opcode) + 1, t0);
|
|
tcg_gen_addi_tl(EA, EA, 8);
|
|
gen_qemu_ld64_i64(ctx, t0, EA);
|
|
set_fpr(rD(ctx->opcode), t0);
|
|
} else {
|
|
gen_qemu_ld64_i64(ctx, t0, EA);
|
|
set_fpr(rD(ctx->opcode), t0);
|
|
tcg_gen_addi_tl(EA, EA, 8);
|
|
gen_qemu_ld64_i64(ctx, t0, EA);
|
|
set_fpr(rD(ctx->opcode) + 1, t0);
|
|
}
|
|
}
|
|
|
|
/* lfdpx */
|
|
static void gen_lfdpx(DisasContext *ctx)
|
|
{
|
|
TCGv EA;
|
|
TCGv_i64 t0;
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
gen_set_access_type(ctx, ACCESS_FLOAT);
|
|
EA = tcg_temp_new();
|
|
gen_addr_reg_index(ctx, EA);
|
|
t0 = tcg_temp_new_i64();
|
|
/*
|
|
* We only need to swap high and low halves. gen_qemu_ld64_i64
|
|
* does necessary 64-bit byteswap already.
|
|
*/
|
|
if (unlikely(ctx->le_mode)) {
|
|
gen_qemu_ld64_i64(ctx, t0, EA);
|
|
set_fpr(rD(ctx->opcode) + 1, t0);
|
|
tcg_gen_addi_tl(EA, EA, 8);
|
|
gen_qemu_ld64_i64(ctx, t0, EA);
|
|
set_fpr(rD(ctx->opcode), t0);
|
|
} else {
|
|
gen_qemu_ld64_i64(ctx, t0, EA);
|
|
set_fpr(rD(ctx->opcode), t0);
|
|
tcg_gen_addi_tl(EA, EA, 8);
|
|
gen_qemu_ld64_i64(ctx, t0, EA);
|
|
set_fpr(rD(ctx->opcode) + 1, t0);
|
|
}
|
|
}
|
|
|
|
/* lfiwax */
|
|
static void gen_lfiwax(DisasContext *ctx)
|
|
{
|
|
TCGv EA;
|
|
TCGv t0;
|
|
TCGv_i64 t1;
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
gen_set_access_type(ctx, ACCESS_FLOAT);
|
|
EA = tcg_temp_new();
|
|
t0 = tcg_temp_new();
|
|
t1 = tcg_temp_new_i64();
|
|
gen_addr_reg_index(ctx, EA);
|
|
gen_qemu_ld32s(ctx, t0, EA);
|
|
tcg_gen_ext_tl_i64(t1, t0);
|
|
set_fpr(rD(ctx->opcode), t1);
|
|
}
|
|
|
|
/* lfiwzx */
|
|
static void gen_lfiwzx(DisasContext *ctx)
|
|
{
|
|
TCGv EA;
|
|
TCGv_i64 t0;
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
gen_set_access_type(ctx, ACCESS_FLOAT);
|
|
EA = tcg_temp_new();
|
|
t0 = tcg_temp_new_i64();
|
|
gen_addr_reg_index(ctx, EA);
|
|
gen_qemu_ld32u_i64(ctx, t0, EA);
|
|
set_fpr(rD(ctx->opcode), t0);
|
|
}
|
|
|
|
#define GEN_STXF(name, stop, opc2, opc3, type) \
|
|
static void glue(gen_, name##x)(DisasContext *ctx) \
|
|
{ \
|
|
TCGv EA; \
|
|
TCGv_i64 t0; \
|
|
if (unlikely(!ctx->fpu_enabled)) { \
|
|
gen_exception(ctx, POWERPC_EXCP_FPU); \
|
|
return; \
|
|
} \
|
|
gen_set_access_type(ctx, ACCESS_FLOAT); \
|
|
EA = tcg_temp_new(); \
|
|
t0 = tcg_temp_new_i64(); \
|
|
gen_addr_reg_index(ctx, EA); \
|
|
get_fpr(t0, rS(ctx->opcode)); \
|
|
gen_qemu_##stop(ctx, t0, EA); \
|
|
}
|
|
|
|
static void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 src, TCGv addr)
|
|
{
|
|
TCGv_i32 tmp = tcg_temp_new_i32();
|
|
gen_helper_tosingle(tmp, src);
|
|
tcg_gen_qemu_st_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL));
|
|
}
|
|
|
|
/* stfdepx (external PID lfdx) */
|
|
static void gen_stfdepx(DisasContext *ctx)
|
|
{
|
|
TCGv EA;
|
|
TCGv_i64 t0;
|
|
CHK_SV(ctx);
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
gen_set_access_type(ctx, ACCESS_FLOAT);
|
|
EA = tcg_temp_new();
|
|
t0 = tcg_temp_new_i64();
|
|
gen_addr_reg_index(ctx, EA);
|
|
get_fpr(t0, rD(ctx->opcode));
|
|
tcg_gen_qemu_st_i64(t0, EA, PPC_TLB_EPID_STORE, DEF_MEMOP(MO_UQ));
|
|
}
|
|
|
|
/* stfdp */
|
|
static void gen_stfdp(DisasContext *ctx)
|
|
{
|
|
TCGv EA;
|
|
TCGv_i64 t0;
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
gen_set_access_type(ctx, ACCESS_FLOAT);
|
|
EA = tcg_temp_new();
|
|
t0 = tcg_temp_new_i64();
|
|
gen_addr_imm_index(ctx, EA, 0);
|
|
/*
|
|
* We only need to swap high and low halves. gen_qemu_st64_i64
|
|
* does necessary 64-bit byteswap already.
|
|
*/
|
|
if (unlikely(ctx->le_mode)) {
|
|
get_fpr(t0, rD(ctx->opcode) + 1);
|
|
gen_qemu_st64_i64(ctx, t0, EA);
|
|
tcg_gen_addi_tl(EA, EA, 8);
|
|
get_fpr(t0, rD(ctx->opcode));
|
|
gen_qemu_st64_i64(ctx, t0, EA);
|
|
} else {
|
|
get_fpr(t0, rD(ctx->opcode));
|
|
gen_qemu_st64_i64(ctx, t0, EA);
|
|
tcg_gen_addi_tl(EA, EA, 8);
|
|
get_fpr(t0, rD(ctx->opcode) + 1);
|
|
gen_qemu_st64_i64(ctx, t0, EA);
|
|
}
|
|
}
|
|
|
|
/* stfdpx */
|
|
static void gen_stfdpx(DisasContext *ctx)
|
|
{
|
|
TCGv EA;
|
|
TCGv_i64 t0;
|
|
if (unlikely(!ctx->fpu_enabled)) {
|
|
gen_exception(ctx, POWERPC_EXCP_FPU);
|
|
return;
|
|
}
|
|
gen_set_access_type(ctx, ACCESS_FLOAT);
|
|
EA = tcg_temp_new();
|
|
t0 = tcg_temp_new_i64();
|
|
gen_addr_reg_index(ctx, EA);
|
|
/*
|
|
* We only need to swap high and low halves. gen_qemu_st64_i64
|
|
* does necessary 64-bit byteswap already.
|
|
*/
|
|
if (unlikely(ctx->le_mode)) {
|
|
get_fpr(t0, rD(ctx->opcode) + 1);
|
|
gen_qemu_st64_i64(ctx, t0, EA);
|
|
tcg_gen_addi_tl(EA, EA, 8);
|
|
get_fpr(t0, rD(ctx->opcode));
|
|
gen_qemu_st64_i64(ctx, t0, EA);
|
|
} else {
|
|
get_fpr(t0, rD(ctx->opcode));
|
|
gen_qemu_st64_i64(ctx, t0, EA);
|
|
tcg_gen_addi_tl(EA, EA, 8);
|
|
get_fpr(t0, rD(ctx->opcode) + 1);
|
|
gen_qemu_st64_i64(ctx, t0, EA);
|
|
}
|
|
}
|
|
|
|
/* Optional: */
|
|
static inline void gen_qemu_st32fiw(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2)
|
|
{
|
|
TCGv t0 = tcg_temp_new();
|
|
tcg_gen_trunc_i64_tl(t0, arg1),
|
|
gen_qemu_st32(ctx, t0, arg2);
|
|
}
|
|
/* stfiwx */
|
|
GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX);
|
|
|
|
/* Floating-point Load/Store Instructions */
|
|
static bool do_lsfpsd(DisasContext *ctx, int rt, int ra, TCGv displ,
|
|
bool update, bool store, bool single)
|
|
{
|
|
TCGv ea;
|
|
TCGv_i64 t0;
|
|
REQUIRE_INSNS_FLAGS(ctx, FLOAT);
|
|
REQUIRE_FPU(ctx);
|
|
if (update && ra == 0) {
|
|
gen_invalid(ctx);
|
|
return true;
|
|
}
|
|
gen_set_access_type(ctx, ACCESS_FLOAT);
|
|
t0 = tcg_temp_new_i64();
|
|
ea = do_ea_calc(ctx, ra, displ);
|
|
if (store) {
|
|
get_fpr(t0, rt);
|
|
if (single) {
|
|
gen_qemu_st32fs(ctx, t0, ea);
|
|
} else {
|
|
gen_qemu_st64_i64(ctx, t0, ea);
|
|
}
|
|
} else {
|
|
if (single) {
|
|
gen_qemu_ld32fs(ctx, t0, ea);
|
|
} else {
|
|
gen_qemu_ld64_i64(ctx, t0, ea);
|
|
}
|
|
set_fpr(rt, t0);
|
|
}
|
|
if (update) {
|
|
tcg_gen_mov_tl(cpu_gpr[ra], ea);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool do_lsfp_D(DisasContext *ctx, arg_D *a, bool update, bool store,
|
|
bool single)
|
|
{
|
|
return do_lsfpsd(ctx, a->rt, a->ra, tcg_constant_tl(a->si), update, store,
|
|
single);
|
|
}
|
|
|
|
static bool do_lsfp_PLS_D(DisasContext *ctx, arg_PLS_D *a, bool update,
|
|
bool store, bool single)
|
|
{
|
|
arg_D d;
|
|
if (!resolve_PLS_D(ctx, &d, a)) {
|
|
return true;
|
|
}
|
|
return do_lsfp_D(ctx, &d, update, store, single);
|
|
}
|
|
|
|
static bool do_lsfp_X(DisasContext *ctx, arg_X *a, bool update,
|
|
bool store, bool single)
|
|
{
|
|
return do_lsfpsd(ctx, a->rt, a->ra, cpu_gpr[a->rb], update, store, single);
|
|
}
|
|
|
|
TRANS(LFS, do_lsfp_D, false, false, true)
|
|
TRANS(LFSU, do_lsfp_D, true, false, true)
|
|
TRANS(LFSX, do_lsfp_X, false, false, true)
|
|
TRANS(LFSUX, do_lsfp_X, true, false, true)
|
|
TRANS(PLFS, do_lsfp_PLS_D, false, false, true)
|
|
|
|
TRANS(LFD, do_lsfp_D, false, false, false)
|
|
TRANS(LFDU, do_lsfp_D, true, false, false)
|
|
TRANS(LFDX, do_lsfp_X, false, false, false)
|
|
TRANS(LFDUX, do_lsfp_X, true, false, false)
|
|
TRANS(PLFD, do_lsfp_PLS_D, false, false, false)
|
|
|
|
TRANS(STFS, do_lsfp_D, false, true, true)
|
|
TRANS(STFSU, do_lsfp_D, true, true, true)
|
|
TRANS(STFSX, do_lsfp_X, false, true, true)
|
|
TRANS(STFSUX, do_lsfp_X, true, true, true)
|
|
TRANS(PSTFS, do_lsfp_PLS_D, false, true, true)
|
|
|
|
TRANS(STFD, do_lsfp_D, false, true, false)
|
|
TRANS(STFDU, do_lsfp_D, true, true, false)
|
|
TRANS(STFDX, do_lsfp_X, false, true, false)
|
|
TRANS(STFDUX, do_lsfp_X, true, true, false)
|
|
TRANS(PSTFD, do_lsfp_PLS_D, false, true, false)
|
|
|
|
#undef GEN_LDF
|
|
#undef GEN_LDUF
|
|
#undef GEN_LDUXF
|
|
#undef GEN_LDXF
|
|
#undef GEN_LDFS
|
|
|
|
#undef GEN_STF
|
|
#undef GEN_STUF
|
|
#undef GEN_STUXF
|
|
#undef GEN_STXF
|
|
#undef GEN_STFS
|