mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-03 07:43:54 -06:00
softfloat: Allow runtime choice of inf * 0 + NaN result
IEEE 758 does not define a fixed rule for what NaN to return in the case of a fused multiply-add of inf * 0 + NaN. Different architectures thus do different things: * some return the default NaN * some return the input NaN * Arm returns the default NaN if the input NaN is quiet, and the input NaN if it is signalling We want to make this logic be runtime selected rather than hardcoded into the binary, because: * this will let us have multiple targets in one QEMU binary * the Arm FEAT_AFP architectural feature includes letting the guest select a NaN propagation rule at runtime In this commit we add an enum for the propagation rule, the field in float_status, and the corresponding getters and setters. We change pickNaNMulAdd to honour this, but because all targets still leave this field at its default 0 value, the fallback logic will pick the rule type with the old ifdef ladder. Note that four architectures both use the muladd softfloat functions and did not have a branch of the ifdef ladder to specify their behaviour (and so were ending up with the "default" case, probably wrongly): i386, HPPA, SH4 and Tricore. SH4 and Tricore both set default_nan_mode, and so will never get into pickNaNMulAdd(). For HPPA and i386 we retain the same behaviour as the old default-case, which is to not ever return the default NaN. This might not be correct but it is not a behaviour change. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20241202131347.498124-4-peter.maydell@linaro.org
This commit is contained in:
parent
ed885e3069
commit
4080eebd73
3 changed files with 95 additions and 30 deletions
|
@ -475,6 +475,8 @@ static int pickNaN(FloatClass a_cls, FloatClass b_cls,
|
|||
static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
||||
bool infzero, float_status *status)
|
||||
{
|
||||
FloatInfZeroNaNRule rule = status->float_infzeronan_rule;
|
||||
|
||||
/*
|
||||
* We guarantee not to require the target to tell us how to
|
||||
* pick a NaN if we're always returning the default NaN.
|
||||
|
@ -482,14 +484,68 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
* specify.
|
||||
*/
|
||||
assert(!status->default_nan_mode);
|
||||
|
||||
if (rule == float_infzeronan_none) {
|
||||
/*
|
||||
* Temporarily fall back to ifdef ladder
|
||||
*/
|
||||
#if defined(TARGET_ARM)
|
||||
/* For ARM, the (inf,zero,qnan) case sets InvalidOp and returns
|
||||
* the default NaN
|
||||
*/
|
||||
if (infzero && is_qnan(c_cls)) {
|
||||
return 3;
|
||||
/*
|
||||
* For ARM, the (inf,zero,qnan) case returns the default NaN,
|
||||
* but (inf,zero,snan) returns the input NaN.
|
||||
*/
|
||||
rule = float_infzeronan_dnan_if_qnan;
|
||||
#elif defined(TARGET_MIPS)
|
||||
if (snan_bit_is_one(status)) {
|
||||
/*
|
||||
* For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan)
|
||||
* case sets InvalidOp and returns the default NaN
|
||||
*/
|
||||
rule = float_infzeronan_dnan_always;
|
||||
} else {
|
||||
/*
|
||||
* For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan)
|
||||
* case sets InvalidOp and returns the input value 'c'
|
||||
*/
|
||||
rule = float_infzeronan_dnan_never;
|
||||
}
|
||||
#elif defined(TARGET_PPC) || defined(TARGET_SPARC) || \
|
||||
defined(TARGET_XTENSA) || defined(TARGET_HPPA) || \
|
||||
defined(TARGET_I386) || defined(TARGET_LOONGARCH)
|
||||
/*
|
||||
* For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan)
|
||||
* case sets InvalidOp and returns the input value 'c'
|
||||
*/
|
||||
/*
|
||||
* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer
|
||||
* to return an input NaN if we have one (ie c) rather than generating
|
||||
* a default NaN
|
||||
*/
|
||||
rule = float_infzeronan_dnan_never;
|
||||
#elif defined(TARGET_S390X)
|
||||
rule = float_infzeronan_dnan_always;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (infzero) {
|
||||
/*
|
||||
* Inf * 0 + NaN -- some implementations return the default NaN here,
|
||||
* and some return the input NaN.
|
||||
*/
|
||||
switch (rule) {
|
||||
case float_infzeronan_dnan_never:
|
||||
return 2;
|
||||
case float_infzeronan_dnan_always:
|
||||
return 3;
|
||||
case float_infzeronan_dnan_if_qnan:
|
||||
return is_qnan(c_cls) ? 3 : 2;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(TARGET_ARM)
|
||||
|
||||
/* This looks different from the ARM ARM pseudocode, because the ARM ARM
|
||||
* puts the operands to a fused mac operation (a*b)+c in the order c,a,b.
|
||||
*/
|
||||
|
@ -508,13 +564,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
}
|
||||
#elif defined(TARGET_MIPS)
|
||||
if (snan_bit_is_one(status)) {
|
||||
/*
|
||||
* For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan)
|
||||
* case sets InvalidOp and returns the default NaN
|
||||
*/
|
||||
if (infzero) {
|
||||
return 3;
|
||||
}
|
||||
/* Prefer sNaN over qNaN, in the a, b, c order. */
|
||||
if (is_snan(a_cls)) {
|
||||
return 0;
|
||||
|
@ -530,10 +579,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
return 2;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan)
|
||||
* case sets InvalidOp and returns the input value 'c'
|
||||
*/
|
||||
/* Prefer sNaN over qNaN, in the c, a, b order. */
|
||||
if (is_snan(c_cls)) {
|
||||
return 2;
|
||||
|
@ -550,11 +595,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
}
|
||||
}
|
||||
#elif defined(TARGET_LOONGARCH64)
|
||||
/*
|
||||
* For LoongArch systems that conform to IEEE754-2008, the (inf,zero,nan)
|
||||
* case sets InvalidOp and returns the input value 'c'
|
||||
*/
|
||||
|
||||
/* Prefer sNaN over qNaN, in the c, a, b order. */
|
||||
if (is_snan(c_cls)) {
|
||||
return 2;
|
||||
|
@ -570,11 +610,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
return 1;
|
||||
}
|
||||
#elif defined(TARGET_PPC)
|
||||
/* For PPC, the (inf,zero,qnan) case sets InvalidOp, but we prefer
|
||||
* to return an input NaN if we have one (ie c) rather than generating
|
||||
* a default NaN
|
||||
*/
|
||||
|
||||
/* If fRA is a NaN return it; otherwise if fRB is a NaN return it;
|
||||
* otherwise return fRC. Note that muladd on PPC is (fRA * fRC) + frB
|
||||
*/
|
||||
|
@ -586,10 +621,6 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
return 1;
|
||||
}
|
||||
#elif defined(TARGET_S390X)
|
||||
if (infzero) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
if (is_snan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_snan(b_cls)) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue