Index: llvm/include/llvm/ADT/APFloat.h =================================================================== --- llvm/include/llvm/ADT/APFloat.h +++ llvm/include/llvm/ADT/APFloat.h @@ -273,7 +273,19 @@ }; static unsigned int semanticsPrecision(const fltSemantics &); + + // Return the encoded, unbiased mininimum exponent. static ExponentType semanticsMinExponent(const fltSemantics &); + + // Return the semantically meaningful mininimum exponent (i.e. the exponent of + // the smallest denormal). + static ExponentType semanticsMinExponentValue(const fltSemantics &); + + // Return the semantic exponent range (i.e. the exponent of the smallest + // denormal, and the exponent of the largest normal. + static std::pair + semanticsExponentRange(const fltSemantics &); + static ExponentType semanticsMaxExponent(const fltSemantics &); static unsigned int semanticsSizeInBits(const fltSemantics &); static unsigned int semanticsIntSizeInBits(const fltSemantics&, bool); Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -221,6 +221,178 @@ Intrinsic::ID getIntrinsicForCallSite(const CallBase &CB, const TargetLibraryInfo *TLI); +/// Track the known range for the exponent of a floating-point value. +struct KnownExponentRange { + using ExponentType = APFloat::ExponentType; + + /// The semantic minimimum exponent value (i.e. the most conservative choice + /// is the exponent of the smallest denormal) + ExponentType MinExp = APFloat::IEK_NaN; + + /// The maximum exponent value, ignoring infinities and NaNs. + ExponentType MaxExpFiniteOnly = APFloat::IEK_Inf - 1; + + /// The maximum exponent value, which include infinities and NaNs. This may + /// differ from MaxExpFiniteOnly in cases involving mixed fast math flag usage + /// and conversions. + ExponentType MaxExp = APFloat::IEK_Inf; + + /// Construct an KnownExponentRange with no knowledge. + constexpr KnownExponentRange() = default; + + /// Construct a KnownExponentRange with the provided finite range. + constexpr KnownExponentRange(std::pair Pair) + : MinExp(Pair.first), MaxExpFiniteOnly(Pair.second) {} + + /// Construct a KnownExponentRange with a finite range from \p Min to \p + /// MaxFiniteOnly. \p Max + constexpr KnownExponentRange(ExponentType Min, ExponentType MaxFiniteOnly, + ExponentType Max = APFloat::IEK_Inf) + : MinExp(Min), MaxExpFiniteOnly(MaxFiniteOnly), MaxExp(Max) { + assert(MaxFiniteOnly <= Max); + } + + /// Get an KnownexponentRange for a constant value. + KnownExponentRange(const APFloat &Val) { + ExponentType ExpVal = ilogb(Val); + if (ExpVal == APFloat::IEK_Zero) + ExpVal = 0; + else if (ExpVal == APFloat::IEK_NaN) + ExpVal = APFloat::IEK_Inf; + MinExp = MaxExp = MaxExpFiniteOnly = ExpVal; + + if (ExpVal == APFloat::IEK_Inf) { + // If we encounter a constant infinity, keep tracking the possible finite + // only range. This value may still be used in an assume finite context. + // Use the minimum value so anything else we union with wins. + MaxExpFiniteOnly = APFloat::IEK_NaN; + } + } + + /// Get a KnownExponentRange with the bounds for the floating-point type. + KnownExponentRange(const fltSemantics &Semantics) { + std::tie(MinExp, MaxExpFiniteOnly) = + APFloat::semanticsExponentRange(Semantics); + } + + /// Get a KnownExponentRange from \p Min to \p Max that assumes the result + /// cannot be infinite or NaN. + static constexpr KnownExponentRange getFiniteOnly(ExponentType Min, + ExponentType Max) { + assert(Max != APFloat::IEK_Inf); + return KnownExponentRange(Min, Max, Max); + } + + /// Get an KnownExponentRange for a literal 0. + static KnownExponentRange getZero() { + return KnownExponentRange(0, 0, 0); + } + + /// Get an KnownExponentRange for a literal infinity. + static KnownExponentRange getInf() { + return KnownExponentRange(APFloat::IEK_Inf, APFloat::IEK_NaN, + APFloat::IEK_Inf); + } + + /// Get an KnownExponentRange for a literal nan. + static KnownExponentRange getNaN() { + return getInf(); + } + + /// Get an KnownExponentRange for a value with an unknown exponent range, but + /// cannot be an infinity or NaN. + static KnownExponentRange getUnknownFinite() { + return KnownExponentRange(APFloat::IEK_NaN, APFloat::IEK_Inf - 1, + APFloat::IEK_Inf - 1); + } + + // Get a union identity value. + static KnownExponentRange getEmpty() { + return KnownExponentRange(APFloat::IEK_Inf, APFloat::IEK_NaN, + APFloat::IEK_NaN); + } + + /// Return true if this cannot be an infinity or NaN. + constexpr bool isFinite() const { + return MaxExp == MaxExpFiniteOnly && MaxExp != APFloat::IEK_Inf; + } + + /// Return true if this has no usable knowledge. + constexpr bool isUnknown() const { + return MinExp == APFloat::IEK_NaN; + } + + /// Return true if the range is unknown, but the value cannot be an infinity + /// or NaN. + constexpr bool isUnknownFinite() const { + return isUnknown() && isFinite(); + } + + ExponentType getMinExp() const { + return MinExp; + } + + ExponentType getMaxExp(bool FiniteOnly) const { + return FiniteOnly ? MaxExpFiniteOnly : MaxExp; + } + + /// Assume the range cannot include an infinity or NaN. + void knownFiniteOnly() { + // Even if we don't know the exponent we may find it can't be infinity. + if (MaxExpFiniteOnly == APFloat::IEK_Inf) + --MaxExpFiniteOnly; + MaxExp = MaxExpFiniteOnly; + } + + /// Apply FastMathFlags from \p FMFSource and return this. + KnownExponentRange &applyFlags(const SimplifyQuery &Q, + const Instruction *FMFSource); + + /// Perform range union. + KnownExponentRange &operator|=(KnownExponentRange RHS) { + MinExp = std::min(MinExp, RHS.MinExp); + MaxExpFiniteOnly = std::max(MaxExpFiniteOnly, RHS.MaxExpFiniteOnly); + MaxExp = std::max(std::max(MaxExp, MaxExpFiniteOnly), RHS.MaxExp); + return *this; + } + + constexpr bool operator==(KnownExponentRange RHS) const { + return MinExp == RHS.MinExp && MaxExp == RHS.MaxExp && + MaxExpFiniteOnly == RHS.MaxExpFiniteOnly; + } +}; + +constexpr KnownExponentRange operator|(KnownExponentRange LHS, + KnownExponentRange RHS) { + LHS |= RHS; + return LHS; +} + +/// Compute the known exponent range for a floating-point value. If nothing +/// useful could be determined for the value range, +/// KnownExponentRange::isUnknown() will be true. The exponent range will be the +/// full range implied by the the type's fltSemantics. If the finite range is +/// unknown, but it's know this cannot be an infinity, +/// KnownExponentRange::isFinite() and KnownExponentRange::isUnknown() will be +/// true. +KnownExponentRange computeKnownExponentRange(const Value *V, + const APInt &DemandedElts, + unsigned Depth, + const SimplifyQuery &Q); +KnownExponentRange computeKnownExponentRange(const Value *V, unsigned Depth, + const SimplifyQuery &Q); + +KnownExponentRange computeKnownExponentRange( + const Value *V, const APInt &DemandedElts, const DataLayout &DL, + unsigned Depth = 0, const TargetLibraryInfo *TLI = nullptr, + AssumptionCache *AC = nullptr, const Instruction *CxtI = nullptr, + const DominatorTree *DT = nullptr, bool UseInstrInfo = true); +KnownExponentRange computeKnownExponentRange( + const Value *V, const DataLayout &DL, unsigned Depth = 0, + const TargetLibraryInfo *TLI = nullptr, AssumptionCache *AC = nullptr, + const Instruction *CxtI = nullptr, const DominatorTree *DT = nullptr, + bool UseInstrInfo = true); + /// Returns a pair of values, which if passed to llvm.is.fpclass, returns the /// same result as an fcmp with the given operands. /// Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -4199,6 +4199,189 @@ return KnownFromAssume; } +KnownExponentRange & +KnownExponentRange::applyFlags(const SimplifyQuery &Q, + const Instruction *FMFSource) { + if (!Q.IIQ.UseInstrInfo) + return *this; + + FastMathFlags FMF = cast(FMFSource)->getFastMathFlags(); + if (FMF.noNaNs() && FMF.noInfs()) + knownFiniteOnly(); + return *this; +} + +/// Compute the known exponent range for a floating-point value. +KnownExponentRange llvm::computeKnownExponentRange(const Value *V, + const APInt &DemandedElts, + unsigned Depth, + const SimplifyQuery &Q) { + if (!DemandedElts) { + // No demanded elts, better to assume we don't know anything. + return KnownExponentRange(); + } + + if (auto *CFP = dyn_cast(V)) + return KnownExponentRange(CFP->getValueAPF()); + + auto *VFVTy = dyn_cast(V->getType()); + const Constant *CV = dyn_cast(V); + if (VFVTy && CV) { + KnownExponentRange Known = KnownExponentRange::getEmpty(); + const unsigned NumElts = VFVTy->getNumElements(); + for (unsigned i = 0; i != NumElts; ++i) { + if (!DemandedElts[i]) + continue; + auto *CElt = dyn_cast_or_null(CV->getAggregateElement(i)); + if (!CElt) + return KnownExponentRange(); + Known |= KnownExponentRange(CElt->getValueAPF()); + } + + return Known; + } + + if (isa(V)) + return KnownExponentRange::getZero(); + + // All recursive calls that increase depth must come after this. + if (Depth == MaxAnalysisRecursionDepth) + return KnownExponentRange(); + + const Instruction *Op = dyn_cast(V); + if (!Op) + return KnownExponentRange(); + + switch (Op->getOpcode()) { + case Instruction::FNeg: + return computeKnownExponentRange(Op->getOperand(0), DemandedElts, Depth + 1, + Q) + .applyFlags(Q, Op); + case Instruction::Freeze: + return computeKnownExponentRange(Op->getOperand(0), DemandedElts, Depth + 1, + Q); + case Instruction::Select: { + KnownExponentRange KnownRHS = computeKnownExponentRange( + Op->getOperand(2), DemandedElts, Depth + 1, Q); + if (KnownRHS.isUnknown()) + return KnownRHS.applyFlags(Q, Op); + + KnownExponentRange KnownLHS = computeKnownExponentRange( + Op->getOperand(1), DemandedElts, Depth + 1, Q); + KnownLHS |= KnownRHS; + return KnownLHS.applyFlags(Q, Op); + } + case Instruction::FPExt: { + const Value *Src = Op->getOperand(0); + KnownExponentRange KnownSrc = + computeKnownExponentRange(Src, DemandedElts, Depth + 1, Q); + if (KnownSrc.isUnknown()) + return KnownExponentRange( + Src->getType()->getScalarType()->getFltSemantics()); + return KnownSrc; + } + case Instruction::Call: { + const CallInst *CI = cast(Op); + const Intrinsic::ID IID = CI->getIntrinsicID(); + switch (IID) { + case Intrinsic::fabs: + case Intrinsic::copysign: + case Intrinsic::arithmetic_fence: + return computeKnownExponentRange(CI->getArgOperand(0), DemandedElts, + Depth + 1, Q) + .applyFlags(Q, CI); + case Intrinsic::canonicalize: + // TODO: Can raise minimum exponent if denormals are flushed. + return computeKnownExponentRange(CI->getArgOperand(0), DemandedElts, + Depth + 1, Q) + .applyFlags(Q, CI); + case Intrinsic::ldexp: + // TODO: Known source range plus known exponent of second argument. + case Intrinsic::experimental_constrained_fpext: + // TODO: Same as fpext + break; + case Intrinsic::floor: + case Intrinsic::ceil: + case Intrinsic::trunc: + case Intrinsic::rint: + case Intrinsic::nearbyint: + case Intrinsic::round: + case Intrinsic::roundeven: + // TODO: Rounding-to-integer cases + + case Intrinsic::minnum: + case Intrinsic::maxnum: + case Intrinsic::minimum: + case Intrinsic::maximum: + // TODO: Treat like select + + default: + break; + } + break; + } + case Instruction::ExtractValue: { + const ExtractValueInst *Extract = cast(Op); + ArrayRef Indices = Extract->getIndices(); + const Value *Src = Extract->getAggregateOperand(); + if (isa(Src->getType()) && Indices.size() == 1 && + Indices[0] == 0) { + if (const auto *CI = dyn_cast(Src)) { + switch (CI->getIntrinsicID()) { + case Intrinsic::frexp: + return KnownExponentRange(-1, 1); + default: + break; + } + } + } + + break; + } + case Instruction::SIToFP: + case Instruction::UIToFP: + // TODO: Check from known range of integer. + break; + case Instruction::ExtractElement: + case Instruction::InsertElement: + case Instruction::ShuffleVector: + // TODO: Handle vectors + break; + case Instruction::PHI: + // TODO: + break; + default: + break; + } + + // TODO: Increase minimum exponent range if computeKnownFPClass says it can't + // be denormal. + + return KnownExponentRange(); +} + +KnownExponentRange llvm::computeKnownExponentRange( + const Value *V, const APInt &DemandedElts, const DataLayout &DL, + unsigned Depth, const TargetLibraryInfo *TLI, AssumptionCache *AC, + const Instruction *CxtI, const DominatorTree *DT, bool UseInstrInfo) { + return computeKnownExponentRange( + V, DemandedElts, Depth, + SimplifyQuery(DL, TLI, DT, AC, safeCxtI(V, CxtI), UseInstrInfo)); +} + +KnownExponentRange +llvm::computeKnownExponentRange(const Value *V, const DataLayout &DL, + unsigned Depth, const TargetLibraryInfo *TLI, + AssumptionCache *AC, const Instruction *CxtI, + const DominatorTree *DT, bool UseInstrInfo) { + auto *FVTy = dyn_cast(V->getType()); + APInt DemandedElts = + FVTy ? APInt::getAllOnes(FVTy->getNumElements()) : APInt(1, 1); + return computeKnownExponentRange( + V, DemandedElts, Depth, + SimplifyQuery(DL, TLI, DT, AC, safeCxtI(V, CxtI), UseInstrInfo)); +} + void computeKnownFPClass(const Value *V, const APInt &DemandedElts, FPClassTest InterestedClasses, KnownFPClass &Known, unsigned Depth, const SimplifyQuery &Q); Index: llvm/lib/Support/APFloat.cpp =================================================================== --- llvm/lib/Support/APFloat.cpp +++ llvm/lib/Support/APFloat.cpp @@ -300,6 +300,18 @@ APFloatBase::semanticsMinExponent(const fltSemantics &semantics) { return semantics.minExponent; } + +APFloatBase::ExponentType +APFloatBase::semanticsMinExponentValue(const fltSemantics &semantics) { + return semantics.minExponent - semantics.precision + 1; +} + +std::pair +APFloatBase::semanticsExponentRange(const fltSemantics &semantics) { + return {semanticsMinExponentValue(semantics), + semanticsMaxExponent(semantics)}; +} + unsigned int APFloatBase::semanticsSizeInBits(const fltSemantics &semantics) { return semantics.sizeInBits; } Index: llvm/unittests/Analysis/ValueTrackingTest.cpp =================================================================== --- llvm/unittests/Analysis/ValueTrackingTest.cpp +++ llvm/unittests/Analysis/ValueTrackingTest.cpp @@ -126,6 +126,8 @@ EXPECT_EQ(SignBitKnown, Known.SignBit); } }; + +class ComputeKnownExponentRangeTest : public ValueTrackingTest {}; } TEST_F(MatchSelectPatternTest, SimpleFMin) { @@ -1964,6 +1966,470 @@ } } +TEST_F(ComputeKnownExponentRangeTest, SelectConstantFPScalar) { + parseAssembly("define float @test(i1 %cond) {\n" + " %A = select i1 %cond, float 0.0, float -0.0\n" + " %A2 = select i1 %cond, float 1.0, float 2.0\n" + " %A3 = select i1 %cond, float -42.0, float 2.0\n" + " %A4 = select i1 %cond, float 16.0, float 0.0\n" + " %A5 = select i1 %cond, float 0.0, float 0.25\n" + " %A6 = select i1 %cond, float poison, float 2.0\n" + " %A7 = select i1 %cond, float 2.0, float poison\n" + " ret float %A\n" + "}\n"); + + { + KnownExponentRange AResult = + computeKnownExponentRange(A, M->getDataLayout()); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(0, 0), AResult); + EXPECT_TRUE(AResult.isFinite()); + EXPECT_FALSE(AResult.isUnknown()); + EXPECT_FALSE(AResult.isUnknownFinite()); + EXPECT_EQ(0, AResult.getMinExp()); + EXPECT_EQ(0, AResult.getMaxExp(true)); + EXPECT_EQ(0, AResult.getMaxExp(false)); + } + + EXPECT_EQ(KnownExponentRange::getFiniteOnly(0, 1), + computeKnownExponentRange(A2, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(1, 5), + computeKnownExponentRange(A3, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(0, 4), + computeKnownExponentRange(A4, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(-2, 0), + computeKnownExponentRange(A5, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A6, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A7, M->getDataLayout())); +} + +TEST_F(ComputeKnownExponentRangeTest, SelectFlags) { + parseAssembly( + "define float @test(i1 %cond, float %unknown0, float %unknown1) {\n" + " %A = select i1 %cond, float %unknown0, float %unknown1\n" + " %A2 = select nnan i1 %cond, float %unknown0, float %unknown1\n" + " %A3 = select ninf i1 %cond, float %unknown0, float %unknown1\n" + " %A4 = select nnan ninf i1 %cond, float %unknown0, float %unknown1\n" + " %A5 = select nnan ninf i1 %cond, float %unknown0, float 1.0\n" + " %A6 = select nnan ninf i1 %cond, float 1.0, float %unknown0\n" + " ret float %A\n" + "}\n"); + KnownExponentRange AResult = computeKnownExponentRange(A, M->getDataLayout()); + EXPECT_TRUE(AResult.isUnknown()); + EXPECT_FALSE(AResult.isUnknownFinite()); + EXPECT_FALSE(AResult.isFinite()); + EXPECT_EQ(KnownExponentRange(), AResult); + + EXPECT_EQ(AResult, computeKnownExponentRange(A2, M->getDataLayout())); + EXPECT_EQ(AResult, computeKnownExponentRange(A3, M->getDataLayout())); + + KnownExponentRange A4Result = + computeKnownExponentRange(A4, M->getDataLayout()); + EXPECT_TRUE(A4Result.isFinite()); + EXPECT_TRUE(A4Result.isUnknownFinite()); + EXPECT_TRUE(A4Result.isUnknown()); + EXPECT_EQ(KnownExponentRange::getUnknownFinite(), A4Result); + + EXPECT_EQ(A4Result, computeKnownExponentRange(A5, M->getDataLayout())); + EXPECT_EQ(A4Result, computeKnownExponentRange(A6, M->getDataLayout())); + + // Check UseInstrInfo=false ignores the flags + + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A4, M->getDataLayout(), 0, nullptr, + nullptr, nullptr, nullptr, + /*UseInstrInfo=*/false)); + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A5, M->getDataLayout(), 0, nullptr, + nullptr, nullptr, nullptr, + /*UseInstrInfo=*/false)); + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A6, M->getDataLayout(), 0, nullptr, + nullptr, nullptr, nullptr, + /*UseInstrInfo=*/false)); +} + +TEST_F(ComputeKnownExponentRangeTest, SelectConstantFPScalarLimits) { + parseAssembly("define float @test(i1 %cond) {\n" + " %A = select i1 %cond, float 0x36A0000000000000, float " + "0xB6A0000000000000\n" + " %A2 = select i1 %cond, float 0x381FFFFFE0000000, float " + "0xB81FFFFFE0000000\n" + " %A3 = select i1 %cond, float 0x3810000000000000, float " + "0xB810000000000000\n" + " %A4 = select i1 %cond, float 0x47EFFFFFE0000000, float " + "0xC7EFFFFFE0000000\n" + " %A5 = select i1 %cond, float 0x7FF0000000000000, float " + "0xFFF0000000000000\n" + " %A6 = select i1 %cond, float 0x7FF8000000000000, float " + "0xFFF8000000000000\n" + " %A7 = select i1 %cond, float 0x7FF4000000000000, float " + "0xFFF4000000000000\n" + " ret float %A\n" + "}\n"); + + // Smallest denormal + EXPECT_EQ(KnownExponentRange::getFiniteOnly(-149, -149), + computeKnownExponentRange(A, M->getDataLayout())); + + // Largest denormal + EXPECT_EQ(KnownExponentRange::getFiniteOnly(-126, -126), + computeKnownExponentRange(A2, M->getDataLayout())); + + // Smallest normal + EXPECT_EQ(KnownExponentRange::getFiniteOnly(-126, -126), + computeKnownExponentRange(A3, M->getDataLayout())); + + // Largest normal + EXPECT_EQ(KnownExponentRange::getFiniteOnly(127, 127), + computeKnownExponentRange(A4, M->getDataLayout())); + + // Infinity + EXPECT_EQ(KnownExponentRange::getInf(), + computeKnownExponentRange(A5, M->getDataLayout())); + + // qnan + EXPECT_EQ(KnownExponentRange::getNaN(), + computeKnownExponentRange(A6, M->getDataLayout())); + + // snan + EXPECT_EQ(KnownExponentRange::getNaN(), + computeKnownExponentRange(A7, M->getDataLayout())); +} + +TEST_F(ComputeKnownExponentRangeTest, SelectConstantFPVector) { + parseAssembly("define <2 x float> @test(i1 %cond) {\n" + " %A = select i1 %cond, <2 x float> zeroinitializer, <2 x " + "float> \n" + " %A2 = select i1 %cond, <2 x float> , " + "<2 x float> \n" + " %A3 = select i1 %cond, <2 x float> , <2 x float> \n" + " %A4 = select i1 %cond, <2 x float> , <2 x float> zeroinitializer\n" + " %A5 = select i1 %cond, <2 x float> zeroinitializer, <2 x " + "float> \n" + " %A6 = select i1 %cond, <2 x float> zeroinitializer, <2 x " + "float> \n" + " %A7 = select i1 %cond, <2 x float> , <2 x float> \n" + " ret <2 x float> %A\n" + "}\n"); + + { + KnownExponentRange AResult = + computeKnownExponentRange(A, M->getDataLayout()); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(0, 0), AResult); + EXPECT_TRUE(AResult.isFinite()); + EXPECT_FALSE(AResult.isUnknown()); + EXPECT_FALSE(AResult.isUnknownFinite()); + } + + EXPECT_EQ(KnownExponentRange::getFiniteOnly(0, 1), + computeKnownExponentRange(A2, M->getDataLayout())); + + { + KnownExponentRange A3Result = + computeKnownExponentRange(A3, M->getDataLayout()); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(1, 7), A3Result); + EXPECT_EQ(1, A3Result.getMinExp()); + EXPECT_EQ(7, A3Result.getMaxExp(true)); + EXPECT_EQ(7, A3Result.getMaxExp(false)); + } + + EXPECT_EQ(KnownExponentRange::getFiniteOnly(0, 4), + computeKnownExponentRange(A4, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(-2, 0), + computeKnownExponentRange(A5, M->getDataLayout())); + { + KnownExponentRange A6Result = + computeKnownExponentRange(A6, M->getDataLayout()); + EXPECT_TRUE(A6Result.isUnknown()); + EXPECT_FALSE(A6Result.isFinite()); + EXPECT_FALSE(A6Result.isUnknownFinite()); + EXPECT_EQ(KnownExponentRange(), A6Result); + } + + { + KnownExponentRange A7Result = + computeKnownExponentRange(A7, M->getDataLayout()); + EXPECT_TRUE(A7Result.isUnknown()); + EXPECT_FALSE(A7Result.isFinite()); + EXPECT_FALSE(A7Result.isUnknownFinite()); + EXPECT_EQ(KnownExponentRange(), A7Result); + } +} + +TEST_F(ComputeKnownExponentRangeTest, SelectConstantFPVector2) { + parseAssembly( + "define <2 x float> @test(i1 %cond) {\n" + " %A = select i1 %cond, <2 x float> , <2 x float> " + "\n" + " %A2 = select i1 %cond, <2 x float> , <2 x float> \n" + " %A3 = select i1 %cond, <2 x float> , <2 x float> zeroinitializer\n" + " %A4 = select i1 %cond, <2 x float> , <2 x " + "float> \n" + " ret <2 x float> %A\n" + "}\n"); + + KnownExponentRange AResult = computeKnownExponentRange(A, M->getDataLayout()); + EXPECT_FALSE(AResult.isUnknown()); + EXPECT_FALSE(AResult.isUnknownFinite()); + EXPECT_FALSE(AResult.isFinite()); + + EXPECT_EQ(KnownExponentRange(-2, 2, APFloat::IEK_Inf), AResult); + EXPECT_EQ(KnownExponentRange(3, 4, APFloat::IEK_Inf), + computeKnownExponentRange(A2, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(0, 0, APFloat::IEK_Inf), + computeKnownExponentRange(A3, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(1, 2, APFloat::IEK_Inf), + computeKnownExponentRange(A4, M->getDataLayout())); +} + +TEST_F(ComputeKnownExponentRangeTest, SelectConstantFPScalableVector) { + parseAssembly("define @test(i1 %cond, %arg) {\n" + " %A = select i1 %cond, zeroinitializer, " + " %arg\n" + " %A2 = select i1 %cond, %arg, zeroinitializer\n" + " %A3 = select i1 %cond, " + "zeroinitializer, poison\n" + " %A4 = select i1 %cond, " + "zeroinitializer, zeroinitializer\n" + " %A5 = select nnan ninf i1 %cond, %arg, " + " zeroinitializer\n" + " %A6 = select nnan i1 %cond, %arg, " + " zeroinitializer\n" + " %A7 = select ninf i1 %cond, %arg, " + " zeroinitializer\n" + " ret %A\n" + "}\n"); + + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A, M->getDataLayout())); + + KnownExponentRange A2Result = + computeKnownExponentRange(A2, M->getDataLayout()); + EXPECT_FALSE(A2Result.isFinite()); + EXPECT_TRUE(A2Result.isUnknown()); + + EXPECT_EQ(KnownExponentRange(), A2Result); + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A3, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(0, 0), + computeKnownExponentRange(A4, M->getDataLayout())); + + KnownExponentRange A5Result = + computeKnownExponentRange(A5, M->getDataLayout()); + EXPECT_TRUE(A5Result.isFinite()); + EXPECT_TRUE(A5Result.isUnknown()); + EXPECT_TRUE(A5Result.isUnknownFinite()); + EXPECT_EQ(KnownExponentRange::getUnknownFinite(), A5Result); + + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A6, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A7, M->getDataLayout())); +} + +TEST_F(ComputeKnownExponentRangeTest, FPExt) { + parseAssembly( + "define float @test(half %src.f16, float %src.f32, bfloat %src.bf16, <2 " + "x half> %src.v2f16, half nofpclass(inf nan) %src.f16.finite) {\n" + " %A = fpext half %src.f16 to float\n" + " %A2 = fpext half %src.f16 to double\n" + " %A3 = fpext float %src.f32 to double\n" + " %A4 = fpext bfloat %src.bf16 to float\n" + " %A5 = fpext bfloat %src.bf16 to double\n" + " %A6 = fpext <2 x half> %src.v2f16 to <2 x float>\n" + " %A7 = fpext half %src.f16.finite to float\n" + " ret float %A\n" + "}\n"); + + { + KnownExponentRange AResult = + computeKnownExponentRange(A, M->getDataLayout()); + EXPECT_EQ(KnownExponentRange(-24, 15), AResult); + EXPECT_EQ(-24, AResult.getMinExp()); + EXPECT_EQ(15, AResult.getMaxExp(true)); + EXPECT_EQ(APFloat::IEK_Inf, AResult.getMaxExp(false)); + } + + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A2, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-149, 127), + computeKnownExponentRange(A3, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-133, 127), + computeKnownExponentRange(A4, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-133, 127), + computeKnownExponentRange(A5, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A6, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-24, 15), // TODO: Could detect finite only + computeKnownExponentRange(A7, M->getDataLayout())); +} + +TEST_F(ComputeKnownExponentRangeTest, SimpleIntrinsics) { + parseAssembly( + "declare float @llvm.fabs.f32(float)\n" + "declare float @llvm.arithmetic.fence.f32(float)\n" + "declare float @llvm.copysign.f32(float, float)\n" + "define float @test(half %src.f16, float %arg1) {\n" + " %A = fpext half %src.f16 to float\n" + " %A2 = call float @llvm.fabs.f32(float %A)\n" + " %A3 = call float @llvm.arithmetic.fence.f32(float %A)\n" + " %A4 = call float @llvm.copysign.f32(float %A, float %arg1)\n" + " %A5 = call nnan ninf float @llvm.copysign.f32(float %A, float %arg1)\n" + " %A6 = call nnan ninf float @llvm.fabs.f32(float %A)\n" + " %A7 = call nnan ninf float @llvm.arithmetic.fence.f32(float %A)\n" + " ret float %A\n" + "}\n"); + + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A2, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A3, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A4, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(-24, 15), + computeKnownExponentRange(A5, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(-24, 15), + computeKnownExponentRange(A6, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(-24, 15), + computeKnownExponentRange(A7, M->getDataLayout())); + + // Check UseInstrInfo=false ignores the flags + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A5, M->getDataLayout(), 0, nullptr, + nullptr, nullptr, nullptr, + /*UseInstrInfo=*/false)); + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A6, M->getDataLayout(), 0, nullptr, + nullptr, nullptr, nullptr, + /*UseInstrInfo=*/false)); + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A7, M->getDataLayout(), 0, nullptr, + nullptr, nullptr, nullptr, + /*UseInstrInfo=*/false)); +} + +TEST_F(ComputeKnownExponentRangeTest, Canonicalize) { + parseAssembly( + "declare float @llvm.canonicalize.f32(float)\n" + "define float @test(half %src.f16, float %arg1) {\n" + " %A = fpext half %src.f16 to float\n" + " %A2 = call float @llvm.canonicalize.f32(float %A)\n" + " %A3 = call nnan ninf float @llvm.canonicalize.f32(float %A)\n" + " ret float %A\n" + "}\n"); + + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A2, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(-24, 15), + computeKnownExponentRange(A3, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A3, M->getDataLayout(), 0, nullptr, + nullptr, nullptr, nullptr, + /*UseInstrInfo=*/false)); +} + +TEST_F(ComputeKnownExponentRangeTest, UnaryOps) { + parseAssembly("define float @test(half %src.f16, float %src.f32) {\n" + " %A = fpext half %src.f16 to float\n" + " %A2 = freeze float %A\n" + " %A3 = freeze float %src.f32\n" + " %A4 = fneg float %A\n" + " %A5 = fneg float %src.f32\n" + " %A6 = fneg nnan ninf float %src.f32\n" + " %A7 = fneg nnan ninf float %A\n" + " ret float %A\n" + "}\n"); + + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A2, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A3, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A4, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A5, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange::getUnknownFinite(), + computeKnownExponentRange(A6, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange::getFiniteOnly(-24, 15), + computeKnownExponentRange(A7, M->getDataLayout())); + + EXPECT_EQ(KnownExponentRange(-24, 15), + computeKnownExponentRange(A7, M->getDataLayout(), 0, nullptr, + nullptr, nullptr, nullptr, + /*UseInstrInfo=*/false)); +} + +TEST_F(ComputeKnownExponentRangeTest, Frexp) { + parseAssembly( + "declare { float, i32 } @llvm.frexp.f32.i32(float)\n" + "declare { double, i32 } @llvm.frexp.f64.i32(double)\n" + "declare { <2 x float>, <2 x i32> } @llvm.frexp.v2f32.v2i32(<2 x " + "float>)\n" + "declare { <2 x double>, <2 x i32> } @llvm.frexp.v2f64.v2i32(<2 x " + "double>)\n" + "define float @test(float %arg.f32, double %arg.f64, <2 x float> " + "%arg.v2f32, <2 x double> %arg.v2f64) {\n" + " %frexp.f32.i32 = call { float, i32 } @llvm.frexp.f32.i32(float " + "%arg.f32)\n" + " %A = extractvalue { float, i32 } %frexp.f32.i32, 0\n" + " %frexp.f64.i32 = call { double, i32 } @llvm.frexp.f64.i32(double " + "%arg.f64)\n" + " %A2 = extractvalue { double, i32 } %frexp.f64.i32, 0\n" + " %frexp.v2f32.v2i32 = call { <2 x float>, <2 x i32> } " + "@llvm.frexp.v2f32.v2i32(<2 x float> %arg.v2f32)\n" + " %A3 = extractvalue { <2 x float>, <2 x i32> } %frexp.v2f32.v2i32, 0\n" + " %frexp.v2f64.v2i32 = call { <2 x double>, <2 x i32> } " + "@llvm.frexp.v2f64.v2i32(<2 x double> %arg.v2f64)\n" + " %A4 = extractvalue { <2 x double>, <2 x i32> } %frexp.v2f64.v2i32, 0\n" + " ret float %A\n" + "}\n"); + + EXPECT_EQ(KnownExponentRange(-1, 1), + computeKnownExponentRange(A, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-1, 1), + computeKnownExponentRange(A2, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-1, 1), + computeKnownExponentRange(A3, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(-1, 1), + computeKnownExponentRange(A4, M->getDataLayout())); +} + +TEST_F(ComputeKnownExponentRangeTest, ExtractValue) { + parseAssembly("define float @test({float, float} %arg0, [2 x float] %arg1, " + "i1 %cond) {\n" + " %A = extractvalue { float, float } %arg0, 1\n" + " %A2 = extractvalue [2 x float ] %arg1, 1\n" + " %A3 = extractvalue { <2 x float>, i32 } zeroinitializer, 0\n" + " %A4 = extractvalue [4 x float] zeroinitializer, 0\n" + " ret float %A\n" + "}\n"); + + // TODO: Better handle arbitrary extracts. + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A2, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A3, M->getDataLayout())); + EXPECT_EQ(KnownExponentRange(), + computeKnownExponentRange(A4, M->getDataLayout())); +} + TEST_F(ValueTrackingTest, isNonZeroRecurrence) { parseAssembly(R"( define i1 @test(i8 %n, i8 %r) {