[llvm] [LVI][SCCP] Add basic ConstantFPRange support (PR #111544)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Tue Oct 8 08:18:54 PDT 2024


https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/111544

Note: Some nan-simplification tests will be broken if we convert fp constant into a range as we did for constant integers.

>From 553fdd7a60c0fc4cf379f284bc226ea427489cbd Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 8 Oct 2024 22:43:59 +0800
Subject: [PATCH] [LVI][SCCP] Add basic ConstantFPRange support

---
 llvm/include/llvm/Analysis/ValueLattice.h | 215 ++++++++++++++++++++--
 llvm/include/llvm/IR/Constant.h           |   6 +
 llvm/lib/Analysis/LazyValueInfo.cpp       |  22 +++
 llvm/lib/Analysis/ValueLattice.cpp        |  66 +++++--
 llvm/lib/IR/Constants.cpp                 |  39 ++++
 llvm/lib/Transforms/Utils/SCCPSolver.cpp  |   9 +-
 6 files changed, 316 insertions(+), 41 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ValueLattice.h b/llvm/include/llvm/Analysis/ValueLattice.h
index 9357a15f7619f1..3c5cfa7390815b 100644
--- a/llvm/include/llvm/Analysis/ValueLattice.h
+++ b/llvm/include/llvm/Analysis/ValueLattice.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_ANALYSIS_VALUELATTICE_H
 #define LLVM_ANALYSIS_VALUELATTICE_H
 
+#include "llvm/IR/ConstantFPRange.h"
 #include "llvm/IR/ConstantRange.h"
 #include "llvm/IR/Constants.h"
 
@@ -38,6 +39,7 @@ class ValueLatticeElement {
     /// Transition allowed to the following states:
     ///  constant
     ///  constantrange_including_undef
+    ///  constantfprange_including_undef
     ///  overdefined
     undef,
 
@@ -70,6 +72,21 @@ class ValueLatticeElement {
     ///  overdefined
     constantrange_including_undef,
 
+    /// The Value falls within this range. (Used only for floating point typed
+    /// values.)
+    /// Transition allowed to the following states:
+    ///  constantfprange (new range must be a superset of the existing range)
+    ///  constantfprange_including_undef
+    ///  overdefined
+    constantfprange,
+
+    /// This Value falls within this range, but also may be undef.
+    /// Merging it with other constant ranges results in
+    /// constantfprange_including_undef.
+    /// Transition allowed to the following states:
+    ///  overdefined
+    constantfprange_including_undef,
+
     /// We can not precisely model the dynamic values this value might take.
     /// No transitions are allowed after reaching overdefined.
     overdefined,
@@ -85,6 +102,7 @@ class ValueLatticeElement {
   union {
     Constant *ConstVal;
     ConstantRange Range;
+    ConstantFPRange FPRange;
   };
 
   /// Destroy contents of lattice value, without destructing the object.
@@ -100,6 +118,10 @@ class ValueLatticeElement {
     case constantrange:
       Range.~ConstantRange();
       break;
+    case constantfprange_including_undef:
+    case constantfprange:
+      FPRange.~ConstantFPRange();
+      break;
     };
   }
 
@@ -154,6 +176,11 @@ class ValueLatticeElement {
       new (&Range) ConstantRange(Other.Range);
       NumRangeExtensions = Other.NumRangeExtensions;
       break;
+    case constantfprange:
+    case constantfprange_including_undef:
+      new (&FPRange) ConstantFPRange(Other.FPRange);
+      NumRangeExtensions = Other.NumRangeExtensions;
+      break;
     case constant:
     case notconstant:
       ConstVal = Other.ConstVal;
@@ -173,6 +200,11 @@ class ValueLatticeElement {
       new (&Range) ConstantRange(std::move(Other.Range));
       NumRangeExtensions = Other.NumRangeExtensions;
       break;
+    case constantfprange:
+    case constantfprange_including_undef:
+      new (&FPRange) ConstantFPRange(Other.FPRange);
+      NumRangeExtensions = Other.NumRangeExtensions;
+      break;
     case constant:
     case notconstant:
       ConstVal = Other.ConstVal;
@@ -225,6 +257,23 @@ class ValueLatticeElement {
                           MergeOptions().setMayIncludeUndef(MayIncludeUndef));
     return Res;
   }
+  static ValueLatticeElement getFPRange(ConstantFPRange CR,
+                                        bool MayIncludeUndef = false) {
+    if (CR.isFullSet())
+      return getOverdefined();
+
+    if (CR.isEmptySet()) {
+      ValueLatticeElement Res;
+      if (MayIncludeUndef)
+        Res.markUndef();
+      return Res;
+    }
+
+    ValueLatticeElement Res;
+    Res.markConstantFPRange(std::move(CR),
+                            MergeOptions().setMayIncludeUndef(MayIncludeUndef));
+    return Res;
+  }
   static ValueLatticeElement getOverdefined() {
     ValueLatticeElement Res;
     Res.markOverdefined();
@@ -239,6 +288,9 @@ class ValueLatticeElement {
   bool isConstantRangeIncludingUndef() const {
     return Tag == constantrange_including_undef;
   }
+  bool isConstantFPRangeIncludingUndef() const {
+    return Tag == constantfprange_including_undef;
+  }
   /// Returns true if this value is a constant range. Use \p UndefAllowed to
   /// exclude non-singleton constant ranges that may also be undef. Note that
   /// this function also returns true if the range may include undef, but only
@@ -247,6 +299,16 @@ class ValueLatticeElement {
     return Tag == constantrange || (Tag == constantrange_including_undef &&
                                     (UndefAllowed || Range.isSingleElement()));
   }
+  /// Returns true if this value is a constant floating point range. Use \p
+  /// UndefAllowed to exclude non-singleton constant ranges that may also be
+  /// undef. Note that this function also returns true if the range may include
+  /// undef, but only contains a single element. In that case, it can be
+  /// replaced by a constant.
+  bool isConstantFPRange(bool UndefAllowed = true) const {
+    return Tag == constantfprange ||
+           (Tag == constantfprange_including_undef &&
+            (UndefAllowed || FPRange.isSingleElement()));
+  }
   bool isOverdefined() const { return Tag == overdefined; }
 
   Constant *getConstant() const {
@@ -269,6 +331,17 @@ class ValueLatticeElement {
     return Range;
   }
 
+  /// Returns the constant floating point range for this value. Use \p
+  /// UndefAllowed to exclude non-singleton constant ranges that may also be
+  /// undef. Note that this function also returns a range if the range may
+  /// include undef, but only contains a single element. In that case, it can be
+  /// replaced by a constant.
+  const ConstantFPRange &getConstantFPRange(bool UndefAllowed = true) const {
+    assert(isConstantFPRange(UndefAllowed) &&
+           "Cannot get the constant-fprange of a non-constant-fprange!");
+    return FPRange;
+  }
+
   std::optional<APInt> asConstantInteger() const {
     if (isConstant() && isa<ConstantInt>(getConstant())) {
       return cast<ConstantInt>(getConstant())->getValue();
@@ -278,6 +351,15 @@ class ValueLatticeElement {
     return std::nullopt;
   }
 
+  std::optional<APFloat> asConstantFP() const {
+    if (isConstant() && isa<ConstantFP>(getConstant())) {
+      return cast<ConstantFP>(getConstant())->getValue();
+    } else if (isConstantFPRange() && getConstantFPRange().isSingleElement()) {
+      return *getConstantFPRange().getSingleElement();
+    }
+    return std::nullopt;
+  }
+
   ConstantRange asConstantRange(unsigned BW, bool UndefAllowed = false) const {
     if (isConstantRange(UndefAllowed))
       return getConstantRange();
@@ -288,11 +370,28 @@ class ValueLatticeElement {
     return ConstantRange::getFull(BW);
   }
 
+  ConstantFPRange asConstantFPRange(const fltSemantics &Sem,
+                                    bool UndefAllowed = false) const {
+    if (isConstantFPRange(UndefAllowed))
+      return getConstantFPRange();
+    if (isConstant())
+      return getConstant()->toConstantFPRange();
+    if (isUnknown())
+      return ConstantFPRange::getEmpty(Sem);
+    return ConstantFPRange::getFull(Sem);
+  }
+
   ConstantRange asConstantRange(Type *Ty, bool UndefAllowed = false) const {
     assert(Ty->isIntOrIntVectorTy() && "Must be integer type");
     return asConstantRange(Ty->getScalarSizeInBits(), UndefAllowed);
   }
 
+  ConstantFPRange asConstantFPRange(Type *Ty, bool UndefAllowed = false) const {
+    assert(Ty->isFPOrFPVectorTy() && "Must be floating point type");
+    return asConstantFPRange(Ty->getScalarType()->getFltSemantics(),
+                             UndefAllowed);
+  }
+
   bool markOverdefined() {
     if (isOverdefined())
       return false;
@@ -394,6 +493,51 @@ class ValueLatticeElement {
     return true;
   }
 
+  /// Mark the object as constant floating point range with \p NewR. If the
+  /// object is already a constant range, nothing changes if the existing range
+  /// is equal to \p NewR and the tag. Otherwise \p NewR must be a superset of
+  /// the existing range or the object must be undef. The tag is set to
+  /// constant_range_including_undef if either the existing value or the new
+  /// range may include undef.
+  bool markConstantFPRange(ConstantFPRange NewR,
+                           MergeOptions Opts = MergeOptions()) {
+    assert(!NewR.isEmptySet() && "should only be called for non-empty sets");
+
+    if (NewR.isFullSet())
+      return markOverdefined();
+
+    ValueLatticeElementTy OldTag = Tag;
+    ValueLatticeElementTy NewTag =
+        (isUndef() || isConstantFPRangeIncludingUndef() || Opts.MayIncludeUndef)
+            ? constantfprange_including_undef
+            : constantfprange;
+    if (isConstantFPRange()) {
+      Tag = NewTag;
+      if (getConstantFPRange() == NewR)
+        return Tag != OldTag;
+
+      // Simple form of widening. If a range is extended multiple times, go to
+      // overdefined.
+      if (Opts.CheckWiden && ++NumRangeExtensions > Opts.MaxWidenSteps)
+        return markOverdefined();
+
+      assert(NewR.contains(getConstantFPRange()) &&
+             "Existing range must be a subset of NewR");
+      FPRange = std::move(NewR);
+      return true;
+    }
+
+    assert(isUnknown() || isUndef() || isConstant());
+    assert(
+        (!isConstant() || NewR.contains(getConstant()->toConstantFPRange())) &&
+        "Constant must be subset of new range");
+
+    NumRangeExtensions = 0;
+    Tag = NewTag;
+    new (&FPRange) ConstantFPRange(std::move(NewR));
+    return true;
+  }
+
   /// Updates this object to approximate both this object and RHS. Returns
   /// true if this object has been changed.
   bool mergeIn(const ValueLatticeElement &RHS,
@@ -414,6 +558,9 @@ class ValueLatticeElement {
       if (RHS.isConstantRange())
         return markConstantRange(RHS.getConstantRange(true),
                                  Opts.setMayIncludeUndef());
+      if (RHS.isConstantFPRange())
+        return markConstantFPRange(RHS.getConstantFPRange(true),
+                                   Opts.setMayIncludeUndef());
       return markOverdefined();
     }
 
@@ -428,15 +575,26 @@ class ValueLatticeElement {
         return false;
       if (RHS.isUndef())
         return false;
-      // If the constant is a vector of integers, try to treat it as a range.
-      if (getConstant()->getType()->isVectorTy() &&
-          getConstant()->getType()->getScalarType()->isIntegerTy()) {
-        ConstantRange L = getConstant()->toConstantRange();
-        ConstantRange NewR = L.unionWith(
-            RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
-        return markConstantRange(
-            std::move(NewR),
-            Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
+      // If the constant is a vector of integers/floating point values, try to
+      // treat it as a range.
+      if (getConstant()->getType()->isVectorTy()) {
+        Type *ScalarTy = getConstant()->getType()->getScalarType();
+        if (ScalarTy->isIntegerTy()) {
+          ConstantRange L = getConstant()->toConstantRange();
+          ConstantRange NewR = L.unionWith(
+              RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
+          return markConstantRange(
+              std::move(NewR),
+              Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
+        }
+        if (ScalarTy->isFloatingPointTy()) {
+          ConstantFPRange L = getConstant()->toConstantFPRange();
+          ConstantFPRange NewR = L.unionWith(
+              RHS.asConstantFPRange(L.getSemantics(), /*UndefAllowed=*/true));
+          return markConstantFPRange(
+              std::move(NewR),
+              Opts.setMayIncludeUndef(RHS.isConstantFPRangeIncludingUndef()));
+        }
       }
       markOverdefined();
       return true;
@@ -450,18 +608,35 @@ class ValueLatticeElement {
     }
 
     auto OldTag = Tag;
-    assert(isConstantRange() && "New ValueLattice type?");
-    if (RHS.isUndef()) {
-      Tag = constantrange_including_undef;
-      return OldTag != Tag;
+    if (isConstantRange()) {
+      if (RHS.isUndef()) {
+        Tag = constantrange_including_undef;
+        return OldTag != Tag;
+      }
+
+      const ConstantRange &L = getConstantRange();
+      ConstantRange NewR = L.unionWith(
+          RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
+      return markConstantRange(
+          std::move(NewR),
+          Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
     }
 
-    const ConstantRange &L = getConstantRange();
-    ConstantRange NewR = L.unionWith(
-        RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true));
-    return markConstantRange(
-        std::move(NewR),
-        Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef()));
+    if (isConstantFPRange()) {
+      if (RHS.isUndef()) {
+        Tag = constantfprange_including_undef;
+        return OldTag != Tag;
+      }
+
+      const ConstantFPRange &L = getConstantFPRange();
+      ConstantFPRange NewR = L.unionWith(
+          RHS.asConstantFPRange(L.getSemantics(), /*UndefAllowed=*/true));
+      return markConstantFPRange(
+          std::move(NewR),
+          Opts.setMayIncludeUndef(RHS.isConstantFPRangeIncludingUndef()));
+    } else {
+      llvm_unreachable("New ValueLattice type?");
+    }
   }
 
   // Compares this symbolic value with Other using Pred and returns either
@@ -492,7 +667,7 @@ class ValueLatticeElement {
   void setNumRangeExtensions(unsigned N) { NumRangeExtensions = N; }
 };
 
-static_assert(sizeof(ValueLatticeElement) <= 40,
+static_assert(sizeof(ValueLatticeElement) <= 80,
               "size of ValueLatticeElement changed unexpectedly");
 
 raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val);
diff --git a/llvm/include/llvm/IR/Constant.h b/llvm/include/llvm/IR/Constant.h
index 0aefb5ecf6b7f2..d6bde764d36d77 100644
--- a/llvm/include/llvm/IR/Constant.h
+++ b/llvm/include/llvm/IR/Constant.h
@@ -20,6 +20,7 @@
 namespace llvm {
 
 class ConstantRange;
+class ConstantFPRange;
 class APInt;
 
 /// This is an important base class in LLVM. It provides the common facilities
@@ -159,6 +160,11 @@ class Constant : public User {
   /// range is the union over the element ranges. Poison elements are ignored.
   ConstantRange toConstantRange() const;
 
+  /// Convert constant to an approximate constant floating point range. For
+  /// vectors, the range is the union over the element ranges. Poison elements
+  /// are ignored.
+  ConstantFPRange toConstantFPRange() const;
+
   /// Called if some element of this constant is no longer valid.
   /// At this point only other constants may be on the use_list for this
   /// constant.  Any constants on our Use list must also be destroy'd.  The
diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp
index 30dc4ae30dbfa5..73ff6825ba7066 100644
--- a/llvm/lib/Analysis/LazyValueInfo.cpp
+++ b/llvm/lib/Analysis/LazyValueInfo.cpp
@@ -77,6 +77,9 @@ static bool hasSingleValue(const ValueLatticeElement &Val) {
       Val.getConstantRange().isSingleElement())
     // Integer constants are single element ranges
     return true;
+  if (Val.isConstantFPRange() && Val.getConstantFPRange().isSingleElement())
+    // Floating point constants are single element ranges
+    return true;
   if (Val.isConstant())
     // Non integer constants
     return true;
@@ -1685,6 +1688,11 @@ Constant *LazyValueInfo::getConstant(Value *V, Instruction *CxtI) {
     if (const APInt *SingleVal = CR.getSingleElement())
       return ConstantInt::get(V->getType(), *SingleVal);
   }
+  if (Result.isConstantFPRange()) {
+    const ConstantFPRange &CR = Result.getConstantFPRange();
+    if (const APFloat *SingleVal = CR.getSingleElement())
+      return ConstantFP::get(V->getType(), *SingleVal);
+  }
   return nullptr;
 }
 
@@ -1720,6 +1728,11 @@ Constant *LazyValueInfo::getConstantOnEdge(Value *V, BasicBlock *FromBB,
     if (const APInt *SingleVal = CR.getSingleElement())
       return ConstantInt::get(V->getType(), *SingleVal);
   }
+  if (Result.isConstantFPRange()) {
+    const ConstantFPRange &CR = Result.getConstantFPRange();
+    if (const APFloat *SingleVal = CR.getSingleElement())
+      return ConstantFP::get(V->getType(), *SingleVal);
+  }
   return nullptr;
 }
 
@@ -1751,6 +1764,15 @@ static Constant *getPredicateResult(CmpInst::Predicate Pred, Constant *C,
       return ConstantInt::getFalse(ResTy);
     return nullptr;
   }
+  if (Val.isConstantFPRange()) {
+    const ConstantFPRange &CR = Val.getConstantFPRange();
+    ConstantFPRange RHS = C->toConstantFPRange();
+    if (CR.fcmp(Pred, RHS))
+      return ConstantInt::getTrue(ResTy);
+    if (CR.fcmp(CmpInst::getInversePredicate(Pred), RHS))
+      return ConstantInt::getFalse(ResTy);
+    return nullptr;
+  }
 
   if (Val.isNotConstant()) {
     // If this is an equality comparison, we can try to fold it knowing that
diff --git a/llvm/lib/Analysis/ValueLattice.cpp b/llvm/lib/Analysis/ValueLattice.cpp
index 03810f1c554e5d..ba4b947f923c1c 100644
--- a/llvm/lib/Analysis/ValueLattice.cpp
+++ b/llvm/lib/Analysis/ValueLattice.cpp
@@ -40,15 +40,21 @@ ValueLatticeElement::getCompare(CmpInst::Predicate Pred, Type *Ty,
 
   // Integer constants are represented as ConstantRanges with single
   // elements.
-  if (!isConstantRange() || !Other.isConstantRange())
-    return nullptr;
-
-  const auto &CR = getConstantRange();
-  const auto &OtherCR = Other.getConstantRange();
-  if (CR.icmp(Pred, OtherCR))
-    return ConstantInt::getTrue(Ty);
-  if (CR.icmp(CmpInst::getInversePredicate(Pred), OtherCR))
-    return ConstantInt::getFalse(Ty);
+  if (isConstantRange() && Other.isConstantRange()) {
+    const auto &CR = getConstantRange();
+    const auto &OtherCR = Other.getConstantRange();
+    if (CR.icmp(Pred, OtherCR))
+      return ConstantInt::getTrue(Ty);
+    if (CR.icmp(CmpInst::getInversePredicate(Pred), OtherCR))
+      return ConstantInt::getFalse(Ty);
+  } else if (isConstantFPRange() && Other.isConstantFPRange()) {
+    const auto &CR = getConstantFPRange();
+    const auto &OtherCR = Other.getConstantFPRange();
+    if (CR.fcmp(Pred, OtherCR))
+      return ConstantInt::getTrue(Ty);
+    if (CR.fcmp(CmpInst::getInversePredicate(Pred), OtherCR))
+      return ConstantInt::getFalse(Ty);
+  }
 
   return nullptr;
 }
@@ -57,6 +63,9 @@ static bool hasSingleValue(const ValueLatticeElement &Val) {
   if (Val.isConstantRange() && Val.getConstantRange().isSingleElement())
     // Integer constants are single element ranges
     return true;
+  if (Val.isConstantFPRange() && Val.getConstantFPRange().isSingleElement())
+    // Floating point constants are single element ranges
+    return true;
   return Val.isConstant();
 }
 
@@ -94,19 +103,30 @@ ValueLatticeElement::intersect(const ValueLatticeElement &Other) const {
     return Other;
 
   // Could be either constant range or not constant here.
-  if (!isConstantRange() || !Other.isConstantRange()) {
-    // TODO: Arbitrary choice, could be improved
-    return *this;
+  if (isConstantRange() && Other.isConstantRange()) {
+    // Intersect two constant ranges
+    ConstantRange Range =
+        getConstantRange().intersectWith(Other.getConstantRange());
+    // Note: An empty range is implicitly converted to unknown or undef
+    // depending on MayIncludeUndef internally.
+    return ValueLatticeElement::getRange(
+        std::move(Range), /*MayIncludeUndef=*/isConstantRangeIncludingUndef() ||
+                              Other.isConstantRangeIncludingUndef());
+  }
+  if (isConstantFPRange() && Other.isConstantFPRange()) {
+    // Intersect two constant ranges
+    ConstantFPRange Range =
+        getConstantFPRange().intersectWith(Other.getConstantFPRange());
+    // Note: An empty range is implicitly converted to unknown or undef
+    // depending on MayIncludeUndef internally.
+    return ValueLatticeElement::getFPRange(
+        std::move(Range),
+        /*MayIncludeUndef=*/isConstantFPRangeIncludingUndef() ||
+            Other.isConstantFPRangeIncludingUndef());
   }
 
-  // Intersect two constant ranges
-  ConstantRange Range =
-      getConstantRange().intersectWith(Other.getConstantRange());
-  // Note: An empty range is implicitly converted to unknown or undef depending
-  // on MayIncludeUndef internally.
-  return ValueLatticeElement::getRange(
-      std::move(Range), /*MayIncludeUndef=*/isConstantRangeIncludingUndef() ||
-                            Other.isConstantRangeIncludingUndef());
+  // TODO: Arbitrary choice, could be improved
+  return *this;
 }
 
 raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val) {
@@ -125,9 +145,15 @@ raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val) {
               << Val.getConstantRange(true).getLower() << ", "
               << Val.getConstantRange(true).getUpper() << ">";
 
+  if (Val.isConstantFPRangeIncludingUndef())
+    return OS << "constantfprange incl. undef " << Val.getConstantFPRange(true);
+
   if (Val.isConstantRange())
     return OS << "constantrange<" << Val.getConstantRange().getLower() << ", "
               << Val.getConstantRange().getUpper() << ">";
+  if (Val.isConstantFPRange())
+    return OS << "constantfprange " << Val.getConstantFPRange(true);
+
   return OS << "constant<" << *Val.getConstant() << ">";
 }
 } // end namespace llvm
diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp
index fe3a086c5772de..76c73ffede6e81 100644
--- a/llvm/lib/IR/Constants.cpp
+++ b/llvm/lib/IR/Constants.cpp
@@ -16,6 +16,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/ConstantFPRange.h"
 #include "llvm/IR/ConstantFold.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/Function.h"
@@ -1796,6 +1797,44 @@ ConstantRange Constant::toConstantRange() const {
   return ConstantRange::getFull(BitWidth);
 }
 
+ConstantFPRange Constant::toConstantFPRange() const {
+  if (auto *CFP = dyn_cast<ConstantFP>(this))
+    return ConstantFPRange(CFP->getValue());
+
+  const fltSemantics &Sem = getType()->getScalarType()->getFltSemantics();
+  if (!getType()->isVectorTy())
+    return ConstantFPRange::getFull(Sem);
+
+  if (auto *CFP =
+          dyn_cast_or_null<ConstantFP>(getSplatValue(/*AllowPoison=*/true)))
+    return ConstantFPRange(CFP->getValue());
+
+  if (auto *CDV = dyn_cast<ConstantDataVector>(this)) {
+    ConstantFPRange CR = ConstantFPRange::getEmpty(Sem);
+    for (unsigned I = 0, E = CDV->getNumElements(); I < E; ++I)
+      CR = CR.unionWith(ConstantFPRange(CDV->getElementAsAPFloat(I)));
+    return CR;
+  }
+
+  if (auto *CV = dyn_cast<ConstantVector>(this)) {
+    ConstantFPRange CR = ConstantFPRange::getEmpty(Sem);
+    for (unsigned I = 0, E = CV->getNumOperands(); I < E; ++I) {
+      Constant *Elem = CV->getOperand(I);
+      if (!Elem)
+        return ConstantFPRange::getFull(Sem);
+      if (isa<PoisonValue>(Elem))
+        continue;
+      auto *CFP = dyn_cast<ConstantFP>(Elem);
+      if (!CFP)
+        return ConstantFPRange::getFull(Sem);
+      CR = CR.unionWith(ConstantFPRange(CFP->getValue()));
+    }
+    return CR;
+  }
+
+  return ConstantFPRange::getFull(Sem);
+}
+
 //---- ConstantPointerNull::get() implementation.
 //
 
diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index 101d60525f4161..31d2ca1fc2a4b6 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -47,7 +47,8 @@ namespace llvm {
 
 bool SCCPSolver::isConstant(const ValueLatticeElement &LV) {
   return LV.isConstant() ||
-         (LV.isConstantRange() && LV.getConstantRange().isSingleElement());
+         (LV.isConstantRange() && LV.getConstantRange().isSingleElement()) ||
+         (LV.isConstantFPRange() && LV.getConstantFPRange().isSingleElement());
 }
 
 bool SCCPSolver::isOverdefined(const ValueLatticeElement &LV) {
@@ -1006,6 +1007,12 @@ Constant *SCCPInstVisitor::getConstant(const ValueLatticeElement &LV,
     if (CR.getSingleElement())
       return ConstantInt::get(Ty, *CR.getSingleElement());
   }
+
+  if (LV.isConstantFPRange()) {
+    const auto &CR = LV.getConstantFPRange();
+    if (CR.getSingleElement())
+      return ConstantFP::get(Ty, *CR.getSingleElement());
+  }
   return nullptr;
 }
 



More information about the llvm-commits mailing list