From 9b3a916af02fc23a5ccd44db8b0c668710a86cc9 Mon Sep 17 00:00:00 2001 From: Walter <90877047+WalterKruger@users.noreply.github.com> Date: Sat, 18 Jul 2026 18:18:10 +1000 Subject: [PATCH] [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 --- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 30 +++++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 964d048b64..b5f784793d 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -8481,7 +8481,7 @@ public: const bool a_notnan = a_known.isKnownNeverNaN() || llvm::cast(ci->getOperand(3))->getZExtValue() != 0; const bool b_notnan = b_known.isKnownNeverNaN() || llvm::cast(ci->getOperand(4))->getZExtValue() != 0; - + if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { if (a.value == b.value || (a_notnan && b_notnan)) @@ -8491,22 +8491,28 @@ public: if (a_notnan) { - const auto ma = sext(fcmp_uno(a != fsplat(0.))); - const auto cb = bitcast(bitcast(b) & ma); - return fma32x4(a, eval(cb), c, a_known, b_known); + const auto normal_fma = fma32x4(a, b, c, a_known, b_known); + return eval(select(fcmp_uno(a != fsplat(0.)), normal_fma, c)); } else if (b_notnan) { - const auto mb = sext(fcmp_uno(b != fsplat(0.))); - const auto ca = bitcast(bitcast(a) & mb); - return fma32x4(eval(ca), b, c, a_known, b_known); + const auto normal_fma = fma32x4(a, b, c, a_known, b_known); + return eval(select(fcmp_uno(b != fsplat(0.)), normal_fma, c)); } - const auto ma = sext(fcmp_uno(a != fsplat(0.))); - const auto mb = sext(fcmp_uno(b != fsplat(0.))); - const auto ca = bitcast(bitcast(a) & mb); - const auto cb = bitcast(bitcast(b) & ma); - return fma32x4(eval(ca), eval(cb), c, a_known, b_known); + // Same number of operations well preventing a serial predicate chain pessimization + if (m_use_avx512) + { + // 0/denormals -> +0, else 1st operand + const auto ca = vfixupimmps(a, b, splat(0x00000800u), 0, 0xff); + const auto cb = vfixupimmps(b, a, splat(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(0.)); + const auto b_cmp = fcmp_uno(b != fsplat(0.)); + return eval(select(a_cmp & b_cmp, normal_fma, c)); } else {