target/arm: Explicitly set 2-NaN propagation rule

Set the 2-NaN propagation rule explicitly in the float_status words
we use.  We wrap this plus the pre-existing setting of the
tininess-before-rounding flag in a new function
arm_set_default_fp_behaviours() to avoid repetition, since we have a
lot of float_status words at this point.

The situation with FPA11 emulation in linux-user is a little odd, and
arguably "correct" behaviour there would be to exactly match a real
Linux kernel's FPA11 emulation.  However FPA11 emulation is
essentially dead at this point and so it seems better to continue
with QEMU's current behaviour and leave a comment describing the
situation.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20241025141254.2141506-4-peter.maydell@linaro.org
This commit is contained in:
Peter Maydell 2024-11-05 10:09:53 +00:00
parent d22c9949d7
commit d1ff996788
3 changed files with 37 additions and 19 deletions
fpu
linux-user/arm/nwfpe
target/arm

View File

@ -402,19 +402,10 @@ static int pickNaN(FloatClass a_cls, FloatClass b_cls,
/* target didn't set the rule: fall back to old ifdef choices */
#if defined(TARGET_AVR) || defined(TARGET_HEXAGON) \
|| defined(TARGET_RISCV) || defined(TARGET_SH4) \
|| defined(TARGET_TRICORE)
|| defined(TARGET_TRICORE) || defined(TARGET_ARM)
g_assert_not_reached();
#elif defined(TARGET_ARM) || defined(TARGET_MIPS) || defined(TARGET_HPPA) || \
#elif defined(TARGET_MIPS) || defined(TARGET_HPPA) || \
defined(TARGET_LOONGARCH64) || defined(TARGET_S390X)
/*
* ARM mandated NaN propagation rules (see FPProcessNaNs()), take
* the first of:
* 1. A if it is signaling
* 2. B if it is signaling
* 3. A (quiet)
* 4. B (quiet)
* A signaling NaN is always quietened before returning it.
*/
/*
* According to MIPS specifications, if one of the two operands is
* a sNaN, a new qNaN has to be generated. This is done in

View File

@ -51,6 +51,24 @@ void resetFPA11(void)
#ifdef MAINTAIN_FPCR
fpa11->fpcr = MASK_RESET;
#endif
/*
* Real FPA11 hardware does not handle NaNs, but always takes an
* exception for them to be software-emulated (ARM7500FE datasheet
* section 10.4). There is no documented architectural requirement
* for NaN propagation rules and it will depend on how the OS
* level software emulation opted to do it. We here use prop_s_ab
* which matches the later VFP hardware choice and how QEMU's
* fpa11 emulation has worked in the past. The real Linux kernel
* does something slightly different: arch/arm/nwfpe/softfloat-specialize
* propagateFloat64NaN() has the curious behaviour that it prefers
* the QNaN over the SNaN, but if both are QNaN it picks A and
* if both are SNaN it picks B. In theory we could add this as
* a NaN propagation rule, but in practice FPA11 emulation is so
* close to totally dead that it's not worth trying to match it at
* this late date.
*/
set_float_2nan_prop_rule(float_2nan_prop_s_ab, &fpa11->fp_status);
}
void SetRoundingMode(const unsigned int opcode)

View File

@ -168,6 +168,18 @@ void arm_register_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook,
QLIST_INSERT_HEAD(&cpu->el_change_hooks, entry, node);
}
/*
* Set the float_status behaviour to match the Arm defaults:
* * tininess-before-rounding
* * 2-input NaN propagation prefers SNaN over QNaN, and then
* operand A over operand B (see FPProcessNaNs() pseudocode)
*/
static void arm_set_default_fp_behaviours(float_status *s)
{
set_float_detect_tininess(float_tininess_before_rounding, s);
set_float_2nan_prop_rule(float_2nan_prop_s_ab, s);
}
static void cp_reg_reset(gpointer key, gpointer value, gpointer opaque)
{
/* Reset a single ARMCPRegInfo register */
@ -549,14 +561,11 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type)
set_flush_inputs_to_zero(1, &env->vfp.standard_fp_status);
set_default_nan_mode(1, &env->vfp.standard_fp_status);
set_default_nan_mode(1, &env->vfp.standard_fp_status_f16);
set_float_detect_tininess(float_tininess_before_rounding,
&env->vfp.fp_status);
set_float_detect_tininess(float_tininess_before_rounding,
&env->vfp.standard_fp_status);
set_float_detect_tininess(float_tininess_before_rounding,
&env->vfp.fp_status_f16);
set_float_detect_tininess(float_tininess_before_rounding,
&env->vfp.standard_fp_status_f16);
arm_set_default_fp_behaviours(&env->vfp.fp_status);
arm_set_default_fp_behaviours(&env->vfp.standard_fp_status);
arm_set_default_fp_behaviours(&env->vfp.fp_status_f16);
arm_set_default_fp_behaviours(&env->vfp.standard_fp_status_f16);
#ifndef CONFIG_USER_ONLY
if (kvm_enabled()) {
kvm_arm_reset_vcpu(cpu);