675 lines
25 KiB
C++
675 lines
25 KiB
C++
//===- 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);
|
|
}
|