mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-01-24 12:01:06 +00:00
target/arm: Implement FPCXT_NS fp system register
Implement the v8.1M FPCXT_NS floating-point system register. This is a little more complicated than FPCXT_S, because it has specific handling for "current FP state is inactive", and it only wants to do PreserveFPState(), not the full set of actions done by ExecuteFPCheck() which vfp_access_check() implements. Backports eb20dafdbff92063a88624176fdc396e01961bf3
This commit is contained in:
parent
311b6fd74c
commit
e586a27a7b
|
@ -674,6 +674,7 @@ static FPSysRegCheckResult fp_sysreg_checks(DisasContext *s, int regno)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ARM_VFP_FPCXT_S:
|
case ARM_VFP_FPCXT_S:
|
||||||
|
case ARM_VFP_FPCXT_NS:
|
||||||
if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) {
|
if (!arm_dc_feature(s, ARM_FEATURE_V8_1M)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -685,13 +686,49 @@ static FPSysRegCheckResult fp_sysreg_checks(DisasContext *s, int regno)
|
||||||
return FPSysRegCheckFailed;
|
return FPSysRegCheckFailed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!vfp_access_check(s)) {
|
/*
|
||||||
|
* FPCXT_NS is a special case: it has specific handling for
|
||||||
|
* "current FP state is inactive", and must do the PreserveFPState()
|
||||||
|
* but not the usual full set of actions done by ExecuteFPCheck().
|
||||||
|
* So we don't call vfp_access_check() and the callers must handle this.
|
||||||
|
*/
|
||||||
|
if (regno != ARM_VFP_FPCXT_NS && !vfp_access_check(s)) {
|
||||||
return FPSysRegCheckDone;
|
return FPSysRegCheckDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FPSysRegCheckContinue;
|
return FPSysRegCheckContinue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gen_branch_fpInactive(DisasContext *s, TCGCond cond,
|
||||||
|
TCGLabel *label)
|
||||||
|
{
|
||||||
|
TCGContext *tcg_ctx = s->uc->tcg_ctx;
|
||||||
|
/*
|
||||||
|
* FPCXT_NS is a special case: it has specific handling for
|
||||||
|
* "current FP state is inactive", and must do the PreserveFPState()
|
||||||
|
* but not the usual full set of actions done by ExecuteFPCheck().
|
||||||
|
* We don't have a TB flag that matches the fpInactive check, so we
|
||||||
|
* do it at runtime as we don't expect FPCXT_NS accesses to be frequent.
|
||||||
|
*
|
||||||
|
* Emit code that checks fpInactive and does a conditional
|
||||||
|
* branch to label based on it:
|
||||||
|
* if cond is TCG_COND_NE then branch if fpInactive != 0 (ie if inactive)
|
||||||
|
* if cond is TCG_COND_EQ then branch if fpInactive == 0 (ie if active)
|
||||||
|
*/
|
||||||
|
assert(cond == TCG_COND_EQ || cond == TCG_COND_NE);
|
||||||
|
|
||||||
|
/* fpInactive = FPCCR_NS.ASPEN == 1 && CONTROL.FPCA == 0 */
|
||||||
|
TCGv_i32 aspen, fpca;
|
||||||
|
aspen = load_cpu_field(s, v7m.fpccr[M_REG_NS]);
|
||||||
|
fpca = load_cpu_field(s, v7m.control[M_REG_S]);
|
||||||
|
tcg_gen_andi_i32(tcg_ctx, aspen, aspen, R_V7M_FPCCR_ASPEN_MASK);
|
||||||
|
tcg_gen_xori_i32(tcg_ctx, aspen, aspen, R_V7M_FPCCR_ASPEN_MASK);
|
||||||
|
tcg_gen_andi_i32(tcg_ctx, fpca, fpca, R_V7M_CONTROL_FPCA_MASK);
|
||||||
|
tcg_gen_or_i32(tcg_ctx, fpca, fpca, aspen);
|
||||||
|
tcg_gen_brcondi_i32(tcg_ctx, tcg_invert_cond(cond), fpca, 0, label);
|
||||||
|
tcg_temp_free_i32(tcg_ctx, aspen);
|
||||||
|
tcg_temp_free_i32(tcg_ctx, fpca);
|
||||||
|
}
|
||||||
|
|
||||||
static bool gen_M_fp_sysreg_write(DisasContext *s, int regno,
|
static bool gen_M_fp_sysreg_write(DisasContext *s, int regno,
|
||||||
fp_sysreg_loadfn *loadfn,
|
fp_sysreg_loadfn *loadfn,
|
||||||
void *opaque)
|
void *opaque)
|
||||||
|
@ -699,6 +736,7 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno,
|
||||||
TCGContext *tcg_ctx = s->uc->tcg_ctx;
|
TCGContext *tcg_ctx = s->uc->tcg_ctx;
|
||||||
/* Do a write to an M-profile floating point system register */
|
/* Do a write to an M-profile floating point system register */
|
||||||
TCGv_i32 tmp;
|
TCGv_i32 tmp;
|
||||||
|
TCGLabel *lab_end = NULL;
|
||||||
|
|
||||||
switch (fp_sysreg_checks(s, regno)) {
|
switch (fp_sysreg_checks(s, regno)) {
|
||||||
case FPSysRegCheckFailed:
|
case FPSysRegCheckFailed:
|
||||||
|
@ -732,6 +770,13 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno,
|
||||||
tcg_temp_free_i32(tcg_ctx, tmp);
|
tcg_temp_free_i32(tcg_ctx, tmp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ARM_VFP_FPCXT_NS:
|
||||||
|
lab_end = gen_new_label(tcg_ctx);
|
||||||
|
/* fpInactive case: write is a NOP, so branch to end */
|
||||||
|
gen_branch_fpInactive(s, TCG_COND_NE, lab_end);
|
||||||
|
/* !fpInactive: PreserveFPState(), and reads same as FPCXT_S */
|
||||||
|
gen_preserve_fp_state(s);
|
||||||
|
/* fall through */
|
||||||
case ARM_VFP_FPCXT_S:
|
case ARM_VFP_FPCXT_S:
|
||||||
{
|
{
|
||||||
TCGv_i32 sfpa, control;
|
TCGv_i32 sfpa, control;
|
||||||
|
@ -755,6 +800,9 @@ static bool gen_M_fp_sysreg_write(DisasContext *s, int regno,
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
if (lab_end) {
|
||||||
|
gen_set_label(tcg_ctx, lab_end);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,6 +813,8 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno,
|
||||||
TCGContext *tcg_ctx = s->uc->tcg_ctx;
|
TCGContext *tcg_ctx = s->uc->tcg_ctx;
|
||||||
/* Do a read from an M-profile floating point system register */
|
/* Do a read from an M-profile floating point system register */
|
||||||
TCGv_i32 tmp;
|
TCGv_i32 tmp;
|
||||||
|
TCGLabel *lab_end = NULL;
|
||||||
|
bool lookup_tb = false;
|
||||||
|
|
||||||
switch (fp_sysreg_checks(s, regno)) {
|
switch (fp_sysreg_checks(s, regno)) {
|
||||||
case FPSysRegCheckFailed:
|
case FPSysRegCheckFailed:
|
||||||
|
@ -823,12 +873,59 @@ static bool gen_M_fp_sysreg_read(DisasContext *s, int regno,
|
||||||
fpscr = load_cpu_field(s, v7m.fpdscr[M_REG_NS]);
|
fpscr = load_cpu_field(s, v7m.fpdscr[M_REG_NS]);
|
||||||
gen_helper_vfp_set_fpscr(tcg_ctx, tcg_ctx->cpu_env, fpscr);
|
gen_helper_vfp_set_fpscr(tcg_ctx, tcg_ctx->cpu_env, fpscr);
|
||||||
tcg_temp_free_i32(tcg_ctx, fpscr);
|
tcg_temp_free_i32(tcg_ctx, fpscr);
|
||||||
gen_lookup_tb(s);
|
lookup_tb = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ARM_VFP_FPCXT_NS:
|
||||||
|
{
|
||||||
|
TCGv_i32 control, sfpa, fpscr, fpdscr, zero;
|
||||||
|
TCGLabel *lab_active = gen_new_label(tcg_ctx);
|
||||||
|
|
||||||
|
lookup_tb = true;
|
||||||
|
|
||||||
|
gen_branch_fpInactive(s, TCG_COND_EQ, lab_active);
|
||||||
|
/* fpInactive case: reads as FPDSCR_NS */
|
||||||
|
TCGv_i32 tmp = load_cpu_field(s, v7m.fpdscr[M_REG_NS]);
|
||||||
|
storefn(s, opaque, tmp);
|
||||||
|
lab_end = gen_new_label(tcg_ctx);
|
||||||
|
tcg_gen_br(tcg_ctx, lab_end);
|
||||||
|
|
||||||
|
gen_set_label(tcg_ctx, lab_active);
|
||||||
|
/* !fpInactive: Reads the same as FPCXT_S, but side effects differ */
|
||||||
|
gen_preserve_fp_state(s);
|
||||||
|
tmp = tcg_temp_new_i32(tcg_ctx);
|
||||||
|
sfpa = tcg_temp_new_i32(tcg_ctx);
|
||||||
|
fpscr = tcg_temp_new_i32(tcg_ctx);
|
||||||
|
gen_helper_vfp_get_fpscr(tcg_ctx, fpscr, tcg_ctx->cpu_env);
|
||||||
|
tcg_gen_andi_i32(tcg_ctx, tmp, fpscr, ~FPCR_NZCV_MASK);
|
||||||
|
control = load_cpu_field(s, v7m.control[M_REG_S]);
|
||||||
|
tcg_gen_andi_i32(tcg_ctx, sfpa, control, R_V7M_CONTROL_SFPA_MASK);
|
||||||
|
tcg_gen_shli_i32(tcg_ctx, sfpa, sfpa, 31 - R_V7M_CONTROL_SFPA_SHIFT);
|
||||||
|
tcg_gen_or_i32(tcg_ctx, tmp, tmp, sfpa);
|
||||||
|
tcg_temp_free_i32(tcg_ctx, control);
|
||||||
|
/* Store result before updating FPSCR, in case it faults */
|
||||||
|
storefn(s, opaque, tmp);
|
||||||
|
/* If SFPA is zero then set FPSCR from FPDSCR_NS */
|
||||||
|
fpdscr = load_cpu_field(s, v7m.fpdscr[M_REG_NS]);
|
||||||
|
zero = tcg_const_i32(tcg_ctx, 0);
|
||||||
|
tcg_gen_movcond_i32(tcg_ctx, TCG_COND_EQ, fpscr, sfpa, zero, fpdscr, fpscr);
|
||||||
|
gen_helper_vfp_set_fpscr(tcg_ctx, tcg_ctx->cpu_env, fpscr);
|
||||||
|
tcg_temp_free_i32(tcg_ctx, zero);
|
||||||
|
tcg_temp_free_i32(tcg_ctx, sfpa);
|
||||||
|
tcg_temp_free_i32(tcg_ctx, fpdscr);
|
||||||
|
tcg_temp_free_i32(tcg_ctx, fpscr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lab_end) {
|
||||||
|
gen_set_label(tcg_ctx, lab_end);
|
||||||
|
}
|
||||||
|
if (lookup_tb) {
|
||||||
|
gen_lookup_tb(s);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue