diff --git a/llvm/include/llvm/Analysis/ValueLattice.h b/llvm/include/llvm/Analysis/ValueLattice.h index 262ff58f07dfd..eb30f237e7ab2 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" #include "llvm/Support/Compiler.h" @@ -39,6 +40,7 @@ class ValueLatticeElement { /// Transition allowed to the following states: /// constant /// constantrange_including_undef + /// constantfprange_including_undef /// overdefined undef, @@ -71,6 +73,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, @@ -86,6 +103,7 @@ class ValueLatticeElement { union { Constant *ConstVal; ConstantRange Range; + ConstantFPRange FPRange; }; /// Destroy contents of lattice value, without destructing the object. @@ -101,6 +119,10 @@ class ValueLatticeElement { case constantrange: Range.~ConstantRange(); break; + case constantfprange_including_undef: + case constantfprange: + FPRange.~ConstantFPRange(); + break; }; } @@ -155,6 +177,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; @@ -174,6 +201,11 @@ class ValueLatticeElement { new (&Range) ConstantRange(std::move(Other.Range)); NumRangeExtensions = Other.NumRangeExtensions; break; + case constantfprange: + case constantfprange_including_undef: + new (&FPRange) ConstantFPRange(std::move(Other.FPRange)); + NumRangeExtensions = Other.NumRangeExtensions; + break; case constant: case notconstant: ConstVal = Other.ConstVal; @@ -226,6 +258,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(); @@ -240,6 +289,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 @@ -248,6 +300,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 { @@ -270,6 +332,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(); @@ -279,6 +352,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(); @@ -289,11 +371,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; @@ -395,6 +494,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, @@ -415,6 +559,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(); } @@ -429,15 +576,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; @@ -451,18 +609,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 @@ -494,7 +669,7 @@ class ValueLatticeElement { void setNumRangeExtensions(unsigned N) { NumRangeExtensions = N; } }; -static_assert(sizeof(ValueLatticeElement) <= 40, +static_assert(sizeof(ValueLatticeElement) <= 64, "size of ValueLatticeElement changed unexpectedly"); LLVM_ABI raw_ostream &operator<<(raw_ostream &OS, diff --git a/llvm/include/llvm/IR/Constant.h b/llvm/include/llvm/IR/Constant.h index 0be1fc172ebd4..c87279444b092 100644 --- a/llvm/include/llvm/IR/Constant.h +++ b/llvm/include/llvm/IR/Constant.h @@ -21,6 +21,7 @@ namespace llvm { class ConstantRange; +class ConstantFPRange; class APInt; /// This is an important base class in LLVM. It provides the common facilities @@ -160,6 +161,11 @@ class Constant : public User { /// range is the union over the element ranges. Poison elements are ignored. LLVM_ABI 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 0e5bc481383a0..f259223e60d16 100644 --- a/llvm/lib/Analysis/LazyValueInfo.cpp +++ b/llvm/lib/Analysis/LazyValueInfo.cpp @@ -23,6 +23,7 @@ #include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/AssemblyAnnotationWriter.h" #include "llvm/IR/CFG.h" +#include "llvm/IR/ConstantFPRange.h" #include "llvm/IR/ConstantRange.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" @@ -80,6 +81,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; @@ -408,19 +412,27 @@ class LazyValueInfoImpl { BasicBlock *BB); std::optional getRangeFor(Value *V, Instruction *CxtI, BasicBlock *BB); + std::optional getFPRangeFor(Value *V, Instruction *CxtI, + BasicBlock *BB); std::optional solveBlockValueBinaryOpImpl( Instruction *I, BasicBlock *BB, std::function OpFn); std::optional solveBlockValueBinaryOp(BinaryOperator *BBI, BasicBlock *BB); + std::optional + solveBlockValueFPBinaryOp(BinaryOperator *BBI, BasicBlock *BB); std::optional solveBlockValueCast(CastInst *CI, BasicBlock *BB); + std::optional solveBlockValueFPCast(CastInst *CI, + BasicBlock *BB); std::optional solveBlockValueOverflowIntrinsic(WithOverflowInst *WO, BasicBlock *BB); std::optional solveBlockValueIntrinsic(IntrinsicInst *II, BasicBlock *BB); std::optional + solveBlockValueFPIntrinsic(IntrinsicInst *II, BasicBlock *BB); + std::optional solveBlockValueInsertElement(InsertElementInst *IEI, BasicBlock *BB); std::optional solveBlockValueExtractValue(ExtractValueInst *EVI, BasicBlock *BB); @@ -443,6 +455,9 @@ class LazyValueInfoImpl { std::optional getValueFromICmpCondition(Value *Val, ICmpInst *ICI, bool isTrueDest, bool UseBlockValue); + std::optional + getValueFromFCmpCondition(Value *Val, FCmpInst *FCI, bool IsTrueDest, + bool UseBlockValue); ValueLatticeElement getValueFromTrunc(Value *Val, TruncInst *Trunc, bool IsTrueDest); @@ -664,6 +679,26 @@ LazyValueInfoImpl::solveBlockValueImpl(Value *Val, BasicBlock *BB) { return solveBlockValueIntrinsic(II, BB); } + if (BBI->getType()->isFPOrFPVectorTy()) { + if (BBI->getOpcode() == Instruction::FNeg) { + std::optional OpRes = + getFPRangeFor(BBI->getOperand(0), BBI, BB); + if (!OpRes) + // More work to do before applying this transfer rule. + return std::nullopt; + return ValueLatticeElement::getFPRange(OpRes->negate()); + } + + if (auto *CI = dyn_cast(BBI)) + return solveBlockValueFPCast(CI, BB); + + if (BinaryOperator *BO = dyn_cast(BBI)) + return solveBlockValueFPBinaryOp(BO, BB); + + if (auto *II = dyn_cast(BBI)) + return solveBlockValueFPIntrinsic(II, BB); + } + LLVM_DEBUG(dbgs() << " compute BB '" << BB->getName() << "' - unknown inst def found.\n"); return getFromRangeMetadata(BBI); @@ -960,6 +995,14 @@ LazyValueInfoImpl::getRangeFor(Value *V, Instruction *CxtI, BasicBlock *BB) { return OptVal->asConstantRange(V->getType()); } +std::optional +LazyValueInfoImpl::getFPRangeFor(Value *V, Instruction *CxtI, BasicBlock *BB) { + std::optional OptVal = getBlockValue(V, BB, CxtI); + if (!OptVal) + return std::nullopt; + return OptVal->asConstantFPRange(V->getType()); +} + std::optional LazyValueInfoImpl::solveBlockValueCast(CastInst *CI, BasicBlock *BB) { // Filter out casts we don't know how to reason about before attempting to @@ -1000,6 +1043,47 @@ LazyValueInfoImpl::solveBlockValueCast(CastInst *CI, BasicBlock *BB) { return ValueLatticeElement::getRange(Res); } +static void postProcessFPRange(ConstantFPRange &R, + DenormalMode::DenormalModeKind Mode, + FastMathFlags FMF) { + if (FMF.noNaNs()) + R = R.getWithoutNaN(); + if (FMF.noInfs()) + R = R.getWithoutInf(); + R.flushDenormals(Mode); +} + +std::optional +LazyValueInfoImpl::solveBlockValueFPCast(CastInst *CI, BasicBlock *BB) { + // Filter out casts we don't know how to reason about before attempting to + // recurse on our operand. This can cut a long search short if we know we're + // not going to be able to get any useful information anways. + switch (CI->getOpcode()) { + case Instruction::FPExt: + case Instruction::FPTrunc: + break; + default: + // Unhandled instructions are overdefined. + LLVM_DEBUG(dbgs() << " compute BB '" << BB->getName() + << "' - overdefined (unknown cast).\n"); + return ValueLatticeElement::getOverdefined(); + } + + std::optional OpRes = + getFPRangeFor(CI->getOperand(0), CI, BB); + if (!OpRes) + // More work to do before applying this transfer rule. + return std::nullopt; + ConstantFPRange &Op = *OpRes; + FastMathFlags FMF = CI->getFastMathFlags(); + auto &Sem = CI->getDestTy()->getScalarType()->getFltSemantics(); + auto DenormalMode = BB->getParent()->getDenormalMode(Sem); + postProcessFPRange(Op, DenormalMode.Input, FMF); + ConstantFPRange Res = Op.cast(Sem); + postProcessFPRange(Res, DenormalMode.Output, FMF); + return ValueLatticeElement::getFPRange(std::move(Res)); +} + std::optional LazyValueInfoImpl::solveBlockValueBinaryOpImpl( Instruction *I, BasicBlock *BB, @@ -1158,6 +1242,46 @@ LazyValueInfoImpl::solveBlockValueBinaryOp(BinaryOperator *BO, BasicBlock *BB) { }); } +std::optional +LazyValueInfoImpl::solveBlockValueFPBinaryOp(BinaryOperator *BO, + BasicBlock *BB) { + FastMathFlags FMF = BO->getFastMathFlags(); + Function *F = BB->getParent(); + auto LHSRes = getFPRangeFor(BO->getOperand(0), BO, BB); + if (!LHSRes) + return std::nullopt; + auto RHSRes = getFPRangeFor(BO->getOperand(1), BO, BB); + if (!RHSRes) + return std::nullopt; + auto &LHSRange = *LHSRes; + auto &RHSRange = *RHSRes; + auto &Sem = LHSRange.getSemantics(); + auto DenormalMode = F->getDenormalMode(Sem); + postProcessFPRange(LHSRange, DenormalMode.Input, FMF); + postProcessFPRange(RHSRange, DenormalMode.Output, FMF); + ConstantFPRange Res = ConstantFPRange::getFull(Sem); + switch (BO->getOpcode()) { + case Instruction::FAdd: + Res = LHSRange.add(RHSRange); + break; + case Instruction::FSub: + Res = LHSRange.sub(RHSRange); + break; + case Instruction::FMul: + Res = LHSRange.mul(RHSRange); + break; + case Instruction::FDiv: + Res = LHSRange.div(RHSRange); + break; + case Instruction::FRem: + break; + default: + llvm_unreachable("Unknown FP binary operator."); + } + postProcessFPRange(Res, DenormalMode.Output, FMF); + return ValueLatticeElement::getFPRange(Res); +} + std::optional LazyValueInfoImpl::solveBlockValueOverflowIntrinsic(WithOverflowInst *WO, BasicBlock *BB) { @@ -1189,6 +1313,42 @@ LazyValueInfoImpl::solveBlockValueIntrinsic(IntrinsicInst *II, BasicBlock *BB) { .intersect(MetadataVal); } +std::optional +LazyValueInfoImpl::solveBlockValueFPIntrinsic(IntrinsicInst *II, + BasicBlock *BB) { + Intrinsic::ID IID = II->getIntrinsicID(); + switch (IID) { + case Intrinsic::fabs: + case Intrinsic::copysign: + break; + default: + return ValueLatticeElement::getOverdefined(); + } + SmallVector OpRanges; + for (Value *Op : II->args()) { + std::optional Range = getFPRangeFor(Op, II, BB); + if (!Range) + return std::nullopt; + OpRanges.push_back(*Range); + } + switch (IID) { + case Intrinsic::fabs: + return ValueLatticeElement::getFPRange(OpRanges[0].abs()); + case Intrinsic::copysign: { + ConstantFPRange Mag = OpRanges[0].abs(); + std::optional Sign = OpRanges[1].getSignBit(); + if (Sign.has_value()) { + if (*Sign) + return ValueLatticeElement::getFPRange(Mag.negate()); + return ValueLatticeElement::getFPRange(Mag); + } + return ValueLatticeElement::getFPRange(Mag.negate().unionWith(Mag)); + } + default: + llvm_unreachable("Unsupported FP intrinsic."); + } +} + std::optional LazyValueInfoImpl::solveBlockValueInsertElement(InsertElementInst *IEI, BasicBlock *BB) { @@ -1434,6 +1594,22 @@ std::optional LazyValueInfoImpl::getValueFromICmpCondition( return ValueLatticeElement::getOverdefined(); } +std::optional LazyValueInfoImpl::getValueFromFCmpCondition( + Value *Val, FCmpInst *FCI, bool IsTrueDest, bool UseBlockValue) { + Value *LHS = FCI->getOperand(0); + Value *RHS = FCI->getOperand(1); + + // Get the predicate that must hold along the considered edge. + CmpInst::Predicate EdgePred = + IsTrueDest ? FCI->getPredicate() : FCI->getInversePredicate(); + const APFloat *RHSC; + if (LHS == Val && match(RHS, m_APFloat(RHSC))) { + if (auto CR = ConstantFPRange::makeExactFCmpRegion(EdgePred, *RHSC)) + return ValueLatticeElement::getFPRange(*CR); + } + return ValueLatticeElement::getOverdefined(); +} + ValueLatticeElement LazyValueInfoImpl::getValueFromTrunc(Value *Val, TruncInst *Trunc, bool IsTrueDest) { @@ -1484,6 +1660,9 @@ LazyValueInfoImpl::getValueFromCondition(Value *Val, Value *Cond, if (ICmpInst *ICI = dyn_cast(Cond)) return getValueFromICmpCondition(Val, ICI, IsTrueDest, UseBlockValue); + if (FCmpInst *FCI = dyn_cast(Cond)) + return getValueFromFCmpCondition(Val, FCI, IsTrueDest, UseBlockValue); + if (auto *Trunc = dyn_cast(Cond)) return getValueFromTrunc(Val, Trunc, IsTrueDest); @@ -1973,6 +2152,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; } @@ -2008,6 +2192,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; } @@ -2039,6 +2228,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 03810f1c554e5..ba4b947f923c1 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 cbce8bd736102..f47a71348d12f 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" @@ -1830,6 +1831,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/Scalar/CorrelatedValuePropagation.cpp b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp index 4627f537dc16b..72570a689e5c7 100644 --- a/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp +++ b/llvm/lib/Transforms/Scalar/CorrelatedValuePropagation.cpp @@ -233,21 +233,22 @@ static Value *getValueOnEdge(LazyValueInfo *LVI, Value *Incoming, // value can never be that constant. In that case replace the incoming // value with the other value of the select. This often allows us to // remove the select later. + if (!SI->getType()->isFPOrFPVectorTy()) { + // The "false" case + if (auto *C = dyn_cast(SI->getFalseValue())) + if (auto *Res = dyn_cast_or_null(LVI->getPredicateOnEdge( + ICmpInst::ICMP_EQ, SI, C, From, To, CxtI)); + Res && Res->isZero()) + return SI->getTrueValue(); - // The "false" case - if (auto *C = dyn_cast(SI->getFalseValue())) - if (auto *Res = dyn_cast_or_null( - LVI->getPredicateOnEdge(ICmpInst::ICMP_EQ, SI, C, From, To, CxtI)); - Res && Res->isZero()) - return SI->getTrueValue(); - - // The "true" case, - // similar to the select "false" case, but try the select "true" value - if (auto *C = dyn_cast(SI->getTrueValue())) - if (auto *Res = dyn_cast_or_null( - LVI->getPredicateOnEdge(ICmpInst::ICMP_EQ, SI, C, From, To, CxtI)); - Res && Res->isZero()) - return SI->getFalseValue(); + // The "true" case, + // similar to the select "false" case, but try the select "true" value + if (auto *C = dyn_cast(SI->getTrueValue())) + if (auto *Res = dyn_cast_or_null(LVI->getPredicateOnEdge( + ICmpInst::ICMP_EQ, SI, C, From, To, CxtI)); + Res && Res->isZero()) + return SI->getFalseValue(); + } return nullptr; } diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp index 9693ae6b8ceb5..1d28bd91d4776 100644 --- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp +++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp @@ -53,7 +53,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) { @@ -1148,6 +1149,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; }