[llvm] [LLVM][IR] Add constant range support for floating-point types (PR #86483)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Sun Sep 15 08:32:25 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/5] 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/5] 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/5] [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/5] [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/5] [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()))
More information about the llvm-commits
mailing list