[SPU LLVM] Add KnownFPClass to clamping

Avoid clamping if the value is known not to be Inf/NaN (overflow). Includes a optional precalculated argument, which only calculates the known if it has not been done already.
This commit is contained in:
Walter
2026-07-17 11:45:16 +10:00
committed by Elad
parent cdbc43712b
commit aec0917a86
2 changed files with 35 additions and 34 deletions
+7
View File
@@ -4279,6 +4279,13 @@ template <typename T1, typename T2, typename T3>
return llvm::KnownBits::makeConstant(llvm::APInt(sizeof(T) * 8, u64(value))); return llvm::KnownBits::makeConstant(llvm::APInt(sizeof(T) * 8, u64(value)));
} }
template <unsigned depth = llvm::MaxAnalysisRecursionDepth, typename T>
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: private:
// Custom intrinsic table // Custom intrinsic table
std::unordered_map<std::string_view, std::function<llvm::Value*(llvm::CallInst*)>> m_intrinsics; std::unordered_map<std::string_view, std::function<llvm::Value*(llvm::CallInst*)>> m_intrinsics;
+28 -34
View File
@@ -7768,56 +7768,50 @@ public:
set_vr(op.rt, -(a * b + c)); set_vr(op.rt, -(a * b + c));
} }
bool is_input_positive(value_t<f32[4]> a)
{
if (auto [ok, v0, v1] = match_expr(a, match<f32[4]>() * match<f32[4]>()); ok && v0.eq(v1))
{
return true;
}
return false;
}
// clamping helpers // clamping helpers
value_t<f32[4]> clamp_positive_smax(value_t<f32[4]> v) value_t<f32[4]> clamp_positive_smax(value_t<f32[4]> v, std::optional<llvm::KnownFPClass> known_opt = std::nullopt)
{ {
return eval(bitcast<f32[4]>(min(bitcast<s32[4]>(v),splat<s32[4]>(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<f32[4]>(min(bitcast<s32[4]>(v), splat<s32[4]>(0x7f7fffff))));
} }
value_t<f32[4]> clamp_negative_smax(value_t<f32[4]> v) value_t<f32[4]> clamp_negative_smax(value_t<f32[4]> v, std::optional<llvm::KnownFPClass> 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 v;
return eval(bitcast<f32[4]>(min(bitcast<u32[4]>(v), splat<u32[4]>(0xff7fffff))));
}
value_t<f32[4]> clamp_smax(value_t<f32[4]> v, std::optional<llvm::KnownFPClass> 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<f32[4]>(std::bit_cast<f32, u32>(0x7f7fffff)), 0x2, 0xff));
} }
return eval(bitcast<f32[4]>(min(bitcast<u32[4]>(v),splat<u32[4]>(0xff7fffff)))); return eval(clamp_positive_smax(clamp_negative_smax(v, known), known));
} }
value_t<f32[4]> clamp_smax(value_t<f32[4]> v, u32 gpr = s_reg_max) value_t<f32[4]> clamp_smax(value_t<f32[4]> v, u32 gpr)
{ {
if (m_reduced_loop_info && gpr < s_reg_max && m_reduced_loop_info->is_gpr_not_NaN_hint(gpr)) if (m_reduced_loop_info && gpr < s_reg_max && m_reduced_loop_info->is_gpr_not_NaN_hint(gpr))
{ {
return v; return v;
} }
if (m_use_avx512) return clamp_smax(v);
{
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<f32[4]>(std::bit_cast<f32, u32>(0x7f7fffff)), 0x2, 0xff));
}
return eval(clamp_positive_smax(clamp_negative_smax(v)));
} }
// Checks for postive and negative zero, or Denormal (treated as zero) // Checks for postive and negative zero, or Denormal (treated as zero)