mtfsf, mtfsfi and mtfsb1 instructions call helper_float_check_status
after updating the value of FPSCR, but helper_float_check_status
checks fp_status and fp_status isn't updated based on FPSCR and
since the value of fp_status is reset earlier in the instruction,
it's always 0.
Because of this helper_float_check_status would change the FI bit to 0
as this bit checks if the last operation was inexact and
float_flag_inexact is always 0.
These instructions also don't throw exceptions correctly since
helper_float_check_status throw exceptions based on fp_status.
This commit created a new helper, helper_fpscr_check_status that checks
FPSCR value instead of fp_status and checks for a larger variety of
exceptions than do_float_check_status.
Since fp_status isn't used, gen_reset_fpstatus() was removed.
The hardware used to compare QEMU's behavior to was a Power9.
Signed-off-by: Lucas Mateus Castro (alqotel) <lucas.araujo@eldorado.org.br>
---
target/ppc/fpu_helper.c | 41 ++++++++++++++++++++++++++++++
target/ppc/helper.h | 1 +
target/ppc/translate/fp-impl.c.inc | 9 +++----
3 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c
index c4896cecc8..f086cb503f 100644
--- a/target/ppc/fpu_helper.c
+++ b/target/ppc/fpu_helper.c
@@ -414,6 +414,47 @@ void helper_store_fpscr(CPUPPCState *env, uint64_t val, uint32_t nibbles)
ppc_store_fpscr(env, val);
}
+void helper_fpscr_check_status(CPUPPCState *env)
+{
+ CPUState *cs = env_cpu(env);
+ target_ulong fpscr = env->fpscr;
+ int error = 0;
+
+ if ((fpscr & FP_VXSOFT) && (fpscr_ve != 0)) {
+ error = POWERPC_EXCP_FP_VXSOFT;
+ } else if ((fpscr & FP_OX) && (fpscr & FP_OE)) {
+ error = POWERPC_EXCP_FP_OX;
+ } else if ((fpscr & FP_UX) && (fpscr & FP_UE)) {
+ error = POWERPC_EXCP_FP_UX;
+ } else if ((fpscr & FP_XX) && (fpscr & FP_XE)) {
+ error = POWERPC_EXCP_FP_XX;
+ } else if ((fpscr & FP_ZX) && (fpscr & FP_ZE)) {
+ error = POWERPC_EXCP_FP_ZX;
+ } else if ((fpscr & FP_VXSNAN) && (fpscr_ve != 0)) {
+ error = POWERPC_EXCP_FP_VXSNAN;
+ } else if ((fpscr & FP_VXISI) && (fpscr_ve != 0)) {
+ error = POWERPC_EXCP_FP_VXISI;
+ } else if ((fpscr & FP_VXIDI) && (fpscr_ve != 0)) {
+ error = POWERPC_EXCP_FP_VXIDI;
+ } else if ((fpscr & FP_VXZDZ) && (fpscr_ve != 0)) {
+ error = POWERPC_EXCP_FP_VXZDZ;
+ } else if ((fpscr & FP_VXIMZ) && (fpscr_ve != 0)) {
+ error = POWERPC_EXCP_FP_VXIMZ;
+ } else if ((fpscr & FP_VXVC) && (fpscr_ve != 0)) {
+ error = POWERPC_EXCP_FP_VXVC;
+ }
+
+ if (error) {
+ cs->exception_index = POWERPC_EXCP_PROGRAM;
+ env->error_code = error | POWERPC_EXCP_FP;
+ /* Deferred floating-point exception after target FPSCR update */
+ if (fp_exceptions_enabled(env)) {
+ raise_exception_err_ra(env, cs->exception_index,
+ env->error_code, GETPC());
+ }
+ }
+}
+
static void do_float_check_status(CPUPPCState *env, uintptr_t raddr)
{
CPUState *cs = env_cpu(env);
diff --git a/target/ppc/helper.h b/target/ppc/helper.h
index 627811cefc..632a81c676 100644
--- a/target/ppc/helper.h
+++ b/target/ppc/helper.h
@@ -63,6 +63,7 @@ DEF_HELPER_FLAGS_1(cntlzw32, TCG_CALL_NO_RWG_SE, i32, i32)
DEF_HELPER_FLAGS_2(brinc, TCG_CALL_NO_RWG_SE, tl, tl, tl)
DEF_HELPER_1(float_check_status, void, env)
+DEF_HELPER_1(fpscr_check_status, void, env)
DEF_HELPER_1(reset_fpstatus, void, env)
DEF_HELPER_2(compute_fprf_float64, void, env, i64)
DEF_HELPER_3(store_fpscr, void, env, i64, i32)
diff --git a/target/ppc/translate/fp-impl.c.inc b/target/ppc/translate/fp-impl.c.inc
index d1dbb1b96b..ca195fd9d2 100644
--- a/target/ppc/translate/fp-impl.c.inc
+++ b/target/ppc/translate/fp-impl.c.inc
@@ -769,7 +769,6 @@ static void gen_mtfsb1(DisasContext *ctx)
return;
}
crb = 31 - crbD(ctx->opcode);
- gen_reset_fpstatus();
/* XXX: we pretend we can only do IEEE floating-point computations */
if (likely(crb != FPSCR_FEX && crb != FPSCR_VX && crb != FPSCR_NI)) {
TCGv_i32 t0;
@@ -782,7 +781,7 @@ static void gen_mtfsb1(DisasContext *ctx)
tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
}
/* We can raise a deferred exception */
- gen_helper_float_check_status(cpu_env);
+ gen_helper_fpscr_check_status(cpu_env);
}
/* mtfsf */
@@ -803,7 +802,6 @@ static void gen_mtfsf(DisasContext *ctx)
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
return;
}
- gen_reset_fpstatus();
if (l) {
t0 = tcg_const_i32((ctx->insns_flags2 & PPC2_ISA205) ? 0xffff : 0xff);
} else {
@@ -818,7 +816,7 @@ static void gen_mtfsf(DisasContext *ctx)
tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
}
/* We can raise a deferred exception */
- gen_helper_float_check_status(cpu_env);
+ gen_helper_fpscr_check_status(cpu_env);
tcg_temp_free_i64(t1);
}
@@ -840,7 +838,6 @@ static void gen_mtfsfi(DisasContext *ctx)
return;
}
sh = (8 * w) + 7 - bf;
- gen_reset_fpstatus();
t0 = tcg_const_i64(((uint64_t)FPIMM(ctx->opcode)) << (4 * sh));
t1 = tcg_const_i32(1 << sh);
gen_helper_store_fpscr(cpu_env, t0, t1);
@@ -851,7 +848,7 @@ static void gen_mtfsfi(DisasContext *ctx)
tcg_gen_shri_i32(cpu_crf[1], cpu_crf[1], FPSCR_OX);
}
/* We can raise a deferred exception */
- gen_helper_float_check_status(cpu_env);
+ gen_helper_fpscr_check_status(cpu_env);
}
static void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 dest, TCGv addr)
--
2.31.1
On 11/18/21 2:25 PM, Lucas Mateus Castro (alqotel) wrote:
> + if ((fpscr & FP_VXSOFT) && (fpscr_ve != 0)) {
> + error = POWERPC_EXCP_FP_VXSOFT;
> + } else if ((fpscr & FP_OX) && (fpscr & FP_OE)) {
> + error = POWERPC_EXCP_FP_OX;
> + } else if ((fpscr & FP_UX) && (fpscr & FP_UE)) {
> + error = POWERPC_EXCP_FP_UX;
> + } else if ((fpscr & FP_XX) && (fpscr & FP_XE)) {
> + error = POWERPC_EXCP_FP_XX;
> + } else if ((fpscr & FP_ZX) && (fpscr & FP_ZE)) {
> + error = POWERPC_EXCP_FP_ZX;
> + } else if ((fpscr & FP_VXSNAN) && (fpscr_ve != 0)) {
> + error = POWERPC_EXCP_FP_VXSNAN;
> + } else if ((fpscr & FP_VXISI) && (fpscr_ve != 0)) {
> + error = POWERPC_EXCP_FP_VXISI;
> + } else if ((fpscr & FP_VXIDI) && (fpscr_ve != 0)) {
> + error = POWERPC_EXCP_FP_VXIDI;
> + } else if ((fpscr & FP_VXZDZ) && (fpscr_ve != 0)) {
> + error = POWERPC_EXCP_FP_VXZDZ;
> + } else if ((fpscr & FP_VXIMZ) && (fpscr_ve != 0)) {
> + error = POWERPC_EXCP_FP_VXIMZ;
> + } else if ((fpscr & FP_VXVC) && (fpscr_ve != 0)) {
> + error = POWERPC_EXCP_FP_VXVC;
> + }
Is there a defined order for these in the manual? I couldn't find it quickly if so. If
there is no defined order, I think you should test VE only once.
Drop the use of fpscr_ve and use fpscr & FP_VE instead. (I think these hidden uses of *env
are evil and should be banished, but that's a bit of a job.)
You could say
} else {
return;
}
> +
> + if (error) {
and then remove this test.
The rest of it looks good.
r~
On 19/11/2021 06:18, Richard Henderson wrote:
> On 11/18/21 2:25 PM, Lucas Mateus Castro (alqotel) wrote:
>> + if ((fpscr & FP_VXSOFT) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXSOFT;
>> + } else if ((fpscr & FP_OX) && (fpscr & FP_OE)) {
>> + error = POWERPC_EXCP_FP_OX;
>> + } else if ((fpscr & FP_UX) && (fpscr & FP_UE)) {
>> + error = POWERPC_EXCP_FP_UX;
>> + } else if ((fpscr & FP_XX) && (fpscr & FP_XE)) {
>> + error = POWERPC_EXCP_FP_XX;
>> + } else if ((fpscr & FP_ZX) && (fpscr & FP_ZE)) {
>> + error = POWERPC_EXCP_FP_ZX;
>> + } else if ((fpscr & FP_VXSNAN) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXSNAN;
>> + } else if ((fpscr & FP_VXISI) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXISI;
>> + } else if ((fpscr & FP_VXIDI) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXIDI;
>> + } else if ((fpscr & FP_VXZDZ) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXZDZ;
>> + } else if ((fpscr & FP_VXIMZ) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXIMZ;
>> + } else if ((fpscr & FP_VXVC) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXVC;
>> + }
>
> Is there a defined order for these in the manual? I couldn't find it
> quickly if so. If
> there is no defined order, I think you should test VE only once.
I also couldn't find a defined order, so I chose to prioritize VXSOFT
then go by ascending order of the bit number. In the v3 I'll move VXSOFT
with the others invalid operation bits to check VE once then.
>
> Drop the use of fpscr_ve and use fpscr & FP_VE instead. (I think these
> hidden uses of *env
> are evil and should be banished, but that's a bit of a job.)
>
> You could say
>
> } else {
> return;
> }
>
>> +
>> + if (error) {
>
> and then remove this test.
Ok, I'll do these in v3.
>
> The rest of it looks good.
>
>
> r~
--
Lucas Mateus M. Araujo e Castro
Instituto de Pesquisas ELDORADO
<https://www.eldorado.org.br/?utm_campaign=assinatura_de_e-mail&utm_medium=email&utm_source=RD+Station>
Departamento Computação Embarcada
Estagiario
Aviso Legal - Disclaimer <https://www.eldorado.org.br/disclaimer.html>
On 11/19/21 10:18, Richard Henderson wrote:
> On 11/18/21 2:25 PM, Lucas Mateus Castro (alqotel) wrote:
>> + if ((fpscr & FP_VXSOFT) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXSOFT;
>> + } else if ((fpscr & FP_OX) && (fpscr & FP_OE)) {
>> + error = POWERPC_EXCP_FP_OX;
>> + } else if ((fpscr & FP_UX) && (fpscr & FP_UE)) {
>> + error = POWERPC_EXCP_FP_UX;
>> + } else if ((fpscr & FP_XX) && (fpscr & FP_XE)) {
>> + error = POWERPC_EXCP_FP_XX;
>> + } else if ((fpscr & FP_ZX) && (fpscr & FP_ZE)) {
>> + error = POWERPC_EXCP_FP_ZX;
>> + } else if ((fpscr & FP_VXSNAN) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXSNAN;
>> + } else if ((fpscr & FP_VXISI) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXISI;
>> + } else if ((fpscr & FP_VXIDI) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXIDI;
>> + } else if ((fpscr & FP_VXZDZ) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXZDZ;
>> + } else if ((fpscr & FP_VXIMZ) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXIMZ;
>> + } else if ((fpscr & FP_VXVC) && (fpscr_ve != 0)) {
>> + error = POWERPC_EXCP_FP_VXVC;
>> + }
>
> Is there a defined order for these in the manual? I couldn't find it quickly if so. If there is no defined order, I think you should test VE only once.
>
> Drop the use of fpscr_ve and use fpscr & FP_VE instead. (I think these hidden uses of *env are evil and should be banished, but that's a bit of a job.)
you mean all the msr_* macros ? I agree. It's huge and I wonder how we could automate parts of it.
Thanks,
C.
>
> You could say
>
> } else {
> return;
> }
>
>> +
>> + if (error) {
>
> and then remove this test.
>
> The rest of it looks good.
>
>
> r~
On 11/19/21 10:24 AM, Cédric Le Goater wrote:
>>> + } else if ((fpscr & FP_VXVC) && (fpscr_ve != 0)) {
>>> + error = POWERPC_EXCP_FP_VXVC;
>>> + }
>>
>> Is there a defined order for these in the manual? I couldn't find it quickly if so. If
>> there is no defined order, I think you should test VE only once.
>>
>> Drop the use of fpscr_ve and use fpscr & FP_VE instead. (I think these hidden uses of
>> *env are evil and should be banished, but that's a bit of a job.)
>
> you mean all the msr_* macros ? I agree. It's huge and I wonder how we could automate
> parts of it.
Well, those too. But fpscr_ve is the one that caught my eye here.
r~
© 2016 - 2026 Red Hat, Inc.