[llvm] [LLVM][IR] Add constant range support for floating-point types (PR #86483)

Yingwei Zheng via llvm-commits llvm-commits at lists.llvm.org
Wed Sep 18 05:29:56 PDT 2024


https://github.com/dtcxzyw updated https://github.com/llvm/llvm-project/pull/86483

>From 2e76f7ce192bfbe1467f32a4d6a3567fa90c7cc0 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Tue, 27 Feb 2024 01:47:23 +0800
Subject: [PATCH 1/9] tmp

---
 llvm/include/llvm/IR/ConstantFPRange.h | 139 +++++++++++++++++++++++++
 1 file changed, 139 insertions(+)
 create mode 100644 llvm/include/llvm/IR/ConstantFPRange.h

diff --git a/llvm/include/llvm/IR/ConstantFPRange.h b/llvm/include/llvm/IR/ConstantFPRange.h
new file mode 100644
index 00000000000000..89b0cd0b7763b5
--- /dev/null
+++ b/llvm/include/llvm/IR/ConstantFPRange.h
@@ -0,0 +1,139 @@
+//===- ConstantFPRange.h - Represent a range for floating-point -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Represent a range of possible values that may occur when the program is run
+// for a floating-point value. This keeps track of a lower and upper bound for
+// the constant.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_IR_CONSTANTFPRANGE_H
+#define LLVM_IR_CONSTANTFPRANGE_H
+
+#include "llvm/ADT/APFloat.h"
+#include "llvm/IR/InstrTypes.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/Support/Compiler.h"
+#include <optional>
+
+namespace llvm {
+
+class raw_ostream;
+struct KnownFPClass;
+
+/// This class represents a range of floating-point values.
+class [[nodiscard]] ConstantFPRange {
+  APFloat Lower, Upper;
+  bool MaybeQNaN : 1;
+  bool MaybeSNaN : 1;
+  bool SignBitMaybeZero : 1;
+  bool SignBitMaybeOne : 1;
+
+  /// Create empty constant range with same semantics.
+  ConstantFPRange getEmpty() const {
+    return ConstantFPRange(getSemantics(), /*IsFullSet=*/false);
+  }
+
+  /// Create full constant range with same semantics.
+  ConstantFPRange getFull() const {
+    return ConstantFPRange(getSemantics(), /*IsFullSet=*/true);
+  }
+
+public:
+  /// Initialize a full or empty set for the specified semantics.
+  explicit ConstantFPRange(const fltSemantics &FloatSema, bool IsFullSet);
+
+  /// Initialize a range to hold the single specified value.
+  ConstantFPRange(const APFloat &Value);
+
+  /// Initialize a range of values explicitly.
+  ConstantFPRange(APFloat Lower, APFloat Upper, bool MaybeQNaN, bool MaybeSNaN,
+                  bool SignBitMaybeZero, bool SignBitMaybeOne);
+
+  /// Create empty constant range with the given semantics.
+  static ConstantFPRange getEmpty(const fltSemantics &FloatSema) {
+    return ConstantFPRange(FloatSema, /*IsFullSet=*/false);
+  }
+
+  /// Create full constant range with the given semantics.
+  static ConstantFPRange getFull(const fltSemantics &FloatSema) {
+    return ConstantFPRange(FloatSema, /*IsFullSet=*/true);
+  }
+
+  /// Initialize a range based on a known floating-point classes constraint.
+  static ConstantFPRange fromKnownFPClass(const KnownFPClass &Known);
+
+  /// Produce the exact range such that all values in the returned range satisfy
+  /// the given predicate with any value contained within Other. Formally, this
+  /// returns the exact answer when the superset of 'union over all y in Other
+  /// is exactly same as the subset of intersection over all y in Other.
+  /// { x : fcmp op x y is true}'.
+  ///
+  /// Example: Pred = olt and Other = float 3 returns [-inf, 3)
+  static ConstantFPRange makeExactFCmpRegion(FCmpInst::Predicate Pred,
+                                             const APFloat &Other);
+
+  /// Does the predicate \p Pred hold between ranges this and \p Other?
+  /// NOTE: false does not mean that inverse predicate holds!
+  bool fcmp(FCmpInst::Predicate Pred, const ConstantFPRange &Other) const;
+
+  /// Return the lower value for this range.
+  const APFloat &getLower() const { return Lower; }
+
+  /// Return the upper value for this range.
+  const APFloat &getUpper() const { return Upper; }
+
+  /// Get the semantics of this ConstantFPRange.
+  const fltSemantics &getSemantics() const { return Lower.getSemantics(); }
+
+  /// Return true if this set contains all of the elements possible
+  /// for this data-type.
+  bool isFullSet() const;
+
+  /// Return true if this set contains no members.
+  bool isEmptySet() const;
+
+  /// Return true if the specified value is in the set.
+  bool contains(const APFloat &Val) const;
+
+  /// Return true if the other range is a subset of this one.
+  bool contains(const ConstantFPRange &CR) const;
+
+  /// If this set contains a single element, return it, otherwise return null.
+  const APFloat *getSingleElement() const;
+
+  /// Return true if this set contains exactly one member.
+  bool isSingleElement() const { return getSingleElement() != nullptr; }
+
+  /// Return true if the sign bit of all values in this range is 1.
+  /// Return false if the sign bit of all values in this range is 0.
+  /// Otherwise, return std::nullopt.
+  std::optional<bool> getSignBit();
+
+  /// Return true if this range is equal to another range.
+  bool operator==(const ConstantFPRange &CR) const;
+  bool operator!=(const ConstantFPRange &CR) const { return !operator==(CR); }
+
+  /// Return known floating-point classes for values in this range.
+  KnownFPClass toKnownFPClass();
+
+  /// Print out the bounds to a stream.
+  void print(raw_ostream &OS) const;
+
+  /// Allow printing from a debugger easily.
+  void dump() const;
+};
+
+inline raw_ostream &operator<<(raw_ostream &OS, const ConstantFPRange &CR) {
+  CR.print(OS);
+  return OS;
+}
+
+} // end namespace llvm
+
+#endif // LLVM_IR_CONSTANTFPRANGE_H

>From 1afb5243727b41081470097b3841031a00f6d508 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Fri, 21 Jun 2024 22:05:04 +0800
Subject: [PATCH 2/9] add basic implementation

---
 llvm/include/llvm/IR/ConstantFPRange.h |  79 ++++++--
 llvm/lib/IR/CMakeLists.txt             |   1 +
 llvm/lib/IR/ConstantFPRange.cpp        | 253 +++++++++++++++++++++++++
 3 files changed, 315 insertions(+), 18 deletions(-)
 create mode 100644 llvm/lib/IR/ConstantFPRange.cpp

diff --git a/llvm/include/llvm/IR/ConstantFPRange.h b/llvm/include/llvm/IR/ConstantFPRange.h
index 89b0cd0b7763b5..3cfc6f362f2b8a 100644
--- a/llvm/include/llvm/IR/ConstantFPRange.h
+++ b/llvm/include/llvm/IR/ConstantFPRange.h
@@ -10,15 +10,18 @@
 // for a floating-point value. This keeps track of a lower and upper bound for
 // the constant.
 //
+// Range = [Lower, Upper] U (MayBeQNaN ? QNaN : {}) U (MayBeSNaN ? SNaN : {})
+// Specifically, [inf, -inf] represents an empty set.
+// Note: -0 is considered to be less than 0. That is, range [0, 0] doesn't
+// contain -0.
+//
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_IR_CONSTANTFPRANGE_H
 #define LLVM_IR_CONSTANTFPRANGE_H
 
 #include "llvm/ADT/APFloat.h"
-#include "llvm/IR/InstrTypes.h"
-#include "llvm/IR/Instruction.h"
-#include "llvm/Support/Compiler.h"
+#include "llvm/IR/Instructions.h"
 #include <optional>
 
 namespace llvm {
@@ -29,10 +32,8 @@ struct KnownFPClass;
 /// This class represents a range of floating-point values.
 class [[nodiscard]] ConstantFPRange {
   APFloat Lower, Upper;
-  bool MaybeQNaN : 1;
-  bool MaybeSNaN : 1;
-  bool SignBitMaybeZero : 1;
-  bool SignBitMaybeOne : 1;
+  bool MayBeQNaN : 1;
+  bool MayBeSNaN : 1;
 
   /// Create empty constant range with same semantics.
   ConstantFPRange getEmpty() const {
@@ -44,29 +45,55 @@ class [[nodiscard]] ConstantFPRange {
     return ConstantFPRange(getSemantics(), /*IsFullSet=*/true);
   }
 
+  void makeEmpty();
+  void makeFull();
+  bool isNaNOnly() const;
+
 public:
+  /// Return true if the floating point format is supported.
+  static bool isSupportedSemantics(const fltSemantics &Sem);
+
   /// Initialize a full or empty set for the specified semantics.
-  explicit ConstantFPRange(const fltSemantics &FloatSema, bool IsFullSet);
+  explicit ConstantFPRange(const fltSemantics &Sem, bool IsFullSet);
 
   /// Initialize a range to hold the single specified value.
   ConstantFPRange(const APFloat &Value);
 
   /// Initialize a range of values explicitly.
-  ConstantFPRange(APFloat Lower, APFloat Upper, bool MaybeQNaN, bool MaybeSNaN,
-                  bool SignBitMaybeZero, bool SignBitMaybeOne);
+  ConstantFPRange(APFloat LowerVal, APFloat UpperVal, bool MayBeQNaN = true,
+                  bool MaybeSNaN = true);
 
   /// Create empty constant range with the given semantics.
-  static ConstantFPRange getEmpty(const fltSemantics &FloatSema) {
-    return ConstantFPRange(FloatSema, /*IsFullSet=*/false);
+  static ConstantFPRange getEmpty(const fltSemantics &Sem) {
+    return ConstantFPRange(Sem, /*IsFullSet=*/false);
   }
 
   /// Create full constant range with the given semantics.
-  static ConstantFPRange getFull(const fltSemantics &FloatSema) {
-    return ConstantFPRange(FloatSema, /*IsFullSet=*/true);
+  static ConstantFPRange getFull(const fltSemantics &Sem) {
+    return ConstantFPRange(Sem, /*IsFullSet=*/true);
   }
 
-  /// Initialize a range based on a known floating-point classes constraint.
-  static ConstantFPRange fromKnownFPClass(const KnownFPClass &Known);
+  /// Produce the smallest range such that all values that may satisfy the given
+  /// predicate with any value contained within Other is contained in the
+  /// returned range.  Formally, this returns a superset of
+  /// 'union over all y in Other . { x : fcmp op x y is true }'.  If the exact
+  /// answer is not representable as a ConstantRange, the return value will be a
+  /// proper superset of the above.
+  ///
+  /// Example: Pred = ole and Other = float [2, 5] returns Result = [-inf, 5]
+  static ConstantFPRange makeAllowedFCmpRegion(FCmpInst::Predicate Pred,
+                                               const ConstantFPRange &Other);
+
+  /// Produce the largest range such that all values in the returned range
+  /// satisfy the given predicate with all values contained within Other.
+  /// Formally, this returns a subset of
+  /// 'intersection over all y in Other . { x : fcmp op x y is true }'.  If the
+  /// exact answer is not representable as a ConstantRange, the return value
+  /// will be a proper subset of the above.
+  ///
+  /// Example: Pred = ole and Other = float [2, 5] returns [-inf, 2]
+  static ConstantFPRange makeSatisfyingFCmpRegion(FCmpInst::Predicate Pred,
+                                                  const ConstantFPRange &Other);
 
   /// Produce the exact range such that all values in the returned range satisfy
   /// the given predicate with any value contained within Other. Formally, this
@@ -113,20 +140,36 @@ class [[nodiscard]] ConstantFPRange {
   /// Return true if the sign bit of all values in this range is 1.
   /// Return false if the sign bit of all values in this range is 0.
   /// Otherwise, return std::nullopt.
-  std::optional<bool> getSignBit();
+  std::optional<bool> getSignBit() const;
 
   /// Return true if this range is equal to another range.
   bool operator==(const ConstantFPRange &CR) const;
+  /// Return true if this range is not equal to another range.
   bool operator!=(const ConstantFPRange &CR) const { return !operator==(CR); }
 
+  /// Return the FPClassTest which will return true for the value.
+  FPClassTest classify() const;
+
   /// Return known floating-point classes for values in this range.
-  KnownFPClass toKnownFPClass();
+  KnownFPClass toKnownFPClass() const;
 
   /// Print out the bounds to a stream.
   void print(raw_ostream &OS) const;
 
   /// Allow printing from a debugger easily.
   void dump() const;
+
+  /// Return the range that results from the intersection of this range with
+  /// another range.
+  ConstantFPRange intersectWith(const ConstantFPRange &CR) const;
+
+  /// Return the range that results from the union of this range
+  /// with another range.  The resultant range is guaranteed to include the
+  /// elements of both sets, but may contain more.
+  ConstantFPRange unionWith(const ConstantFPRange &CR) const;
+
+  /// Return a new range that is the logical not of the current set.
+  ConstantFPRange inverse() const;
 };
 
 inline raw_ostream &operator<<(raw_ostream &OS, const ConstantFPRange &CR) {
diff --git a/llvm/lib/IR/CMakeLists.txt b/llvm/lib/IR/CMakeLists.txt
index 91e0e0cc65f36b..e5756940dd5a03 100644
--- a/llvm/lib/IR/CMakeLists.txt
+++ b/llvm/lib/IR/CMakeLists.txt
@@ -8,6 +8,7 @@ add_llvm_component_library(LLVMCore
   BuiltinGCs.cpp
   Comdat.cpp
   ConstantFold.cpp
+  ConstantFPRange.cpp
   ConstantRange.cpp
   ConstantRangeList.cpp
   Constants.cpp
diff --git a/llvm/lib/IR/ConstantFPRange.cpp b/llvm/lib/IR/ConstantFPRange.cpp
new file mode 100644
index 00000000000000..1576da4fc0b3ef
--- /dev/null
+++ b/llvm/lib/IR/ConstantFPRange.cpp
@@ -0,0 +1,253 @@
+//===- ConstantFPRange.cpp - ConstantFPRange implementation ---------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Represent a range of possible values that may occur when the program is run
+// for a floating-point value. This keeps track of a lower and upper bound for
+// the constant.
+//
+// Range = [Lower, Upper] U (MayBeQNaN ? QNaN : {}) U (MayBeSNaN ? SNaN : {})
+// Specifically, [inf, -inf] represents an empty set.
+// Note: -0 is considered to be less than 0. That is, range [0, 0] doesn't
+// contain -0.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/ConstantFPRange.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/Analysis/ValueTracking.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+
+using namespace llvm;
+
+// A floating point format must supports NaN, Inf and -0.
+bool ConstantFPRange::isSupportedSemantics(const fltSemantics &Sem) {
+  switch (APFloat::SemanticsToEnum(Sem)) {
+  default:
+    return false;
+  case APFloat::S_IEEEhalf:
+  case APFloat::S_BFloat:
+  case APFloat::S_IEEEsingle:
+  case APFloat::S_IEEEdouble:
+  case APFloat::S_IEEEquad:
+    return true;
+  }
+}
+
+void ConstantFPRange::makeEmpty() {
+  auto &Sem = Lower.getSemantics();
+  Lower = APFloat::getInf(Sem, /*Negative=*/false);
+  Upper = APFloat::getInf(Sem, /*Negative=*/true);
+  MayBeQNaN = false;
+  MayBeSNaN = false;
+}
+
+void ConstantFPRange::makeFull() {
+  auto &Sem = Lower.getSemantics();
+  Lower = APFloat::getInf(Sem, /*Negative=*/true);
+  Upper = APFloat::getInf(Sem, /*Negative=*/false);
+  MayBeQNaN = true;
+  MayBeSNaN = true;
+}
+
+bool ConstantFPRange::isNaNOnly() const {
+  return Lower.isPosInfinity() && Upper.isNegInfinity();
+}
+
+ConstantFPRange::ConstantFPRange(const fltSemantics &Sem, bool IsFullSet)
+    : Lower(Sem, APFloat::uninitialized), Upper(Sem, APFloat::uninitialized) {
+  assert(isSupportedSemantics(Sem) && "Unsupported fp format");
+  Lower = APFloat::getInf(Sem, /*Negative=*/IsFullSet);
+  Upper = APFloat::getInf(Sem, /*Negative=*/!IsFullSet);
+  MayBeQNaN = IsFullSet;
+  MayBeSNaN = IsFullSet;
+}
+
+ConstantFPRange::ConstantFPRange(const APFloat &Value)
+    : Lower(Value.getSemantics(), APFloat::uninitialized),
+      Upper(Value.getSemantics(), APFloat::uninitialized) {
+  assert(isSupportedSemantics(getSemantics()) && "Unsupported fp format");
+
+  if (Value.isNaN()) {
+    makeEmpty();
+    bool isSNaN = Value.isSignaling();
+    MayBeQNaN = !isSNaN;
+    MayBeSNaN = isSNaN;
+  } else {
+    Lower = Upper = Value;
+    MayBeQNaN = MayBeSNaN = false;
+  }
+}
+
+// We treat that -0 is less than 0 here.
+static APFloat::cmpResult strictCompare(const APFloat &LHS,
+                                        const APFloat &RHS) {
+  assert(!LHS.isNaN() && !RHS.isNaN() && "Unordered compare");
+  if (LHS.isZero() && RHS.isZero()) {
+    if (LHS.isNegative() == RHS.isNegative())
+      return APFloat::cmpEqual;
+    return LHS.isNegative() ? APFloat::cmpLessThan : APFloat::cmpGreaterThan;
+  }
+  return LHS.compare(RHS);
+}
+
+ConstantFPRange::ConstantFPRange(APFloat LowerVal, APFloat UpperVal,
+                                 bool MayBeQNaN, bool MaybeSNaN)
+    : Lower(std::move(LowerVal)), Upper(std::move(UpperVal)) {
+  assert(isSupportedSemantics(getSemantics()) && "Unsupported fp format");
+
+  // Canonicalize empty set into [Inf, -Inf].
+  if (strictCompare(Lower, Upper) == APFloat::cmpGreaterThan &&
+      !(Lower.isInfinity() && Upper.isInfinity()))
+    makeEmpty();
+  this->MayBeQNaN = MayBeQNaN;
+  this->MayBeSNaN = MayBeSNaN;
+}
+
+ConstantFPRange
+ConstantFPRange::makeAllowedFCmpRegion(FCmpInst::Predicate Pred,
+                                       const ConstantFPRange &Other) {
+  // TODO
+  return getFull(Other.getSemantics());
+}
+
+ConstantFPRange
+ConstantFPRange::makeSatisfyingFCmpRegion(FCmpInst::Predicate Pred,
+                                          const ConstantFPRange &Other) {
+  // TODO
+  return getEmpty(Other.getSemantics());
+}
+
+ConstantFPRange ConstantFPRange::makeExactFCmpRegion(FCmpInst::Predicate Pred,
+                                                     const APFloat &Other) {
+  return makeAllowedFCmpRegion(Pred, Other);
+}
+
+bool ConstantFPRange::fcmp(FCmpInst::Predicate Pred,
+                           const ConstantFPRange &Other) const {
+  return makeSatisfyingFCmpRegion(Pred, Other).contains(*this);
+}
+
+bool ConstantFPRange::isFullSet() const {
+  return Lower.isNegInfinity() && Upper.isPosInfinity() && MayBeQNaN &&
+         MayBeSNaN;
+}
+
+bool ConstantFPRange::isEmptySet() const {
+  return Lower.isPosInfinity() && Upper.isNegInfinity() && !MayBeQNaN &&
+         !MayBeSNaN;
+}
+
+bool ConstantFPRange::contains(const APFloat &Val) const {
+  assert(&getSemantics() == &Val.getSemantics() &&
+         "Should only use the same semantics");
+
+  if (Val.isNaN())
+    return Val.isSignaling() ? MayBeSNaN : MayBeQNaN;
+  return strictCompare(Lower, Val) != APFloat::cmpGreaterThan &&
+         strictCompare(Val, Upper) != APFloat::cmpGreaterThan;
+}
+
+bool ConstantFPRange::contains(const ConstantFPRange &CR) const {
+  assert(&getSemantics() == &CR.getSemantics() &&
+         "Should only use the same semantics");
+
+  if (CR.MayBeQNaN && !MayBeQNaN)
+    return false;
+
+  if (CR.MayBeSNaN && !MayBeSNaN)
+    return false;
+
+  return strictCompare(Lower, CR.Lower) != APFloat::cmpGreaterThan &&
+         strictCompare(CR.Upper, Upper) != APFloat::cmpGreaterThan;
+}
+
+const APFloat *ConstantFPRange::getSingleElement() const {
+  if (MayBeSNaN || MayBeQNaN)
+    return nullptr;
+  return Lower.bitwiseIsEqual(Upper) ? &Lower : nullptr;
+}
+
+std::optional<bool> ConstantFPRange::getSignBit() const {
+  if (!MayBeSNaN && !MayBeQNaN && Lower.isNegative() == Upper.isNegative())
+    return Lower.isNegative();
+  return std::nullopt;
+}
+
+bool ConstantFPRange::operator==(const ConstantFPRange &CR) const {
+  if (MayBeSNaN != CR.MayBeSNaN || MayBeQNaN != CR.MayBeQNaN)
+    return false;
+  return Lower.bitwiseIsEqual(CR.Lower) && Upper.bitwiseIsEqual(CR.Upper);
+}
+
+FPClassTest ConstantFPRange::classify() const {
+  uint32_t Mask = fcNone;
+  if (MayBeSNaN)
+    Mask |= fcSNan;
+  if (MayBeQNaN)
+    Mask |= fcQNan;
+  if (!isNaNOnly()) {
+    FPClassTest LowerMask = Lower.classify();
+    FPClassTest UpperMask = Upper.classify();
+    assert(LowerMask <= UpperMask && "Range is nan-only.");
+    for (uint32_t I = LowerMask; I <= UpperMask; I <<= 1)
+      Mask |= I;
+  }
+  return static_cast<FPClassTest>(Mask);
+}
+
+KnownFPClass ConstantFPRange::toKnownFPClass() const {
+  KnownFPClass Result;
+  Result.KnownFPClasses = classify();
+  Result.SignBit = getSignBit();
+  return Result;
+}
+
+void ConstantFPRange::print(raw_ostream &OS) const {
+  if (isFullSet())
+    OS << "full-set";
+  else if (isEmptySet())
+    OS << "empty-set";
+  else {
+    bool NaNOnly = isNaNOnly();
+    if (!NaNOnly) {
+      OS << '[';
+      Lower.print(OS);
+      OS << ", ";
+      Upper.print(OS);
+      OS << ']';
+    }
+
+    if (MayBeSNaN || MayBeQNaN) {
+      if (!NaNOnly)
+        OS << " with ";
+      if (MayBeSNaN && MayBeQNaN)
+        OS << "NaN";
+      else if (MayBeSNaN)
+        OS << "SNaN";
+      else if (MayBeQNaN)
+        OS << "QNaN";
+    }
+  }
+}
+
+#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
+LLVM_DUMP_METHOD void ConstantFPRange::dump() const { print(dbgs()); }
+#endif
+
+ConstantFPRange
+ConstantFPRange::intersectWith(const ConstantFPRange &CR) const {
+  return ConstantFPRange(maxnum(Lower, CR.Lower), minnum(Upper, CR.Upper),
+                         MayBeQNaN & CR.MayBeQNaN, MayBeSNaN & CR.MayBeSNaN);
+}
+
+ConstantFPRange ConstantFPRange::unionWith(const ConstantFPRange &CR) const {
+  return ConstantFPRange(minnum(Lower, CR.Lower), maxnum(Upper, CR.Upper),
+                         MayBeQNaN | CR.MayBeQNaN, MayBeSNaN | CR.MayBeSNaN);
+}

>From b4eb25f1a43f874ea9e82ccf2e297ba841f4fc99 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sat, 22 Jun 2024 14:21:31 +0800
Subject: [PATCH 3/9] [LLVM][IR] Remove comments. NFC.

---
 llvm/lib/IR/ConstantFPRange.cpp | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/llvm/lib/IR/ConstantFPRange.cpp b/llvm/lib/IR/ConstantFPRange.cpp
index 1576da4fc0b3ef..10462968fbea9e 100644
--- a/llvm/lib/IR/ConstantFPRange.cpp
+++ b/llvm/lib/IR/ConstantFPRange.cpp
@@ -5,17 +5,6 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //===----------------------------------------------------------------------===//
-//
-// Represent a range of possible values that may occur when the program is run
-// for a floating-point value. This keeps track of a lower and upper bound for
-// the constant.
-//
-// Range = [Lower, Upper] U (MayBeQNaN ? QNaN : {}) U (MayBeSNaN ? SNaN : {})
-// Specifically, [inf, -inf] represents an empty set.
-// Note: -0 is considered to be less than 0. That is, range [0, 0] doesn't
-// contain -0.
-//
-//===----------------------------------------------------------------------===//
 
 #include "llvm/IR/ConstantFPRange.h"
 #include "llvm/ADT/APFloat.h"

>From e2bdba25729e5fb50f76489477016342c086fc5e Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 15 Sep 2024 13:46:44 +0800
Subject: [PATCH 4/9] [ConstantFPRange] Add basic unit tests.

---
 llvm/include/llvm/IR/ConstantFPRange.h    |  14 +-
 llvm/lib/IR/ConstantFPRange.cpp           |  26 +-
 llvm/unittests/IR/CMakeLists.txt          |   1 +
 llvm/unittests/IR/ConstantFPRangeTest.cpp | 284 ++++++++++++++++++++++
 4 files changed, 312 insertions(+), 13 deletions(-)
 create mode 100644 llvm/unittests/IR/ConstantFPRangeTest.cpp

diff --git a/llvm/include/llvm/IR/ConstantFPRange.h b/llvm/include/llvm/IR/ConstantFPRange.h
index 3cfc6f362f2b8a..986f0a8ced4ced 100644
--- a/llvm/include/llvm/IR/ConstantFPRange.h
+++ b/llvm/include/llvm/IR/ConstantFPRange.h
@@ -61,7 +61,7 @@ class [[nodiscard]] ConstantFPRange {
 
   /// Initialize a range of values explicitly.
   ConstantFPRange(APFloat LowerVal, APFloat UpperVal, bool MayBeQNaN = true,
-                  bool MaybeSNaN = true);
+                  bool MayBeSNaN = true);
 
   /// Create empty constant range with the given semantics.
   static ConstantFPRange getEmpty(const fltSemantics &Sem) {
@@ -73,6 +73,15 @@ class [[nodiscard]] ConstantFPRange {
     return ConstantFPRange(Sem, /*IsFullSet=*/true);
   }
 
+  /// Helper for (-inf, inf) to represent all finite values.
+  static ConstantFPRange getFinite(const fltSemantics &Sem);
+
+  /// Create a range which doesn't contain NaNs.
+  static ConstantFPRange getNonNaN(APFloat LowerVal, APFloat UpperVal) {
+    return ConstantFPRange(std::move(LowerVal), std::move(UpperVal),
+                           /*MayBeQNaN=*/false, /*MayBeSNaN=*/false);
+  }
+
   /// Produce the smallest range such that all values that may satisfy the given
   /// predicate with any value contained within Other is contained in the
   /// returned range.  Formally, this returns a superset of
@@ -167,9 +176,6 @@ class [[nodiscard]] ConstantFPRange {
   /// with another range.  The resultant range is guaranteed to include the
   /// elements of both sets, but may contain more.
   ConstantFPRange unionWith(const ConstantFPRange &CR) const;
-
-  /// Return a new range that is the logical not of the current set.
-  ConstantFPRange inverse() const;
 };
 
 inline raw_ostream &operator<<(raw_ostream &OS, const ConstantFPRange &CR) {
diff --git a/llvm/lib/IR/ConstantFPRange.cpp b/llvm/lib/IR/ConstantFPRange.cpp
index 10462968fbea9e..093c1cf38febea 100644
--- a/llvm/lib/IR/ConstantFPRange.cpp
+++ b/llvm/lib/IR/ConstantFPRange.cpp
@@ -15,7 +15,7 @@
 
 using namespace llvm;
 
-// A floating point format must supports NaN, Inf and -0.
+// A floating point format must support NaN, Inf and -0.
 bool ConstantFPRange::isSupportedSemantics(const fltSemantics &Sem) {
   switch (APFloat::SemanticsToEnum(Sem)) {
   default:
@@ -65,9 +65,9 @@ ConstantFPRange::ConstantFPRange(const APFloat &Value)
 
   if (Value.isNaN()) {
     makeEmpty();
-    bool isSNaN = Value.isSignaling();
-    MayBeQNaN = !isSNaN;
-    MayBeSNaN = isSNaN;
+    bool IsSNaN = Value.isSignaling();
+    MayBeQNaN = !IsSNaN;
+    MayBeSNaN = IsSNaN;
   } else {
     Lower = Upper = Value;
     MayBeQNaN = MayBeSNaN = false;
@@ -87,7 +87,7 @@ static APFloat::cmpResult strictCompare(const APFloat &LHS,
 }
 
 ConstantFPRange::ConstantFPRange(APFloat LowerVal, APFloat UpperVal,
-                                 bool MayBeQNaN, bool MaybeSNaN)
+                                 bool MayBeQNaN, bool MayBeSNaN)
     : Lower(std::move(LowerVal)), Upper(std::move(UpperVal)) {
   assert(isSupportedSemantics(getSemantics()) && "Unsupported fp format");
 
@@ -99,6 +99,12 @@ ConstantFPRange::ConstantFPRange(APFloat LowerVal, APFloat UpperVal,
   this->MayBeSNaN = MayBeSNaN;
 }
 
+ConstantFPRange ConstantFPRange::getFinite(const fltSemantics &Sem) {
+  return ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/true),
+                         APFloat::getLargest(Sem, /*Negative=*/false),
+                         /*MayBeQNaN=*/false, /*MayBeSNaN=*/false);
+}
+
 ConstantFPRange
 ConstantFPRange::makeAllowedFCmpRegion(FCmpInst::Predicate Pred,
                                        const ConstantFPRange &Other) {
@@ -207,10 +213,12 @@ void ConstantFPRange::print(raw_ostream &OS) const {
     bool NaNOnly = isNaNOnly();
     if (!NaNOnly) {
       OS << '[';
-      Lower.print(OS);
-      OS << ", ";
-      Upper.print(OS);
-      OS << ']';
+      SmallVector<char, 16> Buffer;
+      Lower.toString(Buffer);
+      OS << Buffer << ", ";
+      Buffer.clear();
+      Upper.toString(Buffer);
+      OS << Buffer << ']';
     }
 
     if (MayBeSNaN || MayBeQNaN) {
diff --git a/llvm/unittests/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt
index 633166221c6907..e5c8630f3eed77 100644
--- a/llvm/unittests/IR/CMakeLists.txt
+++ b/llvm/unittests/IR/CMakeLists.txt
@@ -16,6 +16,7 @@ add_llvm_unittest(IRTests
   BasicBlockTest.cpp
   BasicBlockDbgInfoTest.cpp
   CFGBuilder.cpp
+  ConstantFPRangeTest.cpp
   ConstantRangeTest.cpp
   ConstantRangeListTest.cpp
   ConstantsTest.cpp
diff --git a/llvm/unittests/IR/ConstantFPRangeTest.cpp b/llvm/unittests/IR/ConstantFPRangeTest.cpp
new file mode 100644
index 00000000000000..49d9e60acefdf6
--- /dev/null
+++ b/llvm/unittests/IR/ConstantFPRangeTest.cpp
@@ -0,0 +1,284 @@
+//===- ConstantRangeTest.cpp - ConstantRange tests ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/IR/ConstantFPRange.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/ADT/SmallBitVector.h"
+#include "llvm/Analysis/ValueTracking.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/Operator.h"
+#include "llvm/Support/KnownBits.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+class ConstantFPRangeTest : public ::testing::Test {
+protected:
+  static const fltSemantics &Sem;
+  static ConstantFPRange Full;
+  static ConstantFPRange Empty;
+  static ConstantFPRange Finite;
+  static ConstantFPRange One;
+  static ConstantFPRange PosZero;
+  static ConstantFPRange NegZero;
+  static ConstantFPRange Zero;
+  static ConstantFPRange PosInf;
+  static ConstantFPRange NegInf;
+  static ConstantFPRange Denormal;
+  static ConstantFPRange NaN;
+  static ConstantFPRange SNaN;
+  static ConstantFPRange QNaN;
+  static ConstantFPRange Some;
+  static ConstantFPRange SomePos;
+  static ConstantFPRange SomeNeg;
+};
+
+const fltSemantics &ConstantFPRangeTest::Sem = APFloat::IEEEdouble();
+ConstantFPRange ConstantFPRangeTest::Full =
+    ConstantFPRange::getFull(APFloat::IEEEdouble());
+ConstantFPRange ConstantFPRangeTest::Empty =
+    ConstantFPRange::getEmpty(APFloat::IEEEdouble());
+ConstantFPRange ConstantFPRangeTest::Finite =
+    ConstantFPRange::getFinite(APFloat::IEEEdouble());
+ConstantFPRange ConstantFPRangeTest::One = ConstantFPRange(APFloat(1.0));
+ConstantFPRange ConstantFPRangeTest::PosZero = ConstantFPRange(
+    APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/false));
+ConstantFPRange ConstantFPRangeTest::NegZero =
+    ConstantFPRange(APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/true));
+ConstantFPRange ConstantFPRangeTest::Zero = ConstantFPRange::getNonNaN(
+    APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/true),
+    APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/false));
+ConstantFPRange ConstantFPRangeTest::Denormal =
+    ConstantFPRange(APFloat::getSmallest(APFloat::IEEEdouble()));
+ConstantFPRange ConstantFPRangeTest::PosInf =
+    ConstantFPRange(APFloat::getInf(APFloat::IEEEdouble(), /*Negative=*/false));
+ConstantFPRange ConstantFPRangeTest::NegInf =
+    ConstantFPRange(APFloat::getInf(APFloat::IEEEdouble(), /*Negative=*/true));
+ConstantFPRange ConstantFPRangeTest::NaN =
+    ConstantFPRange(APFloat(1.0), APFloat(-1.0));
+ConstantFPRange ConstantFPRangeTest::SNaN =
+    ConstantFPRange(APFloat::getSNaN(APFloat::IEEEdouble()));
+ConstantFPRange ConstantFPRangeTest::QNaN =
+    ConstantFPRange(APFloat::getQNaN(APFloat::IEEEdouble()));
+ConstantFPRange ConstantFPRangeTest::Some =
+    ConstantFPRange::getNonNaN(APFloat(-3.0), APFloat(3.0));
+ConstantFPRange ConstantFPRangeTest::SomePos = ConstantFPRange::getNonNaN(
+    APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/false), APFloat(3.0));
+ConstantFPRange ConstantFPRangeTest::SomeNeg = ConstantFPRange::getNonNaN(
+    APFloat(-3.0), APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/true));
+
+TEST_F(ConstantFPRangeTest, Basics) {
+  EXPECT_TRUE(Full.isFullSet());
+  EXPECT_FALSE(Full.isEmptySet());
+  EXPECT_TRUE(Full.contains(APFloat::getNaN(Sem)));
+  EXPECT_TRUE(Full.contains(APFloat::getInf(Sem, /*Negative=*/false)));
+  EXPECT_TRUE(Full.contains(APFloat::getInf(Sem, /*Negative=*/true)));
+  EXPECT_TRUE(Full.contains(APFloat::getZero(Sem, /*Negative=*/false)));
+  EXPECT_TRUE(Full.contains(APFloat::getZero(Sem, /*Negative=*/true)));
+  EXPECT_TRUE(Full.contains(APFloat::getSmallest(Sem)));
+  EXPECT_TRUE(Full.contains(APFloat(2.0)));
+  EXPECT_TRUE(Full.contains(Full));
+  EXPECT_TRUE(Full.contains(Empty));
+  EXPECT_TRUE(Full.contains(Finite));
+  EXPECT_TRUE(Full.contains(Zero));
+  EXPECT_TRUE(Full.contains(Some));
+
+  EXPECT_FALSE(Empty.isFullSet());
+  EXPECT_TRUE(Empty.isEmptySet());
+  EXPECT_FALSE(Empty.contains(APFloat::getNaN(Sem)));
+  EXPECT_FALSE(Empty.contains(APFloat::getInf(Sem, /*Negative=*/false)));
+  EXPECT_FALSE(Empty.contains(APFloat::getZero(Sem, /*Negative=*/true)));
+  EXPECT_FALSE(Empty.contains(APFloat(2.0)));
+  EXPECT_TRUE(Empty.contains(Empty));
+
+  EXPECT_FALSE(Finite.isFullSet());
+  EXPECT_FALSE(Finite.isEmptySet());
+  EXPECT_FALSE(Finite.contains(APFloat::getNaN(Sem)));
+  EXPECT_FALSE(Finite.contains(APFloat::getInf(Sem, /*Negative=*/false)));
+  EXPECT_FALSE(Finite.contains(APFloat::getInf(Sem, /*Negative=*/true)));
+  EXPECT_TRUE(Finite.contains(APFloat::getLargest(Sem, /*Negative=*/false)));
+  EXPECT_TRUE(Finite.contains(APFloat::getLargest(Sem, /*Negative=*/true)));
+  EXPECT_TRUE(Finite.contains(Finite));
+  EXPECT_TRUE(Finite.contains(Some));
+  EXPECT_TRUE(Finite.contains(Denormal));
+  EXPECT_TRUE(Finite.contains(Zero));
+  EXPECT_FALSE(Finite.contains(PosInf));
+  EXPECT_FALSE(Finite.contains(NaN));
+
+  EXPECT_TRUE(One.contains(APFloat(1.0)));
+  EXPECT_FALSE(One.contains(APFloat(1.1)));
+
+  EXPECT_TRUE(PosZero.contains(APFloat::getZero(Sem, /*Negative=*/false)));
+  EXPECT_FALSE(PosZero.contains(APFloat::getZero(Sem, /*Negative=*/true)));
+  EXPECT_TRUE(NegZero.contains(APFloat::getZero(Sem, /*Negative=*/true)));
+  EXPECT_FALSE(NegZero.contains(APFloat::getZero(Sem, /*Negative=*/false)));
+  EXPECT_TRUE(Zero.contains(PosZero));
+  EXPECT_TRUE(Zero.contains(NegZero));
+  EXPECT_TRUE(Denormal.contains(APFloat::getSmallest(Sem)));
+  EXPECT_FALSE(Denormal.contains(APFloat::getSmallestNormalized(Sem)));
+  EXPECT_TRUE(PosInf.contains(APFloat::getInf(Sem, /*Negative=*/false)));
+  EXPECT_TRUE(NegInf.contains(APFloat::getInf(Sem, /*Negative=*/true)));
+  EXPECT_TRUE(NaN.contains(APFloat::getQNaN(Sem)));
+  EXPECT_TRUE(NaN.contains(APFloat::getSNaN(Sem)));
+  EXPECT_TRUE(NaN.contains(SNaN));
+  EXPECT_TRUE(NaN.contains(QNaN));
+
+  EXPECT_TRUE(Some.contains(APFloat(3.0)));
+  EXPECT_TRUE(Some.contains(APFloat(-3.0)));
+  EXPECT_FALSE(Some.contains(APFloat(4.0)));
+  APFloat Next1(3.0);
+  Next1.next(/*nextDown=*/true);
+  EXPECT_TRUE(Some.contains(Next1));
+  APFloat Next2(3.0);
+  Next2.next(/*nextDown=*/false);
+  EXPECT_FALSE(Some.contains(Next2));
+  EXPECT_TRUE(Some.contains(Zero));
+  EXPECT_TRUE(Some.contains(Some));
+  EXPECT_TRUE(Some.contains(One));
+  EXPECT_FALSE(Some.contains(NaN));
+  EXPECT_FALSE(Some.contains(PosInf));
+  EXPECT_TRUE(SomePos.contains(APFloat(3.0)));
+  EXPECT_FALSE(SomeNeg.contains(APFloat(3.0)));
+  EXPECT_TRUE(SomeNeg.contains(APFloat(-3.0)));
+  EXPECT_FALSE(SomePos.contains(APFloat(-3.0)));
+  EXPECT_TRUE(Some.contains(SomePos));
+  EXPECT_TRUE(Some.contains(SomeNeg));
+}
+
+TEST_F(ConstantFPRangeTest, Equality) {
+  EXPECT_EQ(Full, Full);
+  EXPECT_EQ(Empty, Empty);
+  EXPECT_EQ(One, One);
+  EXPECT_EQ(Some, Some);
+  EXPECT_NE(Full, Empty);
+  EXPECT_NE(Zero, PosZero);
+  EXPECT_NE(One, NaN);
+  EXPECT_NE(Some, One);
+  EXPECT_NE(SNaN, QNaN);
+}
+
+TEST_F(ConstantFPRangeTest, SingleElement) {
+  EXPECT_EQ(Full.getSingleElement(), static_cast<APFloat *>(nullptr));
+  EXPECT_EQ(Empty.getSingleElement(), static_cast<APFloat *>(nullptr));
+  EXPECT_EQ(Finite.getSingleElement(), static_cast<APFloat *>(nullptr));
+  EXPECT_EQ(Zero.getSingleElement(), static_cast<APFloat *>(nullptr));
+  EXPECT_EQ(NaN.getSingleElement(), static_cast<APFloat *>(nullptr));
+  EXPECT_EQ(SNaN.getSingleElement(), static_cast<APFloat *>(nullptr));
+  EXPECT_EQ(QNaN.getSingleElement(), static_cast<APFloat *>(nullptr));
+
+  EXPECT_EQ(*One.getSingleElement(), APFloat(1.0));
+  EXPECT_EQ(*PosZero.getSingleElement(), APFloat::getZero(Sem));
+  EXPECT_EQ(*PosInf.getSingleElement(), APFloat::getInf(Sem));
+
+  EXPECT_FALSE(Full.isSingleElement());
+  EXPECT_FALSE(Empty.isSingleElement());
+  EXPECT_TRUE(One.isSingleElement());
+  EXPECT_FALSE(Some.isSingleElement());
+  EXPECT_FALSE(Zero.isSingleElement());
+}
+
+TEST_F(ConstantFPRangeTest, IntersectWith) {
+  EXPECT_EQ(Empty.intersectWith(Full), Empty);
+  EXPECT_EQ(Empty.intersectWith(Empty), Empty);
+  EXPECT_EQ(Empty.intersectWith(One), Empty);
+  EXPECT_EQ(Empty.intersectWith(Some), Empty);
+  EXPECT_EQ(Full.intersectWith(Full), Full);
+  EXPECT_EQ(Some.intersectWith(Some), Some);
+  EXPECT_EQ(Some.intersectWith(One), One);
+  EXPECT_EQ(Full.intersectWith(One), One);
+  EXPECT_EQ(Full.intersectWith(Some), Some);
+  EXPECT_EQ(Some.intersectWith(SomePos), SomePos);
+  EXPECT_EQ(Some.intersectWith(SomeNeg), SomeNeg);
+  EXPECT_EQ(NaN.intersectWith(Finite), Empty);
+  EXPECT_EQ(NaN.intersectWith(SNaN), SNaN);
+  EXPECT_EQ(NaN.intersectWith(QNaN), QNaN);
+  EXPECT_EQ(Finite.intersectWith(One), One);
+  EXPECT_EQ(Some.intersectWith(Zero), Zero);
+  EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(4.0))
+                .intersectWith(
+                    ConstantFPRange::getNonNaN(APFloat(3.0), APFloat(6.0))),
+            ConstantFPRange::getNonNaN(APFloat(3.0), APFloat(4.0)));
+  EXPECT_EQ(ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))
+                .intersectWith(
+                    ConstantFPRange::getNonNaN(APFloat(5.0), APFloat(6.0))),
+            Empty);
+}
+
+TEST_F(ConstantFPRangeTest, UnionWith) {
+  EXPECT_EQ(Empty.unionWith(Full), Full);
+  EXPECT_EQ(Empty.unionWith(Empty), Empty);
+  EXPECT_EQ(Empty.unionWith(One), One);
+  EXPECT_EQ(Empty.unionWith(Some), Some);
+  EXPECT_EQ(Full.unionWith(Full), Full);
+  EXPECT_EQ(Some.unionWith(Some), Some);
+  EXPECT_EQ(Some.unionWith(One), Some);
+  EXPECT_EQ(Full.unionWith(Some), Full);
+  EXPECT_EQ(Some.unionWith(SomePos), Some);
+  EXPECT_EQ(Some.unionWith(SomeNeg), Some);
+  EXPECT_EQ(Finite.unionWith(One), Finite);
+  EXPECT_EQ(Some.unionWith(Zero), Some);
+  EXPECT_EQ(Finite.unionWith(PosInf).unionWith(NegInf).unionWith(NaN), Full);
+  EXPECT_EQ(PosZero.unionWith(NegZero), Zero);
+  EXPECT_EQ(NaN.unionWith(SNaN), NaN);
+  EXPECT_EQ(NaN.unionWith(QNaN), NaN);
+  EXPECT_EQ(SNaN.unionWith(QNaN), NaN);
+  EXPECT_EQ(
+      ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(4.0))
+          .unionWith(ConstantFPRange::getNonNaN(APFloat(3.0), APFloat(6.0))),
+      ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(6.0)));
+  EXPECT_EQ(
+      ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(2.0))
+          .unionWith(ConstantFPRange::getNonNaN(APFloat(5.0), APFloat(6.0))),
+      ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(6.0)));
+}
+
+TEST_F(ConstantFPRangeTest, FPClassify) {
+  EXPECT_EQ(Empty.classify(), fcNone);
+  EXPECT_EQ(Full.classify(), fcAllFlags);
+  EXPECT_EQ(Finite.classify(), fcFinite);
+  EXPECT_EQ(Zero.classify(), fcZero);
+  EXPECT_EQ(NaN.classify(), fcNan);
+  EXPECT_EQ(SNaN.classify(), fcSNan);
+  EXPECT_EQ(QNaN.classify(), fcQNan);
+  EXPECT_EQ(One.classify(), fcPosNormal);
+  EXPECT_EQ(Some.classify(), fcFinite);
+  EXPECT_EQ(SomePos.classify(), fcPosFinite);
+  EXPECT_EQ(SomeNeg.classify(), fcNegFinite);
+  EXPECT_EQ(PosInf.classify(), fcPosInf);
+  EXPECT_EQ(NegInf.classify(), fcNegInf);
+  EXPECT_TRUE(SomePos.toKnownFPClass().cannotBeOrderedLessThanZero());
+  EXPECT_EQ(Finite.getSignBit(), std::nullopt);
+  EXPECT_EQ(PosZero.getSignBit(), false);
+  EXPECT_EQ(NegZero.getSignBit(), true);
+  EXPECT_EQ(SomePos.getSignBit(), false);
+  EXPECT_EQ(SomeNeg.getSignBit(), true);
+  EXPECT_EQ(SomePos.toKnownFPClass().SignBit, false);
+  EXPECT_EQ(SomeNeg.toKnownFPClass().SignBit, true);
+}
+
+TEST_F(ConstantFPRangeTest, Print) {
+  auto ToString = [](const ConstantFPRange &CR) {
+    std::string Str;
+    raw_string_ostream OS(Str);
+    CR.print(OS);
+    return Str;
+  };
+
+  EXPECT_EQ(ToString(Full), "full-set");
+  EXPECT_EQ(ToString(Empty), "empty-set");
+  EXPECT_EQ(ToString(NaN), "NaN");
+  EXPECT_EQ(ToString(SNaN), "SNaN");
+  EXPECT_EQ(ToString(QNaN), "QNaN");
+  EXPECT_EQ(ToString(One), "[1, 1]");
+  EXPECT_EQ(ToString(Some.unionWith(SNaN)), "[-3, 3] with SNaN");
+}
+
+} // anonymous namespace

>From b7b07b321199ee126cc6f8cbedbb5e4fbe8d0205 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Sun, 15 Sep 2024 23:30:17 +0800
Subject: [PATCH 5/9] [ConstantFPRange] Add header comments.

---
 llvm/include/llvm/IR/ConstantFPRange.h | 10 +++++-----
 llvm/lib/IR/ConstantFPRange.cpp        | 19 -------------------
 2 files changed, 5 insertions(+), 24 deletions(-)

diff --git a/llvm/include/llvm/IR/ConstantFPRange.h b/llvm/include/llvm/IR/ConstantFPRange.h
index 986f0a8ced4ced..c5ff098f251fd1 100644
--- a/llvm/include/llvm/IR/ConstantFPRange.h
+++ b/llvm/include/llvm/IR/ConstantFPRange.h
@@ -12,8 +12,11 @@
 //
 // Range = [Lower, Upper] U (MayBeQNaN ? QNaN : {}) U (MayBeSNaN ? SNaN : {})
 // Specifically, [inf, -inf] represents an empty set.
-// Note: -0 is considered to be less than 0. That is, range [0, 0] doesn't
-// contain -0.
+// Note:
+// 1. Bounds are inclusive.
+// 2. -0 is considered to be less than 0. That is, range [0, 0] doesn't contain
+// -0.
+// 3. Currently wrapping ranges are not supported.
 //
 //===----------------------------------------------------------------------===//
 
@@ -50,9 +53,6 @@ class [[nodiscard]] ConstantFPRange {
   bool isNaNOnly() const;
 
 public:
-  /// Return true if the floating point format is supported.
-  static bool isSupportedSemantics(const fltSemantics &Sem);
-
   /// Initialize a full or empty set for the specified semantics.
   explicit ConstantFPRange(const fltSemantics &Sem, bool IsFullSet);
 
diff --git a/llvm/lib/IR/ConstantFPRange.cpp b/llvm/lib/IR/ConstantFPRange.cpp
index 093c1cf38febea..90ab5653585d4a 100644
--- a/llvm/lib/IR/ConstantFPRange.cpp
+++ b/llvm/lib/IR/ConstantFPRange.cpp
@@ -15,20 +15,6 @@
 
 using namespace llvm;
 
-// A floating point format must support NaN, Inf and -0.
-bool ConstantFPRange::isSupportedSemantics(const fltSemantics &Sem) {
-  switch (APFloat::SemanticsToEnum(Sem)) {
-  default:
-    return false;
-  case APFloat::S_IEEEhalf:
-  case APFloat::S_BFloat:
-  case APFloat::S_IEEEsingle:
-  case APFloat::S_IEEEdouble:
-  case APFloat::S_IEEEquad:
-    return true;
-  }
-}
-
 void ConstantFPRange::makeEmpty() {
   auto &Sem = Lower.getSemantics();
   Lower = APFloat::getInf(Sem, /*Negative=*/false);
@@ -51,7 +37,6 @@ bool ConstantFPRange::isNaNOnly() const {
 
 ConstantFPRange::ConstantFPRange(const fltSemantics &Sem, bool IsFullSet)
     : Lower(Sem, APFloat::uninitialized), Upper(Sem, APFloat::uninitialized) {
-  assert(isSupportedSemantics(Sem) && "Unsupported fp format");
   Lower = APFloat::getInf(Sem, /*Negative=*/IsFullSet);
   Upper = APFloat::getInf(Sem, /*Negative=*/!IsFullSet);
   MayBeQNaN = IsFullSet;
@@ -61,8 +46,6 @@ ConstantFPRange::ConstantFPRange(const fltSemantics &Sem, bool IsFullSet)
 ConstantFPRange::ConstantFPRange(const APFloat &Value)
     : Lower(Value.getSemantics(), APFloat::uninitialized),
       Upper(Value.getSemantics(), APFloat::uninitialized) {
-  assert(isSupportedSemantics(getSemantics()) && "Unsupported fp format");
-
   if (Value.isNaN()) {
     makeEmpty();
     bool IsSNaN = Value.isSignaling();
@@ -89,8 +72,6 @@ static APFloat::cmpResult strictCompare(const APFloat &LHS,
 ConstantFPRange::ConstantFPRange(APFloat LowerVal, APFloat UpperVal,
                                  bool MayBeQNaN, bool MayBeSNaN)
     : Lower(std::move(LowerVal)), Upper(std::move(UpperVal)) {
-  assert(isSupportedSemantics(getSemantics()) && "Unsupported fp format");
-
   // Canonicalize empty set into [Inf, -Inf].
   if (strictCompare(Lower, Upper) == APFloat::cmpGreaterThan &&
       !(Lower.isInfinity() && Upper.isInfinity()))

>From 241323a60b5b5485eab9af5f4768aad814c43960 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 16 Sep 2024 10:06:30 +0800
Subject: [PATCH 6/9] [ConstantFPRange] Address review comments.

---
 llvm/include/llvm/IR/ConstantFPRange.h    | 26 ++++++++++++++-----
 llvm/lib/IR/ConstantFPRange.cpp           | 31 ++++++++++++++++++-----
 llvm/unittests/IR/ConstantFPRangeTest.cpp |  4 +--
 3 files changed, 46 insertions(+), 15 deletions(-)

diff --git a/llvm/include/llvm/IR/ConstantFPRange.h b/llvm/include/llvm/IR/ConstantFPRange.h
index c5ff098f251fd1..1977d99c179509 100644
--- a/llvm/include/llvm/IR/ConstantFPRange.h
+++ b/llvm/include/llvm/IR/ConstantFPRange.h
@@ -52,16 +52,18 @@ class [[nodiscard]] ConstantFPRange {
   void makeFull();
   bool isNaNOnly() const;
 
-public:
   /// Initialize a full or empty set for the specified semantics.
   explicit ConstantFPRange(const fltSemantics &Sem, bool IsFullSet);
 
+public:
   /// Initialize a range to hold the single specified value.
-  ConstantFPRange(const APFloat &Value);
+  explicit ConstantFPRange(const APFloat &Value);
 
   /// Initialize a range of values explicitly.
-  ConstantFPRange(APFloat LowerVal, APFloat UpperVal, bool MayBeQNaN = true,
-                  bool MayBeSNaN = true);
+  /// Note: If \p LowerVal is greater than \p UpperVal, please use the canonical
+  /// form [Inf, -Inf].
+  ConstantFPRange(APFloat LowerVal, APFloat UpperVal, bool MayBeQNaN,
+                  bool MayBeSNaN);
 
   /// Create empty constant range with the given semantics.
   static ConstantFPRange getEmpty(const fltSemantics &Sem) {
@@ -82,12 +84,22 @@ class [[nodiscard]] ConstantFPRange {
                            /*MayBeQNaN=*/false, /*MayBeSNaN=*/false);
   }
 
+  /// Create a range which may contain NaNs.
+  static ConstantFPRange getMayBeNaN(APFloat LowerVal, APFloat UpperVal) {
+    return ConstantFPRange(std::move(LowerVal), std::move(UpperVal),
+                           /*MayBeQNaN=*/true, /*MayBeSNaN=*/true);
+  }
+
+  /// Create a range which only contains NaNs.
+  static ConstantFPRange getNaNOnly(const fltSemantics &Sem, bool MayBeQNaN,
+                                    bool MayBeSNaN);
+
   /// Produce the smallest range such that all values that may satisfy the given
   /// predicate with any value contained within Other is contained in the
   /// returned range.  Formally, this returns a superset of
   /// 'union over all y in Other . { x : fcmp op x y is true }'.  If the exact
-  /// answer is not representable as a ConstantRange, the return value will be a
-  /// proper superset of the above.
+  /// answer is not representable as a ConstantFPRange, the return value will be
+  /// a proper superset of the above.
   ///
   /// Example: Pred = ole and Other = float [2, 5] returns Result = [-inf, 5]
   static ConstantFPRange makeAllowedFCmpRegion(FCmpInst::Predicate Pred,
@@ -97,7 +109,7 @@ class [[nodiscard]] ConstantFPRange {
   /// satisfy the given predicate with all values contained within Other.
   /// Formally, this returns a subset of
   /// 'intersection over all y in Other . { x : fcmp op x y is true }'.  If the
-  /// exact answer is not representable as a ConstantRange, the return value
+  /// exact answer is not representable as a ConstantFPRange, the return value
   /// will be a proper subset of the above.
   ///
   /// Example: Pred = ole and Other = float [2, 5] returns [-inf, 2]
diff --git a/llvm/lib/IR/ConstantFPRange.cpp b/llvm/lib/IR/ConstantFPRange.cpp
index 90ab5653585d4a..2970988d43b1aa 100644
--- a/llvm/lib/IR/ConstantFPRange.cpp
+++ b/llvm/lib/IR/ConstantFPRange.cpp
@@ -69,13 +69,22 @@ static APFloat::cmpResult strictCompare(const APFloat &LHS,
   return LHS.compare(RHS);
 }
 
+static bool isNonCanonicalEmptySet(const APFloat &Lower, const APFloat &Upper) {
+  return strictCompare(Lower, Upper) == APFloat::cmpGreaterThan &&
+         !(Lower.isInfinity() && Upper.isInfinity());
+}
+
+static void canonicalizeRange(APFloat &Lower, APFloat &Upper) {
+  if (isNonCanonicalEmptySet(Lower, Upper)) {
+    Lower = APFloat::getInf(Lower.getSemantics(), /*Negative=*/false);
+    Upper = APFloat::getInf(Upper.getSemantics(), /*Negative=*/true);
+  }
+}
+
 ConstantFPRange::ConstantFPRange(APFloat LowerVal, APFloat UpperVal,
                                  bool MayBeQNaN, bool MayBeSNaN)
     : Lower(std::move(LowerVal)), Upper(std::move(UpperVal)) {
-  // Canonicalize empty set into [Inf, -Inf].
-  if (strictCompare(Lower, Upper) == APFloat::cmpGreaterThan &&
-      !(Lower.isInfinity() && Upper.isInfinity()))
-    makeEmpty();
+  assert(!isNonCanonicalEmptySet(Lower, Upper) && "Non-canonical form");
   this->MayBeQNaN = MayBeQNaN;
   this->MayBeSNaN = MayBeSNaN;
 }
@@ -86,6 +95,13 @@ ConstantFPRange ConstantFPRange::getFinite(const fltSemantics &Sem) {
                          /*MayBeQNaN=*/false, /*MayBeSNaN=*/false);
 }
 
+ConstantFPRange ConstantFPRange::getNaNOnly(const fltSemantics &Sem,
+                                            bool MayBeQNaN, bool MayBeSNaN) {
+  return ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/false),
+                         APFloat::getInf(Sem, /*Negative=*/true), MayBeQNaN,
+                         MayBeSNaN);
+}
+
 ConstantFPRange
 ConstantFPRange::makeAllowedFCmpRegion(FCmpInst::Predicate Pred,
                                        const ConstantFPRange &Other) {
@@ -102,7 +118,7 @@ ConstantFPRange::makeSatisfyingFCmpRegion(FCmpInst::Predicate Pred,
 
 ConstantFPRange ConstantFPRange::makeExactFCmpRegion(FCmpInst::Predicate Pred,
                                                      const APFloat &Other) {
-  return makeAllowedFCmpRegion(Pred, Other);
+  return makeAllowedFCmpRegion(Pred, ConstantFPRange(Other));
 }
 
 bool ConstantFPRange::fcmp(FCmpInst::Predicate Pred,
@@ -221,7 +237,10 @@ LLVM_DUMP_METHOD void ConstantFPRange::dump() const { print(dbgs()); }
 
 ConstantFPRange
 ConstantFPRange::intersectWith(const ConstantFPRange &CR) const {
-  return ConstantFPRange(maxnum(Lower, CR.Lower), minnum(Upper, CR.Upper),
+  APFloat NewLower = maxnum(Lower, CR.Lower);
+  APFloat NewUpper = minnum(Upper, CR.Upper);
+  canonicalizeRange(NewLower, NewUpper);
+  return ConstantFPRange(std::move(NewLower), std::move(NewUpper),
                          MayBeQNaN & CR.MayBeQNaN, MayBeSNaN & CR.MayBeSNaN);
 }
 
diff --git a/llvm/unittests/IR/ConstantFPRangeTest.cpp b/llvm/unittests/IR/ConstantFPRangeTest.cpp
index 49d9e60acefdf6..45f61375f0b63a 100644
--- a/llvm/unittests/IR/ConstantFPRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantFPRangeTest.cpp
@@ -62,8 +62,8 @@ ConstantFPRange ConstantFPRangeTest::PosInf =
     ConstantFPRange(APFloat::getInf(APFloat::IEEEdouble(), /*Negative=*/false));
 ConstantFPRange ConstantFPRangeTest::NegInf =
     ConstantFPRange(APFloat::getInf(APFloat::IEEEdouble(), /*Negative=*/true));
-ConstantFPRange ConstantFPRangeTest::NaN =
-    ConstantFPRange(APFloat(1.0), APFloat(-1.0));
+ConstantFPRange ConstantFPRangeTest::NaN = ConstantFPRange::getNaNOnly(
+    APFloat::IEEEdouble(), /*MayBeQNaN=*/true, /*MayBeSNaN=*/true);
 ConstantFPRange ConstantFPRangeTest::SNaN =
     ConstantFPRange(APFloat::getSNaN(APFloat::IEEEdouble()));
 ConstantFPRange ConstantFPRangeTest::QNaN =

>From 4cc8873862ee77d40327478b449b8e4f6c77bae0 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Mon, 16 Sep 2024 11:56:48 +0800
Subject: [PATCH 7/9] [ConstantFPRange] Exhaustively enumerate ranges

---
 llvm/include/llvm/ADT/APFloat.h           |  5 ++
 llvm/include/llvm/IR/ConstantFPRange.h    |  4 ++
 llvm/lib/IR/ConstantFPRange.cpp           | 17 +++--
 llvm/lib/Support/APFloat.cpp              |  7 +-
 llvm/unittests/IR/ConstantFPRangeTest.cpp | 86 +++++++++++++++++++++++
 5 files changed, 108 insertions(+), 11 deletions(-)

diff --git a/llvm/include/llvm/ADT/APFloat.h b/llvm/include/llvm/ADT/APFloat.h
index 7039e961bff82d..9cc8369a0bf52b 100644
--- a/llvm/include/llvm/ADT/APFloat.h
+++ b/llvm/include/llvm/ADT/APFloat.h
@@ -1534,6 +1534,11 @@ inline APFloat maximumnum(const APFloat &A, const APFloat &B) {
   return A < B ? B : A;
 }
 
+inline raw_ostream &operator<<(raw_ostream &OS, const APFloat &V) {
+  V.print(OS);
+  return OS;
+}
+
 // We want the following functions to be available in the header for inlining.
 // We cannot define them inline in the class definition of `DoubleAPFloat`
 // because doing so would instantiate `std::unique_ptr<APFloat[]>` before
diff --git a/llvm/include/llvm/IR/ConstantFPRange.h b/llvm/include/llvm/IR/ConstantFPRange.h
index 1977d99c179509..23f0e8b8e0d134 100644
--- a/llvm/include/llvm/IR/ConstantFPRange.h
+++ b/llvm/include/llvm/IR/ConstantFPRange.h
@@ -136,6 +136,10 @@ class [[nodiscard]] ConstantFPRange {
   /// Return the upper value for this range.
   const APFloat &getUpper() const { return Upper; }
 
+  bool containsNaN() const { return MayBeQNaN || MayBeSNaN; }
+  bool containsQNaN() const { return MayBeQNaN; }
+  bool containsSNaN() const { return MayBeSNaN; }
+
   /// Get the semantics of this ConstantFPRange.
   const fltSemantics &getSemantics() const { return Lower.getSemantics(); }
 
diff --git a/llvm/lib/IR/ConstantFPRange.cpp b/llvm/lib/IR/ConstantFPRange.cpp
index 2970988d43b1aa..58aab353b43939 100644
--- a/llvm/lib/IR/ConstantFPRange.cpp
+++ b/llvm/lib/IR/ConstantFPRange.cpp
@@ -84,6 +84,8 @@ static void canonicalizeRange(APFloat &Lower, APFloat &Upper) {
 ConstantFPRange::ConstantFPRange(APFloat LowerVal, APFloat UpperVal,
                                  bool MayBeQNaN, bool MayBeSNaN)
     : Lower(std::move(LowerVal)), Upper(std::move(UpperVal)) {
+  assert(&Lower.getSemantics() == &Upper.getSemantics() &&
+         "Should only use the same semantics");
   assert(!isNonCanonicalEmptySet(Lower, Upper) && "Non-canonical form");
   this->MayBeQNaN = MayBeQNaN;
   this->MayBeSNaN = MayBeSNaN;
@@ -208,15 +210,8 @@ void ConstantFPRange::print(raw_ostream &OS) const {
     OS << "empty-set";
   else {
     bool NaNOnly = isNaNOnly();
-    if (!NaNOnly) {
-      OS << '[';
-      SmallVector<char, 16> Buffer;
-      Lower.toString(Buffer);
-      OS << Buffer << ", ";
-      Buffer.clear();
-      Upper.toString(Buffer);
-      OS << Buffer << ']';
-    }
+    if (!NaNOnly)
+      OS << '[' << Lower << ", " << Upper << ']';
 
     if (MayBeSNaN || MayBeQNaN) {
       if (!NaNOnly)
@@ -237,6 +232,8 @@ LLVM_DUMP_METHOD void ConstantFPRange::dump() const { print(dbgs()); }
 
 ConstantFPRange
 ConstantFPRange::intersectWith(const ConstantFPRange &CR) const {
+  assert(&getSemantics() == &CR.getSemantics() &&
+         "Should only use the same semantics");
   APFloat NewLower = maxnum(Lower, CR.Lower);
   APFloat NewUpper = minnum(Upper, CR.Upper);
   canonicalizeRange(NewLower, NewUpper);
@@ -245,6 +242,8 @@ ConstantFPRange::intersectWith(const ConstantFPRange &CR) const {
 }
 
 ConstantFPRange ConstantFPRange::unionWith(const ConstantFPRange &CR) const {
+  assert(&getSemantics() == &CR.getSemantics() &&
+         "Should only use the same semantics");
   return ConstantFPRange(minnum(Lower, CR.Lower), maxnum(Upper, CR.Upper),
                          MayBeQNaN | CR.MayBeQNaN, MayBeSNaN | CR.MayBeSNaN);
 }
diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp
index 7f68c5ab9b7cf7..dee917fd56104c 100644
--- a/llvm/lib/Support/APFloat.cpp
+++ b/llvm/lib/Support/APFloat.cpp
@@ -5366,11 +5366,14 @@ APFloat APFloat::getAllOnesValue(const fltSemantics &Semantics) {
 void APFloat::print(raw_ostream &OS) const {
   SmallVector<char, 16> Buffer;
   toString(Buffer);
-  OS << Buffer << "\n";
+  OS << Buffer;
 }
 
 #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
-LLVM_DUMP_METHOD void APFloat::dump() const { print(dbgs()); }
+LLVM_DUMP_METHOD void APFloat::dump() const {
+  print(dbgs());
+  dbgs() << '\n';
+}
 #endif
 
 void APFloat::Profile(FoldingSetNodeID &NID) const {
diff --git a/llvm/unittests/IR/ConstantFPRangeTest.cpp b/llvm/unittests/IR/ConstantFPRangeTest.cpp
index 45f61375f0b63a..807a8c08594c51 100644
--- a/llvm/unittests/IR/ConstantFPRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantFPRangeTest.cpp
@@ -75,6 +75,56 @@ ConstantFPRange ConstantFPRangeTest::SomePos = ConstantFPRange::getNonNaN(
 ConstantFPRange ConstantFPRangeTest::SomeNeg = ConstantFPRange::getNonNaN(
     APFloat(-3.0), APFloat::getZero(APFloat::IEEEdouble(), /*Negative=*/true));
 
+static void strictNext(APFloat &V) {
+  // Note: nextUp(+/-0) is smallest.
+  if (V.isNegZero())
+    V = APFloat::getZero(V.getSemantics(), /*Negative=*/false);
+  else
+    V.next(/*nextDown=*/false);
+}
+
+template <typename Fn>
+static void EnumerateConstantFPRangesImpl(Fn TestFn, bool MayBeQNaN,
+                                          bool MayBeSNaN) {
+  const fltSemantics &Sem = APFloat::Float8E4M3();
+  APFloat PosInf = APFloat::getInf(Sem, /*Negative=*/false);
+  APFloat NegInf = APFloat::getInf(Sem, /*Negative=*/true);
+  TestFn(ConstantFPRange(PosInf, NegInf, MayBeQNaN, MayBeSNaN));
+
+  auto Next = [&](APFloat &V) {
+    if (V.isPosInfinity())
+      return false;
+    strictNext(V);
+    return true;
+  };
+
+  APFloat Lower = NegInf;
+  do {
+    APFloat Upper = Lower;
+    do {
+      TestFn(ConstantFPRange(Lower, Upper, MayBeQNaN, MayBeSNaN));
+    } while (Next(Upper));
+  } while (Next(Lower));
+}
+
+template <typename Fn> static void EnumerateConstantFPRanges(Fn TestFn) {
+  EnumerateConstantFPRangesImpl(TestFn, /*MayBeQNaN=*/false,
+                                /*MayBeSNaN=*/false);
+  EnumerateConstantFPRangesImpl(TestFn, /*MayBeQNaN=*/false,
+                                /*MayBeSNaN=*/true);
+  EnumerateConstantFPRangesImpl(TestFn, /*MayBeQNaN=*/true,
+                                /*MayBeSNaN=*/false);
+  EnumerateConstantFPRangesImpl(TestFn, /*MayBeQNaN=*/true, /*MayBeSNaN=*/true);
+}
+
+template <typename Fn>
+static void EnumerateTwoInterestingConstantFPRanges(Fn TestFn) {
+  EnumerateConstantFPRanges([&](const ConstantFPRange &CR1) {
+    EnumerateConstantFPRanges(
+        [&](const ConstantFPRange &CR2) { TestFn(CR1, CR2); });
+  });
+}
+
 TEST_F(ConstantFPRangeTest, Basics) {
   EXPECT_TRUE(Full.isFullSet());
   EXPECT_FALSE(Full.isEmptySet());
@@ -185,6 +235,14 @@ TEST_F(ConstantFPRangeTest, SingleElement) {
   EXPECT_FALSE(Zero.isSingleElement());
 }
 
+TEST_F(ConstantFPRangeTest, Enumerate) {
+  constexpr unsigned NNaNValues = (1 << 8) - 2 * ((1 << 3) - 1);
+  constexpr unsigned Expected = 4 * ((NNaNValues + 1) * NNaNValues / 2 + 1);
+  unsigned Count = 0;
+  EnumerateConstantFPRanges([&](const ConstantFPRange &) { ++Count; });
+  EXPECT_EQ(Expected, Count);
+}
+
 TEST_F(ConstantFPRangeTest, IntersectWith) {
   EXPECT_EQ(Empty.intersectWith(Full), Empty);
   EXPECT_EQ(Empty.intersectWith(Empty), Empty);
@@ -262,6 +320,34 @@ TEST_F(ConstantFPRangeTest, FPClassify) {
   EXPECT_EQ(SomeNeg.getSignBit(), true);
   EXPECT_EQ(SomePos.toKnownFPClass().SignBit, false);
   EXPECT_EQ(SomeNeg.toKnownFPClass().SignBit, true);
+
+  EnumerateConstantFPRanges([](const ConstantFPRange &CR) {
+    unsigned Mask = fcNone;
+    if (CR.containsSNaN())
+      Mask |= fcSNan;
+    if (CR.containsQNaN())
+      Mask |= fcQNan;
+    bool HasPos = CR.containsNaN(), HasNeg = CR.containsNaN();
+    APFloat Lower = CR.getLower();
+    const APFloat &Upper = CR.getUpper();
+    if (!(Lower.isPosInfinity() && Upper.isNegInfinity())) {
+      while (true) {
+        Mask |= Lower.classify();
+        HasPos |= !Lower.isNegative();
+        HasNeg |= Lower.isNegative();
+        if (Lower.bitwiseIsEqual(Upper))
+          break;
+        strictNext(Lower);
+      }
+    }
+
+    std::optional<bool> SignBit = std::nullopt;
+    if (HasPos != HasNeg)
+      SignBit = HasNeg;
+
+    EXPECT_EQ(SignBit, CR.getSignBit()) << CR;
+    EXPECT_EQ(Mask, CR.classify()) << CR;
+  });
 }
 
 TEST_F(ConstantFPRangeTest, Print) {

>From 4a4660caefec52c836133b3302d679cf20a4df46 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 18 Sep 2024 15:13:01 +0800
Subject: [PATCH 8/9] [ConstantFPRange] Add death tests. NFC.

---
 llvm/unittests/IR/ConstantFPRangeTest.cpp | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/llvm/unittests/IR/ConstantFPRangeTest.cpp b/llvm/unittests/IR/ConstantFPRangeTest.cpp
index 807a8c08594c51..0ced8c8de2bb05 100644
--- a/llvm/unittests/IR/ConstantFPRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantFPRangeTest.cpp
@@ -367,4 +367,23 @@ TEST_F(ConstantFPRangeTest, Print) {
   EXPECT_EQ(ToString(Some.unionWith(SNaN)), "[-3, 3] with SNaN");
 }
 
+#ifdef GTEST_HAS_DEATH_TEST
+#ifndef NDEBUG
+TEST_F(ConstantFPRangeTest, NonCanonicalEmptySet) {
+  EXPECT_DEATH(ConstantFPRange::getNonNaN(APFloat(1.0), APFloat(0.0)),
+               "Non-canonical form");
+}
+TEST_F(ConstantFPRangeTest, MismatchedSemantics) {
+  EXPECT_DEATH(ConstantFPRange::getNonNaN(APFloat(0.0), APFloat(1.0f)),
+               "Should only use the same semantics");
+  EXPECT_DEATH(One.contains(APFloat(1.0f)),
+               "Should only use the same semantics");
+  ConstantFPRange OneF32 = ConstantFPRange(APFloat(1.0f));
+  EXPECT_DEATH(One.contains(OneF32), "Should only use the same semantics");
+  EXPECT_DEATH(One.intersectWith(OneF32), "Should only use the same semantics");
+  EXPECT_DEATH(One.unionWith(OneF32), "Should only use the same semantics");
+}
+#endif
+#endif
+
 } // anonymous namespace

>From d3a1fbdaeb25df10252904166aab8619c382d92d Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 18 Sep 2024 20:18:38 +0800
Subject: [PATCH 9/9] [ConstantFPRange] Add `EnumerateValuesInConstantFPRange`

---
 llvm/unittests/IR/ConstantFPRangeTest.cpp | 38 +++++++++++++----------
 1 file changed, 21 insertions(+), 17 deletions(-)

diff --git a/llvm/unittests/IR/ConstantFPRangeTest.cpp b/llvm/unittests/IR/ConstantFPRangeTest.cpp
index 0ced8c8de2bb05..30ffb840bbc72f 100644
--- a/llvm/unittests/IR/ConstantFPRangeTest.cpp
+++ b/llvm/unittests/IR/ConstantFPRangeTest.cpp
@@ -125,6 +125,19 @@ static void EnumerateTwoInterestingConstantFPRanges(Fn TestFn) {
   });
 }
 
+template <typename Fn>
+static void EnumerateValuesInConstantFPRange(const ConstantFPRange &CR,
+                                             Fn TestFn) {
+  const fltSemantics &Sem = CR.getSemantics();
+  unsigned Bits = APFloat::semanticsSizeInBits(Sem);
+  assert(Bits < 32 && "Too many bits");
+  for (unsigned I = 0, E = (1U << Bits) - 1; I != E; ++I) {
+    APFloat V(Sem, APInt(Bits, I));
+    if (CR.contains(V))
+      TestFn(V);
+  }
+}
+
 TEST_F(ConstantFPRangeTest, Basics) {
   EXPECT_TRUE(Full.isFullSet());
   EXPECT_FALSE(Full.isEmptySet());
@@ -323,23 +336,14 @@ TEST_F(ConstantFPRangeTest, FPClassify) {
 
   EnumerateConstantFPRanges([](const ConstantFPRange &CR) {
     unsigned Mask = fcNone;
-    if (CR.containsSNaN())
-      Mask |= fcSNan;
-    if (CR.containsQNaN())
-      Mask |= fcQNan;
-    bool HasPos = CR.containsNaN(), HasNeg = CR.containsNaN();
-    APFloat Lower = CR.getLower();
-    const APFloat &Upper = CR.getUpper();
-    if (!(Lower.isPosInfinity() && Upper.isNegInfinity())) {
-      while (true) {
-        Mask |= Lower.classify();
-        HasPos |= !Lower.isNegative();
-        HasNeg |= Lower.isNegative();
-        if (Lower.bitwiseIsEqual(Upper))
-          break;
-        strictNext(Lower);
-      }
-    }
+    bool HasPos = false, HasNeg = false;
+    EnumerateValuesInConstantFPRange(CR, [&](const APFloat &V) {
+      Mask |= V.classify();
+      if (V.isNegative())
+        HasNeg = true;
+      else
+        HasPos = true;
+    });
 
     std::optional<bool> SignBit = std::nullopt;
     if (HasPos != HasNeg)



More information about the llvm-commits mailing list