target/arm: Fix f16_dotadd vs nan selection

Implement FPProcessNaNs4 within f16_dotadd, rather than
simply letting NaNs propagate through the function.

Cc: qemu-stable@nongnu.org
Fixes: 3916841ac7 ("target/arm: Implement FMOPA, FMOPS (widening)")
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20250704142112.1018902-9-richard.henderson@linaro.org
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Richard Henderson
2025-07-04 15:52:21 +01:00
committed by Peter Maydell
parent 3801c5b75f
commit cfc688c00a
+46 -16
View File
@@ -1005,25 +1005,55 @@ static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2,
* - we have pre-set-up copy of s_std which is set to round-to-odd,
* for the multiply (see below)
*/
float64 e1r = float16_to_float64(e1 & 0xffff, true, s_f16);
float64 e1c = float16_to_float64(e1 >> 16, true, s_f16);
float64 e2r = float16_to_float64(e2 & 0xffff, true, s_f16);
float64 e2c = float16_to_float64(e2 >> 16, true, s_f16);
float64 t64;
float16 h1r = e1 & 0xffff;
float16 h1c = e1 >> 16;
float16 h2r = e2 & 0xffff;
float16 h2c = e2 >> 16;
float32 t32;
/*
* The ARM pseudocode function FPDot performs both multiplies
* and the add with a single rounding operation. Emulate this
* by performing the first multiply in round-to-odd, then doing
* the second multiply as fused multiply-add, and rounding to
* float32 all in one step.
*/
t64 = float64_mul(e1r, e2r, s_odd);
t64 = float64r32_muladd(e1c, e2c, t64, 0, s_std);
/* C.f. FPProcessNaNs4 */
if (float16_is_any_nan(h1r) || float16_is_any_nan(h1c) ||
float16_is_any_nan(h2r) || float16_is_any_nan(h2c)) {
float16 t16;
/* This conversion is exact, because we've already rounded. */
t32 = float64_to_float32(t64, s_std);
if (float16_is_signaling_nan(h1r, s_f16)) {
t16 = h1r;
} else if (float16_is_signaling_nan(h1c, s_f16)) {
t16 = h1c;
} else if (float16_is_signaling_nan(h2r, s_f16)) {
t16 = h2r;
} else if (float16_is_signaling_nan(h2c, s_f16)) {
t16 = h2c;
} else if (float16_is_any_nan(h1r)) {
t16 = h1r;
} else if (float16_is_any_nan(h1c)) {
t16 = h1c;
} else if (float16_is_any_nan(h2r)) {
t16 = h2r;
} else {
t16 = h2c;
}
t32 = float16_to_float32(t16, true, s_f16);
} else {
float64 e1r = float16_to_float64(h1r, true, s_f16);
float64 e1c = float16_to_float64(h1c, true, s_f16);
float64 e2r = float16_to_float64(h2r, true, s_f16);
float64 e2c = float16_to_float64(h2c, true, s_f16);
float64 t64;
/*
* The ARM pseudocode function FPDot performs both multiplies
* and the add with a single rounding operation. Emulate this
* by performing the first multiply in round-to-odd, then doing
* the second multiply as fused multiply-add, and rounding to
* float32 all in one step.
*/
t64 = float64_mul(e1r, e2r, s_odd);
t64 = float64r32_muladd(e1c, e2c, t64, 0, s_std);
/* This conversion is exact, because we've already rounded. */
t32 = float64_to_float32(t64, s_std);
}
/* The final accumulation step is not fused. */
return float32_add(sum, t32, s_std);