diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 9d562b7a23..ddfe600a7a 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -7816,23 +7816,6 @@ public: return clamp_smax(v); } - // Checks for postive and negative zero, or Denormal (treated as zero) - // If sign is +-1 check equality againts all sign bits - bool is_spu_float_zero(v128 a, int sign = 0) - { - for (u32 i = 0; i < 4; i++) - { - const u32 exponent = a._u32[i] & 0x7f800000u; - - if (exponent || (sign && (sign >= 0) != (a._s32[i] >= 0))) - { - // Normalized number - return false; - } - } - return true; - } - template static llvm_calli frest(T&& a) { @@ -8384,36 +8367,24 @@ public: value_t fma32x4(value_t a, value_t b, value_t c) { + const auto a_known = get_known_fp_class<2>(a, spu_zero_fp_classes); + const auto b_known = get_known_fp_class<2>(b, spu_zero_fp_classes); + + return fma32x4(a, b, c, a_known, b_known); + } + + value_t fma32x4(value_t a, value_t b, value_t c, llvm::KnownFPClass a_known, llvm::KnownFPClass b_known) + { + const auto c_known = get_known_fp_class<2>(c, spu_zero_fp_classes); + // Optimization: Emit only a floating multiply if the addend is zero // This is odd since SPU code could just use the FM instruction, but it seems common enough - if (auto [ok, data] = get_const_vector(c.value, m_pos); ok) + if (c_known.isKnownAlways(spu_zero_fp_classes)) { - if (is_spu_float_zero(data, 0)) - { - return eval(a * b); - } + return eval(a * b); } - if ([&]() - { - if (auto [ok, data] = get_const_vector(a.value, m_pos); ok) - { - if (is_spu_float_zero(data, 0)) - { - return true; - } - } - - if (auto [ok, data] = get_const_vector(b.value, m_pos); ok) - { - if (is_spu_float_zero(data, 0)) - { - return true; - } - } - - return false; - }()) + if (a_known.isKnownAlways(spu_zero_fp_classes) || b_known.isKnownAlways(spu_zero_fp_classes)) { // Just return the added value if either a or b are +-0 return c; @@ -8454,7 +8425,15 @@ public: const auto b = value(ci->getOperand(1)); const auto c = value(ci->getOperand(2)); - return fma32x4(eval(-clamp_smax(a)), clamp_smax(b), c); + constexpr auto interested_classes = llvm::FPClassTest::fcNan | llvm::FPClassTest::fcInf | spu_zero_fp_classes; + auto a_known = get_known_fp_class<4>(a, interested_classes); + auto b_known = get_known_fp_class<4>(b, interested_classes); + + const auto a_clamp = clamp_smax(a, a_known); + const auto b_clamp = clamp_smax(b, b_known); + + a_known.fneg(); + return fma32x4(eval(-a_clamp), b_clamp, c, a_known, b_known); }); set_vr(op.rt4, fnms(get_vr(op.ra), get_vr(op.rb), get_vr(op.rc))); @@ -8488,38 +8467,45 @@ public: const auto a = value(ci->getOperand(0)); const auto b = value(ci->getOperand(1)); const auto c = value(ci->getOperand(2)); - const bool a_notnan = llvm::cast(ci->getOperand(3))->getZExtValue() != 0; - const bool b_notnan = llvm::cast(ci->getOperand(4))->getZExtValue() != 0; + + constexpr auto interested_classes = llvm::FPClassTest::fcNan | spu_zero_fp_classes; + + // The "mask away with zero multiplier" case is already optimized for in fma32x4 + const auto a_known = get_known_fp_class<2>(a, interested_classes); + const auto b_known = get_known_fp_class<2>(b, interested_classes); + + 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)) { - return fma32x4(a, b, c); + return fma32x4(a, b, c, a_known, b_known); } 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); + return fma32x4(a, eval(cb), c, a_known, b_known); } 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); + return fma32x4(eval(ca), b, c, a_known, b_known); } 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); + return fma32x4(eval(ca), eval(cb), c, a_known, b_known); } else { - return fma32x4(a, b, c); + return fma32x4(a, b, c, a_known, b_known); } }); @@ -8799,7 +8785,14 @@ public: } #endif - return fma32x4(clamp_smax(a), clamp_smax(b), eval(-c)); + constexpr auto interested_classes = llvm::FPClassTest::fcNan | llvm::FPClassTest::fcInf | spu_zero_fp_classes; + const auto a_known = get_known_fp_class<4>(a, interested_classes); + const auto b_known = get_known_fp_class<4>(b, interested_classes); + + const auto a_clamp = clamp_smax(a, a_known); + const auto b_clamp = clamp_smax(b, b_known); + + return fma32x4(a_clamp, b_clamp, eval(-c), a_known, b_known); } else {