[SPU LLVM] Use select in FMA to shorten its dependency chain (#19052)

The `FMA` instruction conditionally zeros one multiplicand if the other
is zero/denormal to emulate the xfloat's extended range. This patch
replaces that with a `select(is_non_zero, fma(a, b, c), c)` which has a
shorter dependency chain in most situations and probably allows LLVM to
better optimize it with surrounding instructions.

I added a AVX512 path that uses `vfixupimmps` to prevent a pessimization
where LLVM transforms it into a strictly serial predicate chain. I
confirmed that it works with denormals and both zeros.

https://godbolt.org/z/hsP5G43Ye
This commit is contained in:
Walter
2026-07-18 18:18:10 +10:00
committed by GitHub
parent 0fcb15ab18
commit 9b3a916af0
+17 -11
View File
@@ -8491,22 +8491,28 @@ public:
if (a_notnan) if (a_notnan)
{ {
const auto ma = sext<s32[4]>(fcmp_uno(a != fsplat<f32[4]>(0.))); const auto normal_fma = fma32x4(a, b, c, a_known, b_known);
const auto cb = bitcast<f32[4]>(bitcast<s32[4]>(b) & ma); return eval(select(fcmp_uno(a != fsplat<f32[4]>(0.)), normal_fma, c));
return fma32x4(a, eval(cb), c, a_known, b_known);
} }
else if (b_notnan) else if (b_notnan)
{ {
const auto mb = sext<s32[4]>(fcmp_uno(b != fsplat<f32[4]>(0.))); const auto normal_fma = fma32x4(a, b, c, a_known, b_known);
const auto ca = bitcast<f32[4]>(bitcast<s32[4]>(a) & mb); return eval(select(fcmp_uno(b != fsplat<f32[4]>(0.)), normal_fma, c));
return fma32x4(eval(ca), b, c, a_known, b_known);
} }
const auto ma = sext<s32[4]>(fcmp_uno(a != fsplat<f32[4]>(0.))); // Same number of operations well preventing a serial predicate chain pessimization
const auto mb = sext<s32[4]>(fcmp_uno(b != fsplat<f32[4]>(0.))); if (m_use_avx512)
const auto ca = bitcast<f32[4]>(bitcast<s32[4]>(a) & mb); {
const auto cb = bitcast<f32[4]>(bitcast<s32[4]>(b) & ma); // 0/denormals -> +0, else 1st operand
return fma32x4(eval(ca), eval(cb), c, a_known, b_known); const auto ca = vfixupimmps(a, b, splat<u32[4]>(0x00000800u), 0, 0xff);
const auto cb = vfixupimmps(b, a, splat<u32[4]>(0x00000800u), 0, 0xff);
return fma32x4(ca, cb, c, a_known, b_known);
}
const auto normal_fma = fma32x4(a, b, c, a_known, b_known);
const auto a_cmp = fcmp_uno(a != fsplat<f32[4]>(0.));
const auto b_cmp = fcmp_uno(b != fsplat<f32[4]>(0.));
return eval(select(a_cmp & b_cmp, normal_fma, c));
} }
else else
{ {