[llvm] 8a37512 - ValueTracking: Extract fcmpToClassTest out of InstCombine
Matt Arsenault via llvm-commits
llvm-commits at lists.llvm.org
Thu Mar 16 20:14:50 PDT 2023
Author: Matt Arsenault
Date: 2023-03-16T23:14:40-04:00
New Revision: 8a37512924de3bdc82183c53099fde3e0575f233
URL: https://github.com/llvm/llvm-project/commit/8a37512924de3bdc82183c53099fde3e0575f233
DIFF: https://github.com/llvm/llvm-project/commit/8a37512924de3bdc82183c53099fde3e0575f233.diff
LOG: ValueTracking: Extract fcmpToClassTest out of InstCombine
Also update unsigned to FPClassTest
Added:
Modified:
llvm/include/llvm/Analysis/ValueTracking.h
llvm/lib/Analysis/ValueTracking.cpp
llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
Removed:
################################################################################
diff --git a/llvm/include/llvm/Analysis/ValueTracking.h b/llvm/include/llvm/Analysis/ValueTracking.h
index c056bb907fa6b..26fb1051978bd 100644
--- a/llvm/include/llvm/Analysis/ValueTracking.h
+++ b/llvm/include/llvm/Analysis/ValueTracking.h
@@ -217,6 +217,12 @@ unsigned ComputeMaxSignificantBits(const Value *Op, const DataLayout &DL,
Intrinsic::ID getIntrinsicForCallSite(const CallBase &CB,
const TargetLibraryInfo *TLI);
+/// 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> fcmpToClassTest(CmpInst::Predicate Pred,
+ const Function &F,
+ Value *LHS, Value *RHS);
+
struct KnownFPClass {
/// Floating-point classes the value could be one of.
FPClassTest KnownFPClasses = fcAllFlags;
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index affb709685917..cfa9b1b0fc6b7 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -4125,6 +4125,178 @@ bool llvm::isKnownNeverNaN(const Value *V, const TargetLibraryInfo *TLI,
return false;
}
+/// 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 Value *Val) {
+ Type *Ty = Val->getType()->getScalarType();
+ return F.getDenormalMode(Ty->getFltSemantics()).Input == DenormalMode::IEEE;
+}
+
+/// 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) {
+ const APFloat *ConstRHS;
+ if (!match(RHS, m_APFloat(ConstRHS)))
+ return {nullptr, fcNone};
+
+ if (ConstRHS->isZero()) {
+ // Compares with fcNone are only exactly equal to fcZero if input denormals are
+ // not flushed.
+ if (FCmpInst::isEquality(Pred) && !inputDenormalIsIEEE(F, LHS))
+ return {nullptr, fcNone};
+
+ switch (Pred) {
+ case FCmpInst::FCMP_OEQ: // Match x == 0.0
+ return {LHS, fcZero};
+ case FCmpInst::FCMP_UEQ: // Match isnan(x) || (x == 0.0)
+ return {LHS, fcZero | fcNan};
+ case FCmpInst::FCMP_UNE: // Match (x != 0.0)
+ return {LHS, ~fcZero};
+ case FCmpInst::FCMP_ONE: // Match !isnan(x) && x != 0.0
+ return {LHS, ~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 {LHS, ~fcNan};
+ case FCmpInst::FCMP_UNO:
+ return {LHS, fcNan};
+ default:
+ break;
+ }
+
+ return {nullptr, fcNone};
+ }
+
+ Value *Src = LHS;
+ const bool IsFabs = match(LHS, m_FAbs(m_Value(Src)));
+
+ // Compute the test mask that would return true for the ordered comparisons.
+ FPClassTest Mask;
+
+ if (ConstRHS->isInfinity()) {
+ 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 (ConstRHS->isNegative()) {
+ 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 (ConstRHS->isNegative()) {
+ 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 (ConstRHS->isNegative()) // TODO
+ return {nullptr, fcNone};
+
+ // 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 (ConstRHS->isNegative()) // TODO
+ return {nullptr, fcNone};
+
+ // 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;
+ }
+ default:
+ return {nullptr, fcNone};
+ }
+ } else if (ConstRHS->isSmallestNormalized() && !ConstRHS->isNegative()) {
+ // 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 {nullptr, fcNone};
+ }
+ } else
+ return {nullptr, fcNone};
+
+ // Invert the comparison for the unordered cases.
+ if (FCmpInst::isUnordered(Pred))
+ Mask = ~Mask;
+
+ return {Src, Mask};
+}
+
// TODO: Merge implementations of isKnownNeverNaN, isKnownNeverInfinity,
// CannotBeNegativeZero, cannotBeOrderedLessThanZero into here.
void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index 43177257d709d..b9cc104f13fda 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -1273,181 +1273,6 @@ static Value *matchIsFiniteTest(InstCombiner::BuilderTy &Builder, FCmpInst *LHS,
return Builder.CreateFCmp(FCmpInst::getOrderedPredicate(PredR), RHS0, RHS1);
}
-/// 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 Value *Val) {
- Type *Ty = Val->getType()->getScalarType();
- return F.getDenormalMode(Ty->getFltSemantics()).Input == DenormalMode::IEEE;
-}
-
-/// Returns a pair of values, which if passed to llvm.is.fpclass, returns the
-/// same result as an fcmp with the given operands.
-static std::pair<Value *, unsigned> fcmpToClassTest(FCmpInst::Predicate Pred,
- const Function &F,
- Value *LHS, Value *RHS) {
- const APFloat *ConstRHS;
- if (!match(RHS, m_APFloat(ConstRHS)))
- return {nullptr, 0};
-
- if (ConstRHS->isZero()) {
- // Compares with 0 are only exactly equal to fcZero if input denormals are
- // not flushed.
- if (FCmpInst::isEquality(Pred) && !inputDenormalIsIEEE(F, LHS))
- return {nullptr, 0};
-
- switch (Pred) {
- case FCmpInst::FCMP_OEQ: // Match x == 0.0
- return {LHS, fcZero};
- case FCmpInst::FCMP_UEQ: // Match isnan(x) || (x == 0.0)
- return {LHS, fcZero | fcNan};
- case FCmpInst::FCMP_UNE: // Match (x != 0.0)
- return {LHS, ~fcZero & fcAllFlags};
- case FCmpInst::FCMP_ONE: // Match !isnan(x) && x != 0.0
- return {LHS, ~fcNan & ~fcZero & fcAllFlags};
- 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 {LHS, ~fcNan & fcAllFlags};
- case FCmpInst::FCMP_UNO:
- return {LHS, fcNan};
- default:
- break;
- }
-
- return {nullptr, 0};
- }
-
- Value *Src = LHS;
- const bool IsFabs = match(LHS, m_FAbs(m_Value(Src)));
-
- // Compute the test mask that would return true for the ordered comparisons.
- unsigned Mask;
-
- if (ConstRHS->isInfinity()) {
- 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 (ConstRHS->isNegative()) {
- Mask = fcNegInf;
- if (IsFabs)
- Mask = 0;
- } 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 (ConstRHS->isNegative()) {
- Mask = ~fcNegInf & ~fcNan;
- if (IsFabs)
- Mask = ~fcNan;
- } else {
- Mask = ~fcPosInf & ~fcNan;
- if (IsFabs)
- Mask &= ~fcNegInf;
- }
-
- Mask &= fcAllFlags;
- break;
- }
- case FCmpInst::FCMP_OLT:
- case FCmpInst::FCMP_UGE: {
- if (ConstRHS->isNegative()) // TODO
- return {nullptr, 0};
-
- // 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 (ConstRHS->isNegative()) // TODO
- return {nullptr, 0};
-
- // 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;
- }
- default:
- return {nullptr, 0};
- }
- } else if (ConstRHS->isSmallestNormalized() && !ConstRHS->isNegative()) {
- // 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 {nullptr, 0};
- }
- } else
- return {nullptr, 0};
-
- // Invert the comparison for the unordered cases.
- if (FCmpInst::isUnordered(Pred))
- Mask = ~Mask & fcAllFlags;
-
- assert((Mask & ~fcAllFlags) == 0);
-
- return {Src, Mask};
-}
-
Value *InstCombinerImpl::foldLogicOfFCmps(FCmpInst *LHS, FCmpInst *RHS,
bool IsAnd, bool IsLogicalSelect) {
Value *LHS0 = LHS->getOperand(0), *LHS1 = LHS->getOperand(1);
More information about the llvm-commits
mailing list