| //===- 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/ADT/FloatingPointMode.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 { | |
| assert(&getSemantics() == &CR.getSemantics() && | |
| "Should only use the same semantics"); | |
| 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); | |
| } | |
| ConstantFPRange ConstantFPRange::abs() const { | |
| if (isNaNOnly()) | |
| return *this; | |
| // Check if the range is all non-negative or all non-positive. | |
| if (Lower.isNegative() == Upper.isNegative()) { | |
| if (Lower.isNegative()) | |
| return negate(); | |
| return *this; | |
| } | |
| // The range contains both positive and negative values. | |
| APFloat NewLower = APFloat::getZero(getSemantics()); | |
| APFloat NewUpper = maxnum(-Lower, Upper); | |
| return ConstantFPRange(std::move(NewLower), std::move(NewUpper), MayBeQNaN, | |
| MayBeSNaN); | |
| } | |
| ConstantFPRange ConstantFPRange::negate() const { | |
| return ConstantFPRange(-Upper, -Lower, MayBeQNaN, MayBeSNaN); | |
| } | |
| /// Return true if the finite part is not empty after removing infinities. | |
| static bool removeInf(APFloat &Lower, APFloat &Upper, bool &HasPosInf, | |
| bool &HasNegInf) { | |
| assert(strictCompare(Lower, Upper) != APFloat::cmpGreaterThan && | |
| "Non-NaN part is empty."); | |
| auto &Sem = Lower.getSemantics(); | |
| if (Lower.isNegInfinity()) { | |
| Lower = APFloat::getLargest(Sem, /*Negative=*/true); | |
| HasNegInf = true; | |
| } | |
| if (Upper.isPosInfinity()) { | |
| Upper = APFloat::getLargest(Sem, /*Negative=*/false); | |
| HasPosInf = true; | |
| } | |
| return strictCompare(Lower, Upper) != APFloat::cmpGreaterThan; | |
| } | |
| ConstantFPRange ConstantFPRange::getWithoutInf() const { | |
| if (isNaNOnly()) | |
| return *this; | |
| APFloat NewLower = Lower; | |
| APFloat NewUpper = Upper; | |
| bool UnusedFlag; | |
| removeInf(NewLower, NewUpper, /*HasPosInf=*/UnusedFlag, | |
| /*HasNegInf=*/UnusedFlag); | |
| canonicalizeRange(NewLower, NewUpper); | |
| return ConstantFPRange(std::move(NewLower), std::move(NewUpper), MayBeQNaN, | |
| MayBeSNaN); | |
| } | |
| ConstantFPRange ConstantFPRange::cast(const fltSemantics &DstSem, | |
| APFloat::roundingMode RM) const { | |
| bool LosesInfo; | |
| APFloat NewLower = Lower; | |
| APFloat NewUpper = Upper; | |
| // For conservative, return full range if conversion is invalid. | |
| if (NewLower.convert(DstSem, RM, &LosesInfo) == APFloat::opInvalidOp || | |
| NewLower.isNaN()) | |
| return getFull(DstSem); | |
| if (NewUpper.convert(DstSem, RM, &LosesInfo) == APFloat::opInvalidOp || | |
| NewUpper.isNaN()) | |
| return getFull(DstSem); | |
| return ConstantFPRange(std::move(NewLower), std::move(NewUpper), | |
| /*MayBeQNaNVal=*/MayBeQNaN || MayBeSNaN, | |
| /*MayBeSNaNVal=*/false); | |
| } | |
| ConstantFPRange ConstantFPRange::add(const ConstantFPRange &Other) const { | |
| bool ResMayBeQNaN = ((MayBeQNaN || MayBeSNaN) && !Other.isEmptySet()) || | |
| ((Other.MayBeQNaN || Other.MayBeSNaN) && !isEmptySet()); | |
| if (isNaNOnly() || Other.isNaNOnly()) | |
| return getNaNOnly(getSemantics(), /*MayBeQNaN=*/ResMayBeQNaN, | |
| /*MayBeSNaN=*/false); | |
| bool LHSHasNegInf = false, LHSHasPosInf = false; | |
| APFloat LHSLower = Lower, LHSUpper = Upper; | |
| bool LHSFiniteIsNonEmpty = | |
| removeInf(LHSLower, LHSUpper, LHSHasPosInf, LHSHasNegInf); | |
| bool RHSHasNegInf = false, RHSHasPosInf = false; | |
| APFloat RHSLower = Other.Lower, RHSUpper = Other.Upper; | |
| bool RHSFiniteIsNonEmpty = | |
| removeInf(RHSLower, RHSUpper, RHSHasPosInf, RHSHasNegInf); | |
| // -inf + +inf = QNaN | |
| ResMayBeQNaN |= | |
| (LHSHasNegInf && RHSHasPosInf) || (LHSHasPosInf && RHSHasNegInf); | |
| // +inf + finite/+inf = +inf, -inf + finite/-inf = -inf | |
| bool HasNegInf = (LHSHasNegInf && (RHSFiniteIsNonEmpty || RHSHasNegInf)) || | |
| (RHSHasNegInf && (LHSFiniteIsNonEmpty || LHSHasNegInf)); | |
| bool HasPosInf = (LHSHasPosInf && (RHSFiniteIsNonEmpty || RHSHasPosInf)) || | |
| (RHSHasPosInf && (LHSFiniteIsNonEmpty || LHSHasPosInf)); | |
| if (LHSFiniteIsNonEmpty && RHSFiniteIsNonEmpty) { | |
| APFloat NewLower = | |
| HasNegInf ? APFloat::getInf(LHSLower.getSemantics(), /*Negative=*/true) | |
| : LHSLower + RHSLower; | |
| APFloat NewUpper = | |
| HasPosInf ? APFloat::getInf(LHSUpper.getSemantics(), /*Negative=*/false) | |
| : LHSUpper + RHSUpper; | |
| return ConstantFPRange(NewLower, NewUpper, ResMayBeQNaN, | |
| /*MayBeSNaN=*/false); | |
| } | |
| // If both HasNegInf and HasPosInf are false, the non-NaN part is empty. | |
| // We just return the canonical form [+inf, -inf] for the empty non-NaN set. | |
| return ConstantFPRange( | |
| APFloat::getInf(Lower.getSemantics(), /*Negative=*/HasNegInf), | |
| APFloat::getInf(Upper.getSemantics(), /*Negative=*/!HasPosInf), | |
| ResMayBeQNaN, | |
| /*MayBeSNaN=*/false); | |
| } | |
| ConstantFPRange ConstantFPRange::sub(const ConstantFPRange &Other) const { | |
| // fsub X, Y = fadd X, (fneg Y) | |
| return add(Other.negate()); | |
| } | |
| void ConstantFPRange::flushDenormals(DenormalMode::DenormalModeKind Mode) { | |
| if (Mode == DenormalMode::IEEE) | |
| return; | |
| FPClassTest Class = classify(); | |
| if (!(Class & fcSubnormal)) | |
| return; | |
| auto &Sem = getSemantics(); | |
| // PreserveSign: PosSubnormal -> PosZero, NegSubnormal -> NegZero | |
| // PositiveZero: PosSubnormal -> PosZero, NegSubnormal -> PosZero | |
| // Dynamic: PosSubnormal -> PosZero, NegSubnormal -> NegZero/PosZero | |
| bool ZeroLowerNegative = | |
| Mode != DenormalMode::PositiveZero && (Class & fcNegSubnormal); | |
| bool ZeroUpperNegative = | |
| Mode == DenormalMode::PreserveSign && !(Class & fcPosSubnormal); | |
| assert((ZeroLowerNegative || !ZeroUpperNegative) && | |
| "ZeroLower is greater than ZeroUpper."); | |
| Lower = minnum(Lower, APFloat::getZero(Sem, ZeroLowerNegative)); | |
| Upper = maxnum(Upper, APFloat::getZero(Sem, ZeroUpperNegative)); | |
| } | |
| /// Represent a contiguous range of values sharing the same sign. | |
| struct SameSignRange { | |
| bool HasZero; | |
| bool HasNonZero; | |
| bool HasInf; | |
| // The lower and upper bounds of the range (inclusive). | |
| // The sign is dropped and infinities are excluded. | |
| std::optional<std::pair<APFloat, APFloat>> FinitePart; | |
| explicit SameSignRange(const APFloat &Lower, const APFloat &Upper) | |
| : HasZero(Lower.isZero()), HasNonZero(!Upper.isZero()), | |
| HasInf(Upper.isInfinity()) { | |
| assert(!Lower.isNegative() && !Upper.isNegative() && | |
| "The sign should be dropped."); | |
| assert(strictCompare(Lower, Upper) != APFloat::cmpGreaterThan && | |
| "Empty set."); | |
| if (!Lower.isInfinity()) | |
| FinitePart = {Lower, | |
| HasInf ? APFloat::getLargest(Lower.getSemantics()) : Upper}; | |
| } | |
| }; | |
| /// Split the range into positive and negative components. | |
| static void splitPosNeg(const APFloat &Lower, const APFloat &Upper, | |
| std::optional<SameSignRange> &NegPart, | |
| std::optional<SameSignRange> &PosPart) { | |
| assert(strictCompare(Lower, Upper) != APFloat::cmpGreaterThan && | |
| "Non-NaN part is empty."); | |
| if (Lower.isNegative() == Upper.isNegative()) { | |
| if (Lower.isNegative()) | |
| NegPart = SameSignRange{abs(Upper), abs(Lower)}; | |
| else | |
| PosPart = SameSignRange{Lower, Upper}; | |
| return; | |
| } | |
| auto &Sem = Lower.getSemantics(); | |
| NegPart = SameSignRange{APFloat::getZero(Sem), abs(Lower)}; | |
| PosPart = SameSignRange{APFloat::getZero(Sem), Upper}; | |
| } | |
| ConstantFPRange ConstantFPRange::mul(const ConstantFPRange &Other) const { | |
| auto &Sem = getSemantics(); | |
| bool ResMayBeQNaN = ((MayBeQNaN || MayBeSNaN) && !Other.isEmptySet()) || | |
| ((Other.MayBeQNaN || Other.MayBeSNaN) && !isEmptySet()); | |
| if (isNaNOnly() || Other.isNaNOnly()) | |
| return getNaNOnly(Sem, /*MayBeQNaN=*/ResMayBeQNaN, | |
| /*MayBeSNaN=*/false); | |
| std::optional<SameSignRange> LHSNeg, LHSPos, RHSNeg, RHSPos; | |
| splitPosNeg(Lower, Upper, LHSNeg, LHSPos); | |
| splitPosNeg(Other.Lower, Other.Upper, RHSNeg, RHSPos); | |
| APFloat ResLower = APFloat::getInf(Sem, /*Negative=*/false); | |
| APFloat ResUpper = APFloat::getInf(Sem, /*Negative=*/true); | |
| auto Update = [&](std::optional<SameSignRange> &LHS, | |
| std::optional<SameSignRange> &RHS, bool Negative) { | |
| if (!LHS || !RHS) | |
| return; | |
| // 0 * inf = QNaN | |
| ResMayBeQNaN |= LHS->HasZero && RHS->HasInf; | |
| ResMayBeQNaN |= RHS->HasZero && LHS->HasInf; | |
| // NonZero * inf = inf | |
| if ((LHS->HasInf && RHS->HasNonZero) || (RHS->HasInf && LHS->HasNonZero)) | |
| (Negative ? ResLower : ResUpper) = APFloat::getInf(Sem, Negative); | |
| // Finite * Finite | |
| if (LHS->FinitePart && RHS->FinitePart) { | |
| APFloat NewLower = LHS->FinitePart->first * RHS->FinitePart->first; | |
| APFloat NewUpper = LHS->FinitePart->second * RHS->FinitePart->second; | |
| if (Negative) { | |
| ResLower = minnum(ResLower, -NewUpper); | |
| ResUpper = maxnum(ResUpper, -NewLower); | |
| } else { | |
| ResLower = minnum(ResLower, NewLower); | |
| ResUpper = maxnum(ResUpper, NewUpper); | |
| } | |
| } | |
| }; | |
| Update(LHSNeg, RHSNeg, /*Negative=*/false); | |
| Update(LHSNeg, RHSPos, /*Negative=*/true); | |
| Update(LHSPos, RHSNeg, /*Negative=*/true); | |
| Update(LHSPos, RHSPos, /*Negative=*/false); | |
| return ConstantFPRange(ResLower, ResUpper, ResMayBeQNaN, /*MayBeSNaN=*/false); | |
| } | |
| ConstantFPRange ConstantFPRange::div(const ConstantFPRange &Other) const { | |
| auto &Sem = getSemantics(); | |
| bool ResMayBeQNaN = ((MayBeQNaN || MayBeSNaN) && !Other.isEmptySet()) || | |
| ((Other.MayBeQNaN || Other.MayBeSNaN) && !isEmptySet()); | |
| if (isNaNOnly() || Other.isNaNOnly()) | |
| return getNaNOnly(Sem, /*MayBeQNaN=*/ResMayBeQNaN, | |
| /*MayBeSNaN=*/false); | |
| std::optional<SameSignRange> LHSNeg, LHSPos, RHSNeg, RHSPos; | |
| splitPosNeg(Lower, Upper, LHSNeg, LHSPos); | |
| splitPosNeg(Other.Lower, Other.Upper, RHSNeg, RHSPos); | |
| APFloat ResLower = APFloat::getInf(Sem, /*Negative=*/false); | |
| APFloat ResUpper = APFloat::getInf(Sem, /*Negative=*/true); | |
| auto Update = [&](std::optional<SameSignRange> &LHS, | |
| std::optional<SameSignRange> &RHS, bool Negative) { | |
| if (!LHS || !RHS) | |
| return; | |
| // inf / inf = QNaN 0 / 0 = QNaN | |
| ResMayBeQNaN |= LHS->HasInf && RHS->HasInf; | |
| ResMayBeQNaN |= LHS->HasZero && RHS->HasZero; | |
| // It is not straightforward to infer HasNonZeroFinite = HasFinite && | |
| // HasNonZero. By definitions we have: | |
| // HasFinite = HasNonZeroFinite || HasZero | |
| // HasNonZero = HasNonZeroFinite || HasInf | |
| // Since the range is contiguous, if both HasFinite and HasNonZero are true, | |
| // HasNonZeroFinite must be true. | |
| bool LHSHasNonZeroFinite = LHS->FinitePart && LHS->HasNonZero; | |
| bool RHSHasNonZeroFinite = RHS->FinitePart && RHS->HasNonZero; | |
| // inf / Finite = inf FiniteNonZero / 0 = inf | |
| if ((LHS->HasInf && RHS->FinitePart) || | |
| (LHSHasNonZeroFinite && RHS->HasZero)) | |
| (Negative ? ResLower : ResUpper) = APFloat::getInf(Sem, Negative); | |
| // Finite / inf = 0 | |
| if (LHS->FinitePart && RHS->HasInf) { | |
| APFloat Zero = APFloat::getZero(Sem, /*Negative=*/Negative); | |
| ResLower = minnum(ResLower, Zero); | |
| ResUpper = maxnum(ResUpper, Zero); | |
| } | |
| // Finite / FiniteNonZero | |
| if (LHS->FinitePart && RHSHasNonZeroFinite) { | |
| assert(!RHS->FinitePart->second.isZero() && | |
| "Divisor should be non-zero."); | |
| APFloat NewLower = LHS->FinitePart->first / RHS->FinitePart->second; | |
| APFloat NewUpper = LHS->FinitePart->second / | |
| (RHS->FinitePart->first.isZero() | |
| ? APFloat::getSmallest(Sem, /*Negative=*/false) | |
| : RHS->FinitePart->first); | |
| if (Negative) { | |
| ResLower = minnum(ResLower, -NewUpper); | |
| ResUpper = maxnum(ResUpper, -NewLower); | |
| } else { | |
| ResLower = minnum(ResLower, NewLower); | |
| ResUpper = maxnum(ResUpper, NewUpper); | |
| } | |
| } | |
| }; | |
| Update(LHSNeg, RHSNeg, /*Negative=*/false); | |
| Update(LHSNeg, RHSPos, /*Negative=*/true); | |
| Update(LHSPos, RHSNeg, /*Negative=*/true); | |
| Update(LHSPos, RHSPos, /*Negative=*/false); | |
| return ConstantFPRange(ResLower, ResUpper, ResMayBeQNaN, /*MayBeSNaN=*/false); | |
| } |