[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
@@ -4278,6 +4278,13 @@ template <typename T1, typename T2, typename T3>
{
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:
// Custom intrinsic table
+28 -34
View File
@@ -7768,56 +7768,50 @@ public:
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
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 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))
{
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<f32[4]>(std::bit_cast<f32, u32>(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)