mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-05 16:53:55 -06:00
softfloat: Allow runtime choice of NaN propagation for muladd
IEEE 758 does not define a fixed rule for which NaN to pick as the result if both operands of a 3-operand fused multiply-add operation are NaNs. As a result different architectures have ended up with different rules for propagating NaNs. QEMU currently hardcodes the NaN propagation logic into the binary because pickNaNMulAdd() has an ifdef ladder for different targets. We want to make the propagation rule instead be selectable at runtime, 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. It's valid not to set a propagation rule if default_nan_mode is enabled, because in that case there's no need to pick a NaN; all the callers of pickNaNMulAdd() catch this case and skip calling it. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20241202131347.498124-16-peter.maydell@linaro.org
This commit is contained in:
parent
d62c734d52
commit
7a944c30f7
3 changed files with 107 additions and 126 deletions
|
@ -475,6 +475,10 @@ static int pickNaN(FloatClass a_cls, FloatClass b_cls,
|
|||
static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
||||
bool infzero, bool have_snan, float_status *status)
|
||||
{
|
||||
FloatClass cls[3] = { a_cls, b_cls, c_cls };
|
||||
Float3NaNPropRule rule = status->float_3nan_prop_rule;
|
||||
int which;
|
||||
|
||||
/*
|
||||
* We guarantee not to require the target to tell us how to
|
||||
* pick a NaN if we're always returning the default NaN.
|
||||
|
@ -500,145 +504,56 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls,
|
|||
}
|
||||
}
|
||||
|
||||
if (rule == float_3nan_prop_none) {
|
||||
#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.
|
||||
*/
|
||||
if (is_snan(c_cls)) {
|
||||
return 2;
|
||||
} else if (is_snan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_snan(b_cls)) {
|
||||
return 1;
|
||||
} else if (is_qnan(c_cls)) {
|
||||
return 2;
|
||||
} else if (is_qnan(a_cls)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
rule = float_3nan_prop_s_cab;
|
||||
#elif defined(TARGET_MIPS)
|
||||
if (snan_bit_is_one(status)) {
|
||||
/* Prefer sNaN over qNaN, in the a, b, c order. */
|
||||
if (is_snan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_snan(b_cls)) {
|
||||
return 1;
|
||||
} else if (is_snan(c_cls)) {
|
||||
return 2;
|
||||
} else if (is_qnan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_qnan(b_cls)) {
|
||||
return 1;
|
||||
if (snan_bit_is_one(status)) {
|
||||
rule = float_3nan_prop_s_abc;
|
||||
} else {
|
||||
return 2;
|
||||
rule = float_3nan_prop_s_cab;
|
||||
}
|
||||
} else {
|
||||
/* Prefer sNaN over qNaN, in the c, a, b order. */
|
||||
if (is_snan(c_cls)) {
|
||||
return 2;
|
||||
} else if (is_snan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_snan(b_cls)) {
|
||||
return 1;
|
||||
} else if (is_qnan(c_cls)) {
|
||||
return 2;
|
||||
} else if (is_qnan(a_cls)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#elif defined(TARGET_LOONGARCH64)
|
||||
/* Prefer sNaN over qNaN, in the c, a, b order. */
|
||||
if (is_snan(c_cls)) {
|
||||
return 2;
|
||||
} else if (is_snan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_snan(b_cls)) {
|
||||
return 1;
|
||||
} else if (is_qnan(c_cls)) {
|
||||
return 2;
|
||||
} else if (is_qnan(a_cls)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
rule = float_3nan_prop_s_cab;
|
||||
#elif defined(TARGET_PPC)
|
||||
/* 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
|
||||
*/
|
||||
if (is_nan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_nan(c_cls)) {
|
||||
return 2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
rule = float_3nan_prop_acb;
|
||||
#elif defined(TARGET_S390X)
|
||||
if (is_snan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_snan(b_cls)) {
|
||||
return 1;
|
||||
} else if (is_snan(c_cls)) {
|
||||
return 2;
|
||||
} else if (is_qnan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_qnan(b_cls)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
rule = float_3nan_prop_s_abc;
|
||||
#elif defined(TARGET_SPARC)
|
||||
/* Prefer SNaN over QNaN, order C, B, A. */
|
||||
if (is_snan(c_cls)) {
|
||||
return 2;
|
||||
} else if (is_snan(b_cls)) {
|
||||
return 1;
|
||||
} else if (is_snan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_qnan(c_cls)) {
|
||||
return 2;
|
||||
} else if (is_qnan(b_cls)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
rule = float_3nan_prop_s_cba;
|
||||
#elif defined(TARGET_XTENSA)
|
||||
/*
|
||||
* For Xtensa, the (inf,zero,nan) case sets InvalidOp and returns
|
||||
* an input NaN if we have one (ie c).
|
||||
*/
|
||||
if (status->use_first_nan) {
|
||||
if (is_nan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_nan(b_cls)) {
|
||||
return 1;
|
||||
if (status->use_first_nan) {
|
||||
rule = float_3nan_prop_abc;
|
||||
} else {
|
||||
return 2;
|
||||
rule = float_3nan_prop_cba;
|
||||
}
|
||||
} else {
|
||||
if (is_nan(c_cls)) {
|
||||
return 2;
|
||||
} else if (is_nan(b_cls)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* A default implementation: prefer a to b to c.
|
||||
* This is unlikely to actually match any real implementation.
|
||||
*/
|
||||
if (is_nan(a_cls)) {
|
||||
return 0;
|
||||
} else if (is_nan(b_cls)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
rule = float_3nan_prop_abc;
|
||||
#endif
|
||||
}
|
||||
|
||||
assert(rule != float_3nan_prop_none);
|
||||
if (have_snan && (rule & R_3NAN_SNAN_MASK)) {
|
||||
/* We have at least one SNaN input and should prefer it */
|
||||
do {
|
||||
which = rule & R_3NAN_1ST_MASK;
|
||||
rule >>= R_3NAN_1ST_LENGTH;
|
||||
} while (!is_snan(cls[which]));
|
||||
} else {
|
||||
do {
|
||||
which = rule & R_3NAN_1ST_MASK;
|
||||
rule >>= R_3NAN_1ST_LENGTH;
|
||||
} while (!is_nan(cls[which]));
|
||||
}
|
||||
return which;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue