diff --git a/llvm/include/llvm/Analysis/ValueLattice.h b/llvm/include/llvm/Analysis/ValueLattice.h index 9357a15f7619f1..3c5cfa7390815b 100644 --- a/llvm/include/llvm/Analysis/ValueLattice.h +++ b/llvm/include/llvm/Analysis/ValueLattice.h @@ -9,6 +9,7 @@ #ifndef LLVM_ANALYSIS_VALUELATTICE_H #define LLVM_ANALYSIS_VALUELATTICE_H +#include "llvm/IR/ConstantFPRange.h" #include "llvm/IR/ConstantRange.h" #include "llvm/IR/Constants.h" @@ -38,6 +39,7 @@ class ValueLatticeElement { /// Transition allowed to the following states: /// constant /// constantrange_including_undef + /// constantfprange_including_undef /// overdefined undef, @@ -70,6 +72,21 @@ class ValueLatticeElement { /// overdefined constantrange_including_undef, + /// The Value falls within this range. (Used only for floating point typed + /// values.) + /// Transition allowed to the following states: + /// constantfprange (new range must be a superset of the existing range) + /// constantfprange_including_undef + /// overdefined + constantfprange, + + /// This Value falls within this range, but also may be undef. + /// Merging it with other constant ranges results in + /// constantfprange_including_undef. + /// Transition allowed to the following states: + /// overdefined + constantfprange_including_undef, + /// We can not precisely model the dynamic values this value might take. /// No transitions are allowed after reaching overdefined. overdefined, @@ -85,6 +102,7 @@ class ValueLatticeElement { union { Constant *ConstVal; ConstantRange Range; + ConstantFPRange FPRange; }; /// Destroy contents of lattice value, without destructing the object. @@ -100,6 +118,10 @@ class ValueLatticeElement { case constantrange: Range.~ConstantRange(); break; + case constantfprange_including_undef: + case constantfprange: + FPRange.~ConstantFPRange(); + break; }; } @@ -154,6 +176,11 @@ class ValueLatticeElement { new (&Range) ConstantRange(Other.Range); NumRangeExtensions = Other.NumRangeExtensions; break; + case constantfprange: + case constantfprange_including_undef: + new (&FPRange) ConstantFPRange(Other.FPRange); + NumRangeExtensions = Other.NumRangeExtensions; + break; case constant: case notconstant: ConstVal = Other.ConstVal; @@ -173,6 +200,11 @@ class ValueLatticeElement { new (&Range) ConstantRange(std::move(Other.Range)); NumRangeExtensions = Other.NumRangeExtensions; break; + case constantfprange: + case constantfprange_including_undef: + new (&FPRange) ConstantFPRange(Other.FPRange); + NumRangeExtensions = Other.NumRangeExtensions; + break; case constant: case notconstant: ConstVal = Other.ConstVal; @@ -225,6 +257,23 @@ class ValueLatticeElement { MergeOptions().setMayIncludeUndef(MayIncludeUndef)); return Res; } + static ValueLatticeElement getFPRange(ConstantFPRange CR, + bool MayIncludeUndef = false) { + if (CR.isFullSet()) + return getOverdefined(); + + if (CR.isEmptySet()) { + ValueLatticeElement Res; + if (MayIncludeUndef) + Res.markUndef(); + return Res; + } + + ValueLatticeElement Res; + Res.markConstantFPRange(std::move(CR), + MergeOptions().setMayIncludeUndef(MayIncludeUndef)); + return Res; + } static ValueLatticeElement getOverdefined() { ValueLatticeElement Res; Res.markOverdefined(); @@ -239,6 +288,9 @@ class ValueLatticeElement { bool isConstantRangeIncludingUndef() const { return Tag == constantrange_including_undef; } + bool isConstantFPRangeIncludingUndef() const { + return Tag == constantfprange_including_undef; + } /// Returns true if this value is a constant range. Use \p UndefAllowed to /// exclude non-singleton constant ranges that may also be undef. Note that /// this function also returns true if the range may include undef, but only @@ -247,6 +299,16 @@ class ValueLatticeElement { return Tag == constantrange || (Tag == constantrange_including_undef && (UndefAllowed || Range.isSingleElement())); } + /// Returns true if this value is a constant floating point range. Use \p + /// UndefAllowed to exclude non-singleton constant ranges that may also be + /// undef. Note that this function also returns true if the range may include + /// undef, but only contains a single element. In that case, it can be + /// replaced by a constant. + bool isConstantFPRange(bool UndefAllowed = true) const { + return Tag == constantfprange || + (Tag == constantfprange_including_undef && + (UndefAllowed || FPRange.isSingleElement())); + } bool isOverdefined() const { return Tag == overdefined; } Constant *getConstant() const { @@ -269,6 +331,17 @@ class ValueLatticeElement { return Range; } + /// Returns the constant floating point range for this value. Use \p + /// UndefAllowed to exclude non-singleton constant ranges that may also be + /// undef. Note that this function also returns a range if the range may + /// include undef, but only contains a single element. In that case, it can be + /// replaced by a constant. + const ConstantFPRange &getConstantFPRange(bool UndefAllowed = true) const { + assert(isConstantFPRange(UndefAllowed) && + "Cannot get the constant-fprange of a non-constant-fprange!"); + return FPRange; + } + std::optional asConstantInteger() const { if (isConstant() && isa(getConstant())) { return cast(getConstant())->getValue(); @@ -278,6 +351,15 @@ class ValueLatticeElement { return std::nullopt; } + std::optional asConstantFP() const { + if (isConstant() && isa(getConstant())) { + return cast(getConstant())->getValue(); + } else if (isConstantFPRange() && getConstantFPRange().isSingleElement()) { + return *getConstantFPRange().getSingleElement(); + } + return std::nullopt; + } + ConstantRange asConstantRange(unsigned BW, bool UndefAllowed = false) const { if (isConstantRange(UndefAllowed)) return getConstantRange(); @@ -288,11 +370,28 @@ class ValueLatticeElement { return ConstantRange::getFull(BW); } + ConstantFPRange asConstantFPRange(const fltSemantics &Sem, + bool UndefAllowed = false) const { + if (isConstantFPRange(UndefAllowed)) + return getConstantFPRange(); + if (isConstant()) + return getConstant()->toConstantFPRange(); + if (isUnknown()) + return ConstantFPRange::getEmpty(Sem); + return ConstantFPRange::getFull(Sem); + } + ConstantRange asConstantRange(Type *Ty, bool UndefAllowed = false) const { assert(Ty->isIntOrIntVectorTy() && "Must be integer type"); return asConstantRange(Ty->getScalarSizeInBits(), UndefAllowed); } + ConstantFPRange asConstantFPRange(Type *Ty, bool UndefAllowed = false) const { + assert(Ty->isFPOrFPVectorTy() && "Must be floating point type"); + return asConstantFPRange(Ty->getScalarType()->getFltSemantics(), + UndefAllowed); + } + bool markOverdefined() { if (isOverdefined()) return false; @@ -394,6 +493,51 @@ class ValueLatticeElement { return true; } + /// Mark the object as constant floating point range with \p NewR. If the + /// object is already a constant range, nothing changes if the existing range + /// is equal to \p NewR and the tag. Otherwise \p NewR must be a superset of + /// the existing range or the object must be undef. The tag is set to + /// constant_range_including_undef if either the existing value or the new + /// range may include undef. + bool markConstantFPRange(ConstantFPRange NewR, + MergeOptions Opts = MergeOptions()) { + assert(!NewR.isEmptySet() && "should only be called for non-empty sets"); + + if (NewR.isFullSet()) + return markOverdefined(); + + ValueLatticeElementTy OldTag = Tag; + ValueLatticeElementTy NewTag = + (isUndef() || isConstantFPRangeIncludingUndef() || Opts.MayIncludeUndef) + ? constantfprange_including_undef + : constantfprange; + if (isConstantFPRange()) { + Tag = NewTag; + if (getConstantFPRange() == NewR) + return Tag != OldTag; + + // Simple form of widening. If a range is extended multiple times, go to + // overdefined. + if (Opts.CheckWiden && ++NumRangeExtensions > Opts.MaxWidenSteps) + return markOverdefined(); + + assert(NewR.contains(getConstantFPRange()) && + "Existing range must be a subset of NewR"); + FPRange = std::move(NewR); + return true; + } + + assert(isUnknown() || isUndef() || isConstant()); + assert( + (!isConstant() || NewR.contains(getConstant()->toConstantFPRange())) && + "Constant must be subset of new range"); + + NumRangeExtensions = 0; + Tag = NewTag; + new (&FPRange) ConstantFPRange(std::move(NewR)); + return true; + } + /// Updates this object to approximate both this object and RHS. Returns /// true if this object has been changed. bool mergeIn(const ValueLatticeElement &RHS, @@ -414,6 +558,9 @@ class ValueLatticeElement { if (RHS.isConstantRange()) return markConstantRange(RHS.getConstantRange(true), Opts.setMayIncludeUndef()); + if (RHS.isConstantFPRange()) + return markConstantFPRange(RHS.getConstantFPRange(true), + Opts.setMayIncludeUndef()); return markOverdefined(); } @@ -428,15 +575,26 @@ class ValueLatticeElement { return false; if (RHS.isUndef()) return false; - // If the constant is a vector of integers, try to treat it as a range. - if (getConstant()->getType()->isVectorTy() && - getConstant()->getType()->getScalarType()->isIntegerTy()) { - ConstantRange L = getConstant()->toConstantRange(); - ConstantRange NewR = L.unionWith( - RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true)); - return markConstantRange( - std::move(NewR), - Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef())); + // If the constant is a vector of integers/floating point values, try to + // treat it as a range. + if (getConstant()->getType()->isVectorTy()) { + Type *ScalarTy = getConstant()->getType()->getScalarType(); + if (ScalarTy->isIntegerTy()) { + ConstantRange L = getConstant()->toConstantRange(); + ConstantRange NewR = L.unionWith( + RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true)); + return markConstantRange( + std::move(NewR), + Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef())); + } + if (ScalarTy->isFloatingPointTy()) { + ConstantFPRange L = getConstant()->toConstantFPRange(); + ConstantFPRange NewR = L.unionWith( + RHS.asConstantFPRange(L.getSemantics(), /*UndefAllowed=*/true)); + return markConstantFPRange( + std::move(NewR), + Opts.setMayIncludeUndef(RHS.isConstantFPRangeIncludingUndef())); + } } markOverdefined(); return true; @@ -450,18 +608,35 @@ class ValueLatticeElement { } auto OldTag = Tag; - assert(isConstantRange() && "New ValueLattice type?"); - if (RHS.isUndef()) { - Tag = constantrange_including_undef; - return OldTag != Tag; + if (isConstantRange()) { + if (RHS.isUndef()) { + Tag = constantrange_including_undef; + return OldTag != Tag; + } + + const ConstantRange &L = getConstantRange(); + ConstantRange NewR = L.unionWith( + RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true)); + return markConstantRange( + std::move(NewR), + Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef())); } - const ConstantRange &L = getConstantRange(); - ConstantRange NewR = L.unionWith( - RHS.asConstantRange(L.getBitWidth(), /*UndefAllowed=*/true)); - return markConstantRange( - std::move(NewR), - Opts.setMayIncludeUndef(RHS.isConstantRangeIncludingUndef())); + if (isConstantFPRange()) { + if (RHS.isUndef()) { + Tag = constantfprange_including_undef; + return OldTag != Tag; + } + + const ConstantFPRange &L = getConstantFPRange(); + ConstantFPRange NewR = L.unionWith( + RHS.asConstantFPRange(L.getSemantics(), /*UndefAllowed=*/true)); + return markConstantFPRange( + std::move(NewR), + Opts.setMayIncludeUndef(RHS.isConstantFPRangeIncludingUndef())); + } else { + llvm_unreachable("New ValueLattice type?"); + } } // Compares this symbolic value with Other using Pred and returns either @@ -492,7 +667,7 @@ class ValueLatticeElement { void setNumRangeExtensions(unsigned N) { NumRangeExtensions = N; } }; -static_assert(sizeof(ValueLatticeElement) <= 40, +static_assert(sizeof(ValueLatticeElement) <= 80, "size of ValueLatticeElement changed unexpectedly"); raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val); diff --git a/llvm/include/llvm/IR/Constant.h b/llvm/include/llvm/IR/Constant.h index 0aefb5ecf6b7f2..d6bde764d36d77 100644 --- a/llvm/include/llvm/IR/Constant.h +++ b/llvm/include/llvm/IR/Constant.h @@ -20,6 +20,7 @@ namespace llvm { class ConstantRange; +class ConstantFPRange; class APInt; /// This is an important base class in LLVM. It provides the common facilities @@ -159,6 +160,11 @@ class Constant : public User { /// range is the union over the element ranges. Poison elements are ignored. ConstantRange toConstantRange() const; + /// Convert constant to an approximate constant floating point range. For + /// vectors, the range is the union over the element ranges. Poison elements + /// are ignored. + ConstantFPRange toConstantFPRange() const; + /// Called if some element of this constant is no longer valid. /// At this point only other constants may be on the use_list for this /// constant. Any constants on our Use list must also be destroy'd. The diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp index 30dc4ae30dbfa5..73ff6825ba7066 100644 --- a/llvm/lib/Analysis/LazyValueInfo.cpp +++ b/llvm/lib/Analysis/LazyValueInfo.cpp @@ -77,6 +77,9 @@ static bool hasSingleValue(const ValueLatticeElement &Val) { Val.getConstantRange().isSingleElement()) // Integer constants are single element ranges return true; + if (Val.isConstantFPRange() && Val.getConstantFPRange().isSingleElement()) + // Floating point constants are single element ranges + return true; if (Val.isConstant()) // Non integer constants return true; @@ -1685,6 +1688,11 @@ Constant *LazyValueInfo::getConstant(Value *V, Instruction *CxtI) { if (const APInt *SingleVal = CR.getSingleElement()) return ConstantInt::get(V->getType(), *SingleVal); } + if (Result.isConstantFPRange()) { + const ConstantFPRange &CR = Result.getConstantFPRange(); + if (const APFloat *SingleVal = CR.getSingleElement()) + return ConstantFP::get(V->getType(), *SingleVal); + } return nullptr; } @@ -1720,6 +1728,11 @@ Constant *LazyValueInfo::getConstantOnEdge(Value *V, BasicBlock *FromBB, if (const APInt *SingleVal = CR.getSingleElement()) return ConstantInt::get(V->getType(), *SingleVal); } + if (Result.isConstantFPRange()) { + const ConstantFPRange &CR = Result.getConstantFPRange(); + if (const APFloat *SingleVal = CR.getSingleElement()) + return ConstantFP::get(V->getType(), *SingleVal); + } return nullptr; } @@ -1751,6 +1764,15 @@ static Constant *getPredicateResult(CmpInst::Predicate Pred, Constant *C, return ConstantInt::getFalse(ResTy); return nullptr; } + if (Val.isConstantFPRange()) { + const ConstantFPRange &CR = Val.getConstantFPRange(); + ConstantFPRange RHS = C->toConstantFPRange(); + if (CR.fcmp(Pred, RHS)) + return ConstantInt::getTrue(ResTy); + if (CR.fcmp(CmpInst::getInversePredicate(Pred), RHS)) + return ConstantInt::getFalse(ResTy); + return nullptr; + } if (Val.isNotConstant()) { // If this is an equality comparison, we can try to fold it knowing that diff --git a/llvm/lib/Analysis/ValueLattice.cpp b/llvm/lib/Analysis/ValueLattice.cpp index 03810f1c554e5d..ba4b947f923c1c 100644 --- a/llvm/lib/Analysis/ValueLattice.cpp +++ b/llvm/lib/Analysis/ValueLattice.cpp @@ -40,15 +40,21 @@ ValueLatticeElement::getCompare(CmpInst::Predicate Pred, Type *Ty, // Integer constants are represented as ConstantRanges with single // elements. - if (!isConstantRange() || !Other.isConstantRange()) - return nullptr; - - const auto &CR = getConstantRange(); - const auto &OtherCR = Other.getConstantRange(); - if (CR.icmp(Pred, OtherCR)) - return ConstantInt::getTrue(Ty); - if (CR.icmp(CmpInst::getInversePredicate(Pred), OtherCR)) - return ConstantInt::getFalse(Ty); + if (isConstantRange() && Other.isConstantRange()) { + const auto &CR = getConstantRange(); + const auto &OtherCR = Other.getConstantRange(); + if (CR.icmp(Pred, OtherCR)) + return ConstantInt::getTrue(Ty); + if (CR.icmp(CmpInst::getInversePredicate(Pred), OtherCR)) + return ConstantInt::getFalse(Ty); + } else if (isConstantFPRange() && Other.isConstantFPRange()) { + const auto &CR = getConstantFPRange(); + const auto &OtherCR = Other.getConstantFPRange(); + if (CR.fcmp(Pred, OtherCR)) + return ConstantInt::getTrue(Ty); + if (CR.fcmp(CmpInst::getInversePredicate(Pred), OtherCR)) + return ConstantInt::getFalse(Ty); + } return nullptr; } @@ -57,6 +63,9 @@ static bool hasSingleValue(const ValueLatticeElement &Val) { if (Val.isConstantRange() && Val.getConstantRange().isSingleElement()) // Integer constants are single element ranges return true; + if (Val.isConstantFPRange() && Val.getConstantFPRange().isSingleElement()) + // Floating point constants are single element ranges + return true; return Val.isConstant(); } @@ -94,19 +103,30 @@ ValueLatticeElement::intersect(const ValueLatticeElement &Other) const { return Other; // Could be either constant range or not constant here. - if (!isConstantRange() || !Other.isConstantRange()) { - // TODO: Arbitrary choice, could be improved - return *this; + if (isConstantRange() && Other.isConstantRange()) { + // Intersect two constant ranges + ConstantRange Range = + getConstantRange().intersectWith(Other.getConstantRange()); + // Note: An empty range is implicitly converted to unknown or undef + // depending on MayIncludeUndef internally. + return ValueLatticeElement::getRange( + std::move(Range), /*MayIncludeUndef=*/isConstantRangeIncludingUndef() || + Other.isConstantRangeIncludingUndef()); + } + if (isConstantFPRange() && Other.isConstantFPRange()) { + // Intersect two constant ranges + ConstantFPRange Range = + getConstantFPRange().intersectWith(Other.getConstantFPRange()); + // Note: An empty range is implicitly converted to unknown or undef + // depending on MayIncludeUndef internally. + return ValueLatticeElement::getFPRange( + std::move(Range), + /*MayIncludeUndef=*/isConstantFPRangeIncludingUndef() || + Other.isConstantFPRangeIncludingUndef()); } - // Intersect two constant ranges - ConstantRange Range = - getConstantRange().intersectWith(Other.getConstantRange()); - // Note: An empty range is implicitly converted to unknown or undef depending - // on MayIncludeUndef internally. - return ValueLatticeElement::getRange( - std::move(Range), /*MayIncludeUndef=*/isConstantRangeIncludingUndef() || - Other.isConstantRangeIncludingUndef()); + // TODO: Arbitrary choice, could be improved + return *this; } raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val) { @@ -125,9 +145,15 @@ raw_ostream &operator<<(raw_ostream &OS, const ValueLatticeElement &Val) { << Val.getConstantRange(true).getLower() << ", " << Val.getConstantRange(true).getUpper() << ">"; + if (Val.isConstantFPRangeIncludingUndef()) + return OS << "constantfprange incl. undef " << Val.getConstantFPRange(true); + if (Val.isConstantRange()) return OS << "constantrange<" << Val.getConstantRange().getLower() << ", " << Val.getConstantRange().getUpper() << ">"; + if (Val.isConstantFPRange()) + return OS << "constantfprange " << Val.getConstantFPRange(true); + return OS << "constant<" << *Val.getConstant() << ">"; } } // end namespace llvm diff --git a/llvm/lib/IR/Constants.cpp b/llvm/lib/IR/Constants.cpp index fe3a086c5772de..76c73ffede6e81 100644 --- a/llvm/lib/IR/Constants.cpp +++ b/llvm/lib/IR/Constants.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/IR/BasicBlock.h" +#include "llvm/IR/ConstantFPRange.h" #include "llvm/IR/ConstantFold.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" @@ -1796,6 +1797,44 @@ ConstantRange Constant::toConstantRange() const { return ConstantRange::getFull(BitWidth); } +ConstantFPRange Constant::toConstantFPRange() const { + if (auto *CFP = dyn_cast(this)) + return ConstantFPRange(CFP->getValue()); + + const fltSemantics &Sem = getType()->getScalarType()->getFltSemantics(); + if (!getType()->isVectorTy()) + return ConstantFPRange::getFull(Sem); + + if (auto *CFP = + dyn_cast_or_null(getSplatValue(/*AllowPoison=*/true))) + return ConstantFPRange(CFP->getValue()); + + if (auto *CDV = dyn_cast(this)) { + ConstantFPRange CR = ConstantFPRange::getEmpty(Sem); + for (unsigned I = 0, E = CDV->getNumElements(); I < E; ++I) + CR = CR.unionWith(ConstantFPRange(CDV->getElementAsAPFloat(I))); + return CR; + } + + if (auto *CV = dyn_cast(this)) { + ConstantFPRange CR = ConstantFPRange::getEmpty(Sem); + for (unsigned I = 0, E = CV->getNumOperands(); I < E; ++I) { + Constant *Elem = CV->getOperand(I); + if (!Elem) + return ConstantFPRange::getFull(Sem); + if (isa(Elem)) + continue; + auto *CFP = dyn_cast(Elem); + if (!CFP) + return ConstantFPRange::getFull(Sem); + CR = CR.unionWith(ConstantFPRange(CFP->getValue())); + } + return CR; + } + + return ConstantFPRange::getFull(Sem); +} + //---- ConstantPointerNull::get() implementation. // diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index 101d60525f4161..31d2ca1fc2a4b6 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -47,7 +47,8 @@ namespace llvm { bool SCCPSolver::isConstant(const ValueLatticeElement &LV) { return LV.isConstant() || - (LV.isConstantRange() && LV.getConstantRange().isSingleElement()); + (LV.isConstantRange() && LV.getConstantRange().isSingleElement()) || + (LV.isConstantFPRange() && LV.getConstantFPRange().isSingleElement()); } bool SCCPSolver::isOverdefined(const ValueLatticeElement &LV) { @@ -1006,6 +1007,12 @@ Constant *SCCPInstVisitor::getConstant(const ValueLatticeElement &LV, if (CR.getSingleElement()) return ConstantInt::get(Ty, *CR.getSingleElement()); } + + if (LV.isConstantFPRange()) { + const auto &CR = LV.getConstantFPRange(); + if (CR.getSingleElement()) + return ConstantFP::get(Ty, *CR.getSingleElement()); + } return nullptr; }