|  | //===- 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 | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "llvm/IR/ConstantFPRange.h" | 
|  | #include "llvm/ADT/APFloat.h" | 
|  | #include "llvm/Support/Debug.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  | #include <cassert> | 
|  |  | 
|  | using namespace llvm; | 
|  |  | 
|  | 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) { | 
|  | 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) { | 
|  | 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); | 
|  | } | 
|  |  | 
|  | 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 MayBeQNaNVal, bool MayBeSNaNVal) | 
|  | : Lower(std::move(LowerVal)), Upper(std::move(UpperVal)), | 
|  | MayBeQNaN(MayBeQNaNVal), MayBeSNaN(MayBeSNaNVal) { | 
|  | assert(&Lower.getSemantics() == &Upper.getSemantics() && | 
|  | "Should only use the same semantics"); | 
|  | assert(!isNonCanonicalEmptySet(Lower, Upper) && "Non-canonical form"); | 
|  | } | 
|  |  | 
|  | ConstantFPRange ConstantFPRange::getFinite(const fltSemantics &Sem) { | 
|  | return ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/true), | 
|  | APFloat::getLargest(Sem, /*Negative=*/false), | 
|  | /*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::getNonNaN(const fltSemantics &Sem) { | 
|  | return ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/true), | 
|  | APFloat::getInf(Sem, /*Negative=*/false), | 
|  | /*MayBeQNaN=*/false, /*MayBeSNaN=*/false); | 
|  | } | 
|  |  | 
|  | /// Return true for ULT/UGT/OLT/OGT | 
|  | static bool fcmpPredExcludesEqual(FCmpInst::Predicate Pred) { | 
|  | return !(Pred & FCmpInst::FCMP_OEQ); | 
|  | } | 
|  |  | 
|  | /// Return [-inf, V) or [-inf, V] | 
|  | static ConstantFPRange makeLessThan(APFloat V, FCmpInst::Predicate Pred) { | 
|  | const fltSemantics &Sem = V.getSemantics(); | 
|  | if (fcmpPredExcludesEqual(Pred)) { | 
|  | if (V.isNegInfinity()) | 
|  | return ConstantFPRange::getEmpty(Sem); | 
|  | V.next(/*nextDown=*/true); | 
|  | } | 
|  | return ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true), | 
|  | std::move(V)); | 
|  | } | 
|  |  | 
|  | /// Return (V, +inf] or [V, +inf] | 
|  | static ConstantFPRange makeGreaterThan(APFloat V, FCmpInst::Predicate Pred) { | 
|  | const fltSemantics &Sem = V.getSemantics(); | 
|  | if (fcmpPredExcludesEqual(Pred)) { | 
|  | if (V.isPosInfinity()) | 
|  | return ConstantFPRange::getEmpty(Sem); | 
|  | V.next(/*nextDown=*/false); | 
|  | } | 
|  | return ConstantFPRange::getNonNaN(std::move(V), | 
|  | APFloat::getInf(Sem, /*Negative=*/false)); | 
|  | } | 
|  |  | 
|  | /// Make sure that +0/-0 are both included in the range. | 
|  | static ConstantFPRange extendZeroIfEqual(const ConstantFPRange &CR, | 
|  | FCmpInst::Predicate Pred) { | 
|  | if (fcmpPredExcludesEqual(Pred)) | 
|  | return CR; | 
|  |  | 
|  | APFloat Lower = CR.getLower(); | 
|  | APFloat Upper = CR.getUpper(); | 
|  | if (Lower.isPosZero()) | 
|  | Lower = APFloat::getZero(Lower.getSemantics(), /*Negative=*/true); | 
|  | if (Upper.isNegZero()) | 
|  | Upper = APFloat::getZero(Upper.getSemantics(), /*Negative=*/false); | 
|  | return ConstantFPRange(std::move(Lower), std::move(Upper), CR.containsQNaN(), | 
|  | CR.containsSNaN()); | 
|  | } | 
|  |  | 
|  | static ConstantFPRange setNaNField(const ConstantFPRange &CR, | 
|  | FCmpInst::Predicate Pred) { | 
|  | bool ContainsNaN = FCmpInst::isUnordered(Pred); | 
|  | return ConstantFPRange(CR.getLower(), CR.getUpper(), | 
|  | /*MayBeQNaN=*/ContainsNaN, /*MayBeSNaN=*/ContainsNaN); | 
|  | } | 
|  |  | 
|  | ConstantFPRange | 
|  | ConstantFPRange::makeAllowedFCmpRegion(FCmpInst::Predicate Pred, | 
|  | const ConstantFPRange &Other) { | 
|  | if (Other.isEmptySet()) | 
|  | return Other; | 
|  | if (Other.containsNaN() && FCmpInst::isUnordered(Pred)) | 
|  | return getFull(Other.getSemantics()); | 
|  | if (Other.isNaNOnly() && FCmpInst::isOrdered(Pred)) | 
|  | return getEmpty(Other.getSemantics()); | 
|  |  | 
|  | switch (Pred) { | 
|  | case FCmpInst::FCMP_TRUE: | 
|  | return getFull(Other.getSemantics()); | 
|  | case FCmpInst::FCMP_FALSE: | 
|  | return getEmpty(Other.getSemantics()); | 
|  | case FCmpInst::FCMP_ORD: | 
|  | return getNonNaN(Other.getSemantics()); | 
|  | case FCmpInst::FCMP_UNO: | 
|  | return getNaNOnly(Other.getSemantics(), /*MayBeQNaN=*/true, | 
|  | /*MayBeSNaN=*/true); | 
|  | case FCmpInst::FCMP_OEQ: | 
|  | case FCmpInst::FCMP_UEQ: | 
|  | return setNaNField(extendZeroIfEqual(Other, Pred), Pred); | 
|  | case FCmpInst::FCMP_ONE: | 
|  | case FCmpInst::FCMP_UNE: | 
|  | if (const APFloat *SingleElement = | 
|  | Other.getSingleElement(/*ExcludesNaN=*/true)) { | 
|  | const fltSemantics &Sem = SingleElement->getSemantics(); | 
|  | if (SingleElement->isPosInfinity()) | 
|  | return setNaNField( | 
|  | getNonNaN(APFloat::getInf(Sem, /*Negative=*/true), | 
|  | APFloat::getLargest(Sem, /*Negative=*/false)), | 
|  | Pred); | 
|  | if (SingleElement->isNegInfinity()) | 
|  | return setNaNField( | 
|  | getNonNaN(APFloat::getLargest(Sem, /*Negative=*/true), | 
|  | APFloat::getInf(Sem, /*Negative=*/false)), | 
|  | Pred); | 
|  | } | 
|  | return Pred == FCmpInst::FCMP_ONE ? getNonNaN(Other.getSemantics()) | 
|  | : getFull(Other.getSemantics()); | 
|  | case FCmpInst::FCMP_OLT: | 
|  | case FCmpInst::FCMP_OLE: | 
|  | case FCmpInst::FCMP_ULT: | 
|  | case FCmpInst::FCMP_ULE: | 
|  | return setNaNField( | 
|  | extendZeroIfEqual(makeLessThan(Other.getUpper(), Pred), Pred), Pred); | 
|  | case FCmpInst::FCMP_OGT: | 
|  | case FCmpInst::FCMP_OGE: | 
|  | case FCmpInst::FCMP_UGT: | 
|  | case FCmpInst::FCMP_UGE: | 
|  | return setNaNField( | 
|  | extendZeroIfEqual(makeGreaterThan(Other.getLower(), Pred), Pred), Pred); | 
|  | default: | 
|  | llvm_unreachable("Unexpected predicate"); | 
|  | } | 
|  | } | 
|  |  | 
|  | ConstantFPRange | 
|  | ConstantFPRange::makeSatisfyingFCmpRegion(FCmpInst::Predicate Pred, | 
|  | const ConstantFPRange &Other) { | 
|  | if (Other.isEmptySet()) | 
|  | return getFull(Other.getSemantics()); | 
|  | if (Other.containsNaN() && FCmpInst::isOrdered(Pred)) | 
|  | return getEmpty(Other.getSemantics()); | 
|  | if (Other.isNaNOnly() && FCmpInst::isUnordered(Pred)) | 
|  | return getFull(Other.getSemantics()); | 
|  |  | 
|  | switch (Pred) { | 
|  | case FCmpInst::FCMP_TRUE: | 
|  | return getFull(Other.getSemantics()); | 
|  | case FCmpInst::FCMP_FALSE: | 
|  | return getEmpty(Other.getSemantics()); | 
|  | case FCmpInst::FCMP_ORD: | 
|  | return getNonNaN(Other.getSemantics()); | 
|  | case FCmpInst::FCMP_UNO: | 
|  | return getNaNOnly(Other.getSemantics(), /*MayBeQNaN=*/true, | 
|  | /*MayBeSNaN=*/true); | 
|  | case FCmpInst::FCMP_OEQ: | 
|  | case FCmpInst::FCMP_UEQ: | 
|  | return setNaNField(Other.isSingleElement(/*ExcludesNaN=*/true) || | 
|  | ((Other.classify() & ~fcNan) == fcZero) | 
|  | ? extendZeroIfEqual(Other, Pred) | 
|  | : getEmpty(Other.getSemantics()), | 
|  | Pred); | 
|  | case FCmpInst::FCMP_ONE: | 
|  | case FCmpInst::FCMP_UNE: | 
|  | return getEmpty(Other.getSemantics()); | 
|  | case FCmpInst::FCMP_OLT: | 
|  | case FCmpInst::FCMP_OLE: | 
|  | case FCmpInst::FCMP_ULT: | 
|  | case FCmpInst::FCMP_ULE: | 
|  | return setNaNField( | 
|  | extendZeroIfEqual(makeLessThan(Other.getLower(), Pred), Pred), Pred); | 
|  | case FCmpInst::FCMP_OGT: | 
|  | case FCmpInst::FCMP_OGE: | 
|  | case FCmpInst::FCMP_UGT: | 
|  | case FCmpInst::FCMP_UGE: | 
|  | return setNaNField( | 
|  | extendZeroIfEqual(makeGreaterThan(Other.getUpper(), Pred), Pred), Pred); | 
|  | default: | 
|  | llvm_unreachable("Unexpected predicate"); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::optional<ConstantFPRange> | 
|  | ConstantFPRange::makeExactFCmpRegion(FCmpInst::Predicate Pred, | 
|  | const APFloat &Other) { | 
|  | if ((Pred == FCmpInst::FCMP_UNE || Pred == FCmpInst::FCMP_ONE) && | 
|  | !Other.isNaN()) | 
|  | return std::nullopt; | 
|  | return makeSatisfyingFCmpRegion(Pred, ConstantFPRange(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(bool ExcludesNaN) const { | 
|  | if (!ExcludesNaN && (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."); | 
|  | // Set all bits from log2(LowerMask) to log2(UpperMask). | 
|  | Mask |= (UpperMask << 1) - LowerMask; | 
|  | } | 
|  | return static_cast<FPClassTest>(Mask); | 
|  | } | 
|  |  | 
|  | 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 << ", " << Upper << ']'; | 
|  |  | 
|  | 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 { | 
|  | 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); | 
|  | return ConstantFPRange(std::move(NewLower), std::move(NewUpper), | 
|  | MayBeQNaN & CR.MayBeQNaN, MayBeSNaN & CR.MayBeSNaN); | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } |