[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:
@@ -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;
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user