From d0fdd9bb6d096eca0c88eb98583d2fc728c67979 Mon Sep 17 00:00:00 2001 From: Walter Date: Fri, 17 Jul 2026 11:46:55 +1000 Subject: [PATCH] [SPU LLVM] Add `KnownFPClass` to multiply and equals Replace constant checks in `FM`, `FCEQ`, and `FCMEQ` with `KnownFPClass`. The equality compares now also optimize for when the operands is infinity, which is safe as it behaves identically to the default path. --- rpcs3/Emu/Cell/SPULLVMRecompiler.cpp | 88 +++++++--------------------- 1 file changed, 22 insertions(+), 66 deletions(-) diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 3e3f383ecb..9d562b7a23 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -7767,6 +7767,8 @@ public: else set_vr(op.rt, -(a * b + c)); } + + static constexpr auto spu_zero_fp_classes = llvm::FPClassTest::fcSubnormal | llvm::FPClassTest::fcZero; // clamping helpers value_t clamp_positive_smax(value_t v, std::optional known_opt = std::nullopt) @@ -8135,8 +8137,12 @@ public: { const auto a = value(ci->getOperand(0)); const auto b = value(ci->getOperand(1)); - const bool a_notnan = llvm::cast(ci->getOperand(2))->getZExtValue() != 0; - const bool b_notnan = llvm::cast(ci->getOperand(3))->getZExtValue() != 0; + + const auto a_known = get_known_fp_class<2>(a, llvm::FPClassTest::fcNan); + const auto b_known = get_known_fp_class<2>(b, llvm::FPClassTest::fcNan); + + const bool a_notnan = a_known.isKnownNeverNaN() || llvm::cast(ci->getOperand(2))->getZExtValue() != 0; + const bool b_notnan = b_known.isKnownNeverNaN() || llvm::cast(ci->getOperand(3))->getZExtValue() != 0; if (g_cfg.core.spu_xfloat_accuracy == xfloat_accuracy::approximate) { @@ -8294,44 +8300,19 @@ public: const auto a = value(ci->getOperand(0)); const auto b = value(ci->getOperand(1)); - const value_t ab[2]{a, b}; + constexpr auto interested_classes = llvm::FPClassTest::fcNan | spu_zero_fp_classes; + const auto a_known = get_known_fp_class<2>(a, interested_classes); + const auto b_known = get_known_fp_class<2>(b, interested_classes); - bit_set<2> safe_float_compare(0); - bit_set<2> safe_int_compare(0); + const bool safe_float_compare = a_known.isKnownNeverNaN() && b_known.isKnownNeverNaN(); + const bool safe_int_compare = a_known.isKnownNever(spu_zero_fp_classes) && b_known.isKnownNever(spu_zero_fp_classes); - for (u32 i = 0; i < 2; i++) - { - if (auto [ok, data] = get_const_vector(ab[i].value, m_pos, __LINE__ + i); ok) - { - safe_float_compare.set_unsafe(i); - safe_int_compare.set_unsafe(i); - - for (u32 j = 0; j < 4; j++) - { - const u32 value = data._u32[j]; - const u8 exponent = static_cast(value >> 23); - - // unsafe if nan - if (exponent == 255) - { - safe_float_compare.reset_unsafe(i); - } - - // unsafe if denormal or 0 - if (!exponent) - { - safe_int_compare.reset_unsafe(i); - } - } - } - } - - if (safe_float_compare.any()) + if (safe_float_compare) { return eval(sext(fcmp_ord(a == b))); } - if (safe_int_compare.any()) + if (safe_int_compare) { return eval(sext(bitcast(a) == bitcast(b))); } @@ -8368,47 +8349,22 @@ public: const auto a = value(ci->getOperand(0)); const auto b = value(ci->getOperand(1)); - const value_t ab[2]{a, b}; + constexpr auto interested_classes = llvm::FPClassTest::fcNan | spu_zero_fp_classes; + const auto a_known = get_known_fp_class<2>(a, interested_classes); + const auto b_known = get_known_fp_class<2>(b, interested_classes); - bit_set<2> safe_float_compare(0); - bit_set<2> safe_int_compare(0); - - for (u32 i = 0; i < 2; i++) - { - if (auto [ok, data] = get_const_vector(ab[i].value, m_pos, __LINE__ + i); ok) - { - safe_float_compare.set_unsafe(i); - safe_int_compare.set_unsafe(i); - - for (u32 j = 0; j < 4; j++) - { - const u32 value = data._u32[j]; - const u8 exponent = static_cast(value >> 23); - - // unsafe if nan - if (exponent == 255) - { - safe_float_compare.reset_unsafe(i); - } - - // unsafe if denormal or 0 - if (!exponent) - { - safe_int_compare.reset_unsafe(i); - } - } - } - } + const bool safe_float_compare = a_known.isKnownNeverNaN() && b_known.isKnownNeverNaN(); + const bool safe_int_compare = a_known.isKnownNever(spu_zero_fp_classes) && b_known.isKnownNever(spu_zero_fp_classes); const auto fa = eval(fabs(a)); const auto fb = eval(fabs(b)); - if (safe_float_compare.any()) + if (safe_float_compare) { return eval(sext(fcmp_ord(fa == fb))); } - if (safe_int_compare.any()) + if (safe_int_compare) { return eval(sext(bitcast(fa) == bitcast(fb))); }