[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:
@@ -8481,7 +8481,7 @@ public:
|
|||||||
|
|
||||||
const bool a_notnan = a_known.isKnownNeverNaN() || llvm::cast<llvm::ConstantInt>(ci->getOperand(3))->getZExtValue() != 0;
|
const bool a_notnan = a_known.isKnownNeverNaN() || llvm::cast<llvm::ConstantInt>(ci->getOperand(3))->getZExtValue() != 0;
|
||||||
const bool b_notnan = b_known.isKnownNeverNaN() || llvm::cast<llvm::ConstantInt>(ci->getOperand(4))->getZExtValue() != 0;
|
const bool b_notnan = b_known.isKnownNeverNaN() || llvm::cast<llvm::ConstantInt>(ci->getOperand(4))->getZExtValue() != 0;
|
||||||
|
|
||||||
if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate)
|
if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate)
|
||||||
{
|
{
|
||||||
if (a.value == b.value || (a_notnan && b_notnan))
|
if (a.value == b.value || (a_notnan && b_notnan))
|
||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user