diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index b95ea4051b..6b6c214123 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -4278,6 +4278,13 @@ template { return llvm::KnownBits::makeConstant(llvm::APInt(sizeof(T) * 8, u64(value))); } + + template + llvm::KnownFPClass get_known_fp_class(T a, llvm::FPClassTest interested_classes) + { + static_assert(depth <= llvm::MaxAnalysisRecursionDepth, "Depth parameter can only decrease search. Default is max."); + return llvm::computeKnownFPClass(a.eval(m_ir), m_module->getDataLayout(), interested_classes, llvm::MaxAnalysisRecursionDepth - depth); + } private: // Custom intrinsic table diff --git a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp index 7243562c49..3e3f383ecb 100644 --- a/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp +++ b/rpcs3/Emu/Cell/SPULLVMRecompiler.cpp @@ -7768,56 +7768,50 @@ public: set_vr(op.rt, -(a * b + c)); } - bool is_input_positive(value_t a) - { - if (auto [ok, v0, v1] = match_expr(a, match() * match()); ok && v0.eq(v1)) - { - return true; - } - - return false; - } - // clamping helpers - value_t clamp_positive_smax(value_t v) + value_t clamp_positive_smax(value_t v, std::optional known_opt = std::nullopt) { - return eval(bitcast(min(bitcast(v),splat(0x7f7fffff)))); + constexpr auto overflow_classes = llvm::FPClassTest::fcNan | llvm::FPClassTest::fcPosInf; + const auto known = known_opt.value_or(get_known_fp_class<3>(v, overflow_classes)); + + if (known.isKnownNever(overflow_classes)) + return v; + + return eval(bitcast(min(bitcast(v), splat(0x7f7fffff)))); } - value_t clamp_negative_smax(value_t v) + value_t clamp_negative_smax(value_t v, std::optional known_opt = std::nullopt) { - if (is_input_positive(v)) - { + constexpr auto overflow_classes = llvm::FPClassTest::fcNan | llvm::FPClassTest::fcNegInf; + const auto known = known_opt.value_or(get_known_fp_class<3>(v, overflow_classes)); + + if (known.isKnownNever(overflow_classes)) return v; + + return eval(bitcast(min(bitcast(v), splat(0xff7fffff)))); + } + + value_t clamp_smax(value_t v, std::optional known_opt = std::nullopt) + { + const auto known = known_opt.value_or(get_known_fp_class<3>(v, llvm::FPClassTest::fcNan | llvm::FPClassTest::fcInf)); + + // Avoid pessimation when full clamping isn't needed + if (m_use_avx512 && !(known.isKnownNeverNaN() && (known.isKnownNeverPosInfinity() || known.isKnownNeverNegInfinity()))) + { + return eval(vrangeps(v, fsplat(std::bit_cast(0x7f7fffff)), 0x2, 0xff)); } - return eval(bitcast(min(bitcast(v),splat(0xff7fffff)))); + return eval(clamp_positive_smax(clamp_negative_smax(v, known), known)); } - value_t clamp_smax(value_t v, u32 gpr = s_reg_max) + value_t clamp_smax(value_t v, u32 gpr) { if (m_reduced_loop_info && gpr < s_reg_max && m_reduced_loop_info->is_gpr_not_NaN_hint(gpr)) { return v; } - if (m_use_avx512) - { - if (is_input_positive(v)) - { - return eval(clamp_positive_smax(v)); - } - - if (auto [ok, data] = get_const_vector(v.value, m_pos); ok) - { - // Avoid pessimation when input is constant - return eval(clamp_positive_smax(clamp_negative_smax(v))); - } - - return eval(vrangeps(v, fsplat(std::bit_cast(0x7f7fffff)), 0x2, 0xff)); - } - - return eval(clamp_positive_smax(clamp_negative_smax(v))); + return clamp_smax(v); } // Checks for postive and negative zero, or Denormal (treated as zero)