aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Analysis/ValueTracking.cpp
diff options
context:
space:
mode:
authorTim Gymnich <tim@gymni.ch>2025-05-21 23:45:31 +0200
committerGitHub <noreply@github.com>2025-05-21 23:45:31 +0200
commitd00d74bb2564103ae3cb5ac6b6ffecf7e1cc2238 (patch)
treed21fb1c557c8ad633a3ee436572f0933c349dced /llvm/lib/Analysis/ValueTracking.cpp
parentb499f7f2b246c284f8deb428f68d0b488b80c630 (diff)
downloadllvm-d00d74bb2564103ae3cb5ac6b6ffecf7e1cc2238.zip
llvm-d00d74bb2564103ae3cb5ac6b6ffecf7e1cc2238.tar.gz
llvm-d00d74bb2564103ae3cb5ac6b6ffecf7e1cc2238.tar.bz2
[llvm] add GenericFloatingPointPredicateUtils (#140254)
add `GenericFloatingPointPredicateUtils` in order to generalize effects of floating point comparisons on `KnownFPClass` for both IR and MIR. --------- Co-authored-by: Matt Arsenault <arsenm2@gmail.com>
Diffstat (limited to 'llvm/lib/Analysis/ValueTracking.cpp')
-rw-r--r--llvm/lib/Analysis/ValueTracking.cpp424
1 files changed, 2 insertions, 422 deletions
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 8405678..5b9338b 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -15,6 +15,7 @@
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/FloatingPointMode.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/SmallPtrSet.h"
@@ -27,6 +28,7 @@
#include "llvm/Analysis/AssumptionCache.h"
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Analysis/DomConditionCache.h"
+#include "llvm/Analysis/FloatingPointPredicateUtils.h"
#include "llvm/Analysis/GuardUtils.h"
#include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/Loads.h"
@@ -4498,13 +4500,6 @@ Intrinsic::ID llvm::getIntrinsicForCallSite(const CallBase &CB,
return Intrinsic::not_intrinsic;
}
-/// Return true if it's possible to assume IEEE treatment of input denormals in
-/// \p F for \p Val.
-static bool inputDenormalIsIEEE(const Function &F, const Type *Ty) {
- Ty = Ty->getScalarType();
- return F.getDenormalMode(Ty->getFltSemantics()).Input == DenormalMode::IEEE;
-}
-
static bool outputDenormalIsIEEEOrPosZero(const Function &F, const Type *Ty) {
Ty = Ty->getScalarType();
DenormalMode Mode = F.getDenormalMode(Ty->getFltSemantics());
@@ -4550,421 +4545,6 @@ bool llvm::isSignBitCheck(ICmpInst::Predicate Pred, const APInt &RHS,
}
}
-/// Returns a pair of values, which if passed to llvm.is.fpclass, returns the
-/// same result as an fcmp with the given operands.
-std::pair<Value *, FPClassTest> llvm::fcmpToClassTest(FCmpInst::Predicate Pred,
- const Function &F,
- Value *LHS, Value *RHS,
- bool LookThroughSrc) {
- const APFloat *ConstRHS;
- if (!match(RHS, m_APFloatAllowPoison(ConstRHS)))
- return {nullptr, fcAllFlags};
-
- return fcmpToClassTest(Pred, F, LHS, ConstRHS, LookThroughSrc);
-}
-
-std::pair<Value *, FPClassTest>
-llvm::fcmpToClassTest(FCmpInst::Predicate Pred, const Function &F, Value *LHS,
- const APFloat *ConstRHS, bool LookThroughSrc) {
-
- auto [Src, ClassIfTrue, ClassIfFalse] =
- fcmpImpliesClass(Pred, F, LHS, *ConstRHS, LookThroughSrc);
- if (Src && ClassIfTrue == ~ClassIfFalse)
- return {Src, ClassIfTrue};
- return {nullptr, fcAllFlags};
-}
-
-/// Return the return value for fcmpImpliesClass for a compare that produces an
-/// exact class test.
-static std::tuple<Value *, FPClassTest, FPClassTest> exactClass(Value *V,
- FPClassTest M) {
- return {V, M, ~M};
-}
-
-std::tuple<Value *, FPClassTest, FPClassTest>
-llvm::fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
- FPClassTest RHSClass, bool LookThroughSrc) {
- assert(RHSClass != fcNone);
- Value *Src = LHS;
-
- if (Pred == FCmpInst::FCMP_TRUE)
- return exactClass(Src, fcAllFlags);
-
- if (Pred == FCmpInst::FCMP_FALSE)
- return exactClass(Src, fcNone);
-
- const FPClassTest OrigClass = RHSClass;
-
- const bool IsNegativeRHS = (RHSClass & fcNegative) == RHSClass;
- const bool IsPositiveRHS = (RHSClass & fcPositive) == RHSClass;
- const bool IsNaN = (RHSClass & ~fcNan) == fcNone;
-
- if (IsNaN) {
- // fcmp o__ x, nan -> false
- // fcmp u__ x, nan -> true
- return exactClass(Src, CmpInst::isOrdered(Pred) ? fcNone : fcAllFlags);
- }
-
- // fcmp ord x, zero|normal|subnormal|inf -> ~fcNan
- if (Pred == FCmpInst::FCMP_ORD)
- return exactClass(Src, ~fcNan);
-
- // fcmp uno x, zero|normal|subnormal|inf -> fcNan
- if (Pred == FCmpInst::FCMP_UNO)
- return exactClass(Src, fcNan);
-
- const bool IsFabs = LookThroughSrc && match(LHS, m_FAbs(m_Value(Src)));
- if (IsFabs)
- RHSClass = llvm::inverse_fabs(RHSClass);
-
- const bool IsZero = (OrigClass & fcZero) == OrigClass;
- if (IsZero) {
- assert(Pred != FCmpInst::FCMP_ORD && Pred != FCmpInst::FCMP_UNO);
- // Compares with fcNone are only exactly equal to fcZero if input denormals
- // are not flushed.
- // TODO: Handle DAZ by expanding masks to cover subnormal cases.
- if (!inputDenormalIsIEEE(F, LHS->getType()))
- return {nullptr, fcAllFlags, fcAllFlags};
-
- switch (Pred) {
- case FCmpInst::FCMP_OEQ: // Match x == 0.0
- return exactClass(Src, fcZero);
- case FCmpInst::FCMP_UEQ: // Match isnan(x) || (x == 0.0)
- return exactClass(Src, fcZero | fcNan);
- case FCmpInst::FCMP_UNE: // Match (x != 0.0)
- return exactClass(Src, ~fcZero);
- case FCmpInst::FCMP_ONE: // Match !isnan(x) && x != 0.0
- return exactClass(Src, ~fcNan & ~fcZero);
- case FCmpInst::FCMP_ORD:
- // Canonical form of ord/uno is with a zero. We could also handle
- // non-canonical other non-NaN constants or LHS == RHS.
- return exactClass(Src, ~fcNan);
- case FCmpInst::FCMP_UNO:
- return exactClass(Src, fcNan);
- case FCmpInst::FCMP_OGT: // x > 0
- return exactClass(Src, fcPosSubnormal | fcPosNormal | fcPosInf);
- case FCmpInst::FCMP_UGT: // isnan(x) || x > 0
- return exactClass(Src, fcPosSubnormal | fcPosNormal | fcPosInf | fcNan);
- case FCmpInst::FCMP_OGE: // x >= 0
- return exactClass(Src, fcPositive | fcNegZero);
- case FCmpInst::FCMP_UGE: // isnan(x) || x >= 0
- return exactClass(Src, fcPositive | fcNegZero | fcNan);
- case FCmpInst::FCMP_OLT: // x < 0
- return exactClass(Src, fcNegSubnormal | fcNegNormal | fcNegInf);
- case FCmpInst::FCMP_ULT: // isnan(x) || x < 0
- return exactClass(Src, fcNegSubnormal | fcNegNormal | fcNegInf | fcNan);
- case FCmpInst::FCMP_OLE: // x <= 0
- return exactClass(Src, fcNegative | fcPosZero);
- case FCmpInst::FCMP_ULE: // isnan(x) || x <= 0
- return exactClass(Src, fcNegative | fcPosZero | fcNan);
- default:
- llvm_unreachable("all compare types are handled");
- }
-
- return {nullptr, fcAllFlags, fcAllFlags};
- }
-
- const bool IsDenormalRHS = (OrigClass & fcSubnormal) == OrigClass;
-
- const bool IsInf = (OrigClass & fcInf) == OrigClass;
- if (IsInf) {
- FPClassTest Mask = fcAllFlags;
-
- switch (Pred) {
- case FCmpInst::FCMP_OEQ:
- case FCmpInst::FCMP_UNE: {
- // Match __builtin_isinf patterns
- //
- // fcmp oeq x, +inf -> is_fpclass x, fcPosInf
- // fcmp oeq fabs(x), +inf -> is_fpclass x, fcInf
- // fcmp oeq x, -inf -> is_fpclass x, fcNegInf
- // fcmp oeq fabs(x), -inf -> is_fpclass x, 0 -> false
- //
- // fcmp une x, +inf -> is_fpclass x, ~fcPosInf
- // fcmp une fabs(x), +inf -> is_fpclass x, ~fcInf
- // fcmp une x, -inf -> is_fpclass x, ~fcNegInf
- // fcmp une fabs(x), -inf -> is_fpclass x, fcAllFlags -> true
- if (IsNegativeRHS) {
- Mask = fcNegInf;
- if (IsFabs)
- Mask = fcNone;
- } else {
- Mask = fcPosInf;
- if (IsFabs)
- Mask |= fcNegInf;
- }
- break;
- }
- case FCmpInst::FCMP_ONE:
- case FCmpInst::FCMP_UEQ: {
- // Match __builtin_isinf patterns
- // fcmp one x, -inf -> is_fpclass x, fcNegInf
- // fcmp one fabs(x), -inf -> is_fpclass x, ~fcNegInf & ~fcNan
- // fcmp one x, +inf -> is_fpclass x, ~fcNegInf & ~fcNan
- // fcmp one fabs(x), +inf -> is_fpclass x, ~fcInf & fcNan
- //
- // fcmp ueq x, +inf -> is_fpclass x, fcPosInf|fcNan
- // fcmp ueq (fabs x), +inf -> is_fpclass x, fcInf|fcNan
- // fcmp ueq x, -inf -> is_fpclass x, fcNegInf|fcNan
- // fcmp ueq fabs(x), -inf -> is_fpclass x, fcNan
- if (IsNegativeRHS) {
- Mask = ~fcNegInf & ~fcNan;
- if (IsFabs)
- Mask = ~fcNan;
- } else {
- Mask = ~fcPosInf & ~fcNan;
- if (IsFabs)
- Mask &= ~fcNegInf;
- }
-
- break;
- }
- case FCmpInst::FCMP_OLT:
- case FCmpInst::FCMP_UGE: {
- if (IsNegativeRHS) {
- // No value is ordered and less than negative infinity.
- // All values are unordered with or at least negative infinity.
- // fcmp olt x, -inf -> false
- // fcmp uge x, -inf -> true
- Mask = fcNone;
- break;
- }
-
- // fcmp olt fabs(x), +inf -> fcFinite
- // fcmp uge fabs(x), +inf -> ~fcFinite
- // fcmp olt x, +inf -> fcFinite|fcNegInf
- // fcmp uge x, +inf -> ~(fcFinite|fcNegInf)
- Mask = fcFinite;
- if (!IsFabs)
- Mask |= fcNegInf;
- break;
- }
- case FCmpInst::FCMP_OGE:
- case FCmpInst::FCMP_ULT: {
- if (IsNegativeRHS) {
- // fcmp oge x, -inf -> ~fcNan
- // fcmp oge fabs(x), -inf -> ~fcNan
- // fcmp ult x, -inf -> fcNan
- // fcmp ult fabs(x), -inf -> fcNan
- Mask = ~fcNan;
- break;
- }
-
- // fcmp oge fabs(x), +inf -> fcInf
- // fcmp oge x, +inf -> fcPosInf
- // fcmp ult fabs(x), +inf -> ~fcInf
- // fcmp ult x, +inf -> ~fcPosInf
- Mask = fcPosInf;
- if (IsFabs)
- Mask |= fcNegInf;
- break;
- }
- case FCmpInst::FCMP_OGT:
- case FCmpInst::FCMP_ULE: {
- if (IsNegativeRHS) {
- // fcmp ogt x, -inf -> fcmp one x, -inf
- // fcmp ogt fabs(x), -inf -> fcmp ord x, x
- // fcmp ule x, -inf -> fcmp ueq x, -inf
- // fcmp ule fabs(x), -inf -> fcmp uno x, x
- Mask = IsFabs ? ~fcNan : ~(fcNegInf | fcNan);
- break;
- }
-
- // No value is ordered and greater than infinity.
- Mask = fcNone;
- break;
- }
- case FCmpInst::FCMP_OLE:
- case FCmpInst::FCMP_UGT: {
- if (IsNegativeRHS) {
- Mask = IsFabs ? fcNone : fcNegInf;
- break;
- }
-
- // fcmp ole x, +inf -> fcmp ord x, x
- // fcmp ole fabs(x), +inf -> fcmp ord x, x
- // fcmp ole x, -inf -> fcmp oeq x, -inf
- // fcmp ole fabs(x), -inf -> false
- Mask = ~fcNan;
- break;
- }
- default:
- llvm_unreachable("all compare types are handled");
- }
-
- // Invert the comparison for the unordered cases.
- if (FCmpInst::isUnordered(Pred))
- Mask = ~Mask;
-
- return exactClass(Src, Mask);
- }
-
- if (Pred == FCmpInst::FCMP_OEQ)
- return {Src, RHSClass, fcAllFlags};
-
- if (Pred == FCmpInst::FCMP_UEQ) {
- FPClassTest Class = RHSClass | fcNan;
- return {Src, Class, ~fcNan};
- }
-
- if (Pred == FCmpInst::FCMP_ONE)
- return {Src, ~fcNan, RHSClass | fcNan};
-
- if (Pred == FCmpInst::FCMP_UNE)
- return {Src, fcAllFlags, RHSClass};
-
- assert((RHSClass == fcNone || RHSClass == fcPosNormal ||
- RHSClass == fcNegNormal || RHSClass == fcNormal ||
- RHSClass == fcPosSubnormal || RHSClass == fcNegSubnormal ||
- RHSClass == fcSubnormal) &&
- "should have been recognized as an exact class test");
-
- if (IsNegativeRHS) {
- // TODO: Handle fneg(fabs)
- if (IsFabs) {
- // fabs(x) o> -k -> fcmp ord x, x
- // fabs(x) u> -k -> true
- // fabs(x) o< -k -> false
- // fabs(x) u< -k -> fcmp uno x, x
- switch (Pred) {
- case FCmpInst::FCMP_OGT:
- case FCmpInst::FCMP_OGE:
- return {Src, ~fcNan, fcNan};
- case FCmpInst::FCMP_UGT:
- case FCmpInst::FCMP_UGE:
- return {Src, fcAllFlags, fcNone};
- case FCmpInst::FCMP_OLT:
- case FCmpInst::FCMP_OLE:
- return {Src, fcNone, fcAllFlags};
- case FCmpInst::FCMP_ULT:
- case FCmpInst::FCMP_ULE:
- return {Src, fcNan, ~fcNan};
- default:
- break;
- }
-
- return {nullptr, fcAllFlags, fcAllFlags};
- }
-
- FPClassTest ClassesLE = fcNegInf | fcNegNormal;
- FPClassTest ClassesGE = fcPositive | fcNegZero | fcNegSubnormal;
-
- if (IsDenormalRHS)
- ClassesLE |= fcNegSubnormal;
- else
- ClassesGE |= fcNegNormal;
-
- switch (Pred) {
- case FCmpInst::FCMP_OGT:
- case FCmpInst::FCMP_OGE:
- return {Src, ClassesGE, ~ClassesGE | RHSClass};
- case FCmpInst::FCMP_UGT:
- case FCmpInst::FCMP_UGE:
- return {Src, ClassesGE | fcNan, ~(ClassesGE | fcNan) | RHSClass};
- case FCmpInst::FCMP_OLT:
- case FCmpInst::FCMP_OLE:
- return {Src, ClassesLE, ~ClassesLE | RHSClass};
- case FCmpInst::FCMP_ULT:
- case FCmpInst::FCMP_ULE:
- return {Src, ClassesLE | fcNan, ~(ClassesLE | fcNan) | RHSClass};
- default:
- break;
- }
- } else if (IsPositiveRHS) {
- FPClassTest ClassesGE = fcPosNormal | fcPosInf;
- FPClassTest ClassesLE = fcNegative | fcPosZero | fcPosSubnormal;
- if (IsDenormalRHS)
- ClassesGE |= fcPosSubnormal;
- else
- ClassesLE |= fcPosNormal;
-
- if (IsFabs) {
- ClassesGE = llvm::inverse_fabs(ClassesGE);
- ClassesLE = llvm::inverse_fabs(ClassesLE);
- }
-
- switch (Pred) {
- case FCmpInst::FCMP_OGT:
- case FCmpInst::FCMP_OGE:
- return {Src, ClassesGE, ~ClassesGE | RHSClass};
- case FCmpInst::FCMP_UGT:
- case FCmpInst::FCMP_UGE:
- return {Src, ClassesGE | fcNan, ~(ClassesGE | fcNan) | RHSClass};
- case FCmpInst::FCMP_OLT:
- case FCmpInst::FCMP_OLE:
- return {Src, ClassesLE, ~ClassesLE | RHSClass};
- case FCmpInst::FCMP_ULT:
- case FCmpInst::FCMP_ULE:
- return {Src, ClassesLE | fcNan, ~(ClassesLE | fcNan) | RHSClass};
- default:
- break;
- }
- }
-
- return {nullptr, fcAllFlags, fcAllFlags};
-}
-
-std::tuple<Value *, FPClassTest, FPClassTest>
-llvm::fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
- const APFloat &ConstRHS, bool LookThroughSrc) {
- // We can refine checks against smallest normal / largest denormal to an
- // exact class test.
- if (!ConstRHS.isNegative() && ConstRHS.isSmallestNormalized()) {
- Value *Src = LHS;
- const bool IsFabs = LookThroughSrc && match(LHS, m_FAbs(m_Value(Src)));
-
- FPClassTest Mask;
- // Match pattern that's used in __builtin_isnormal.
- switch (Pred) {
- case FCmpInst::FCMP_OLT:
- case FCmpInst::FCMP_UGE: {
- // fcmp olt x, smallest_normal -> fcNegInf|fcNegNormal|fcSubnormal|fcZero
- // fcmp olt fabs(x), smallest_normal -> fcSubnormal|fcZero
- // fcmp uge x, smallest_normal -> fcNan|fcPosNormal|fcPosInf
- // fcmp uge fabs(x), smallest_normal -> ~(fcSubnormal|fcZero)
- Mask = fcZero | fcSubnormal;
- if (!IsFabs)
- Mask |= fcNegNormal | fcNegInf;
-
- break;
- }
- case FCmpInst::FCMP_OGE:
- case FCmpInst::FCMP_ULT: {
- // fcmp oge x, smallest_normal -> fcPosNormal | fcPosInf
- // fcmp oge fabs(x), smallest_normal -> fcInf | fcNormal
- // fcmp ult x, smallest_normal -> ~(fcPosNormal | fcPosInf)
- // fcmp ult fabs(x), smallest_normal -> ~(fcInf | fcNormal)
- Mask = fcPosInf | fcPosNormal;
- if (IsFabs)
- Mask |= fcNegInf | fcNegNormal;
- break;
- }
- default:
- return fcmpImpliesClass(Pred, F, LHS, ConstRHS.classify(),
- LookThroughSrc);
- }
-
- // Invert the comparison for the unordered cases.
- if (FCmpInst::isUnordered(Pred))
- Mask = ~Mask;
-
- return exactClass(Src, Mask);
- }
-
- return fcmpImpliesClass(Pred, F, LHS, ConstRHS.classify(), LookThroughSrc);
-}
-
-std::tuple<Value *, FPClassTest, FPClassTest>
-llvm::fcmpImpliesClass(CmpInst::Predicate Pred, const Function &F, Value *LHS,
- Value *RHS, bool LookThroughSrc) {
- const APFloat *ConstRHS;
- if (!match(RHS, m_APFloatAllowPoison(ConstRHS)))
- return {nullptr, fcAllFlags, fcAllFlags};
-
- // TODO: Just call computeKnownFPClass for RHS to handle non-constants.
- return fcmpImpliesClass(Pred, F, LHS, *ConstRHS, LookThroughSrc);
-}
-
static void computeKnownFPClassFromCond(const Value *V, Value *Cond,
unsigned Depth, bool CondIsTrue,
const Instruction *CxtI,