Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -306,6 +306,8 @@ static constexpr FPClassTest OrderedLessThanZeroMask = fcNegSubnormal | fcNegNormal | fcNegInf; + static constexpr FPClassTest OrderedGreaterThanZeroMask = + fcPosSubnormal | fcPosNormal | fcPosInf; /// Return true if it's know this can never be interpreted as a negative zero. bool isKnownNeverLogicalNegZero(const Function &F, Type *Ty) const; @@ -325,6 +327,17 @@ return isKnownNever(OrderedLessThanZeroMask); } + /// Return true if we can prove that the analyzed floating-point value is + /// either NaN or never greater than -0.0. + /// NaN --> true + /// +0 --> true + /// -0 --> true + /// x > +0 --> false + /// x < -0 --> true + bool cannotBeOrderedGreaterThanZero() const { + return isKnownNever(OrderedGreaterThanZeroMask); + } + KnownFPClass &operator|=(const KnownFPClass &RHS) { KnownFPClasses = KnownFPClasses | RHS.KnownFPClasses; Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -4369,23 +4369,55 @@ Known.knownNot(fcNan); break; } - case Intrinsic::minnum: + case Intrinsic::maxnum: + case Intrinsic::minnum: case Intrinsic::minimum: case Intrinsic::maximum: { - KnownFPClass Known2; - computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedClasses, - Known, Depth + 1, Q, TLI); - computeKnownFPClass(II->getArgOperand(1), DemandedElts, InterestedClasses, - Known2, Depth + 1, Q, TLI); + KnownFPClass KnownLHS, KnownRHS; + computeKnownFPClass(II->getArgOperand(0), DemandedElts, + InterestedClasses, KnownLHS, Depth + 1, Q, TLI); + computeKnownFPClass(II->getArgOperand(1), DemandedElts, + InterestedClasses, KnownRHS, Depth + 1, Q, TLI); - bool NeverNaN = Known.isKnownNeverNaN() || Known2.isKnownNeverNaN(); - Known |= Known2; + bool NeverNaN = + KnownLHS.isKnownNeverNaN() || KnownRHS.isKnownNeverNaN(); + Known = KnownLHS | KnownRHS; // If either operand is not NaN, the result is not NaN. if (NeverNaN && (IID == Intrinsic::minnum || IID == Intrinsic::maxnum)) Known.knownNot(fcNan); + if (IID == Intrinsic::maxnum) { + // If at least one operand is known to be positive, the result must be + // positive. + if ((KnownLHS.cannotBeOrderedLessThanZero() && + KnownLHS.isKnownNeverNaN()) || + (KnownRHS.cannotBeOrderedLessThanZero() && + KnownRHS.isKnownNeverNaN())) + Known.knownNot(KnownFPClass::OrderedLessThanZeroMask); + } else if (IID == Intrinsic::maximum) { + // If at least one operand is known to be positive, the result must be + // positive. + if (KnownLHS.cannotBeOrderedLessThanZero() || + KnownRHS.cannotBeOrderedLessThanZero()) + Known.knownNot(KnownFPClass::OrderedLessThanZeroMask); + } else if (IID == Intrinsic::minnum) { + // If at least one operand is known to be negative, the result must be + // negative. + if ((KnownLHS.cannotBeOrderedGreaterThanZero() && + KnownLHS.isKnownNeverNaN()) || + (KnownRHS.cannotBeOrderedGreaterThanZero() && + KnownRHS.isKnownNeverNaN())) + Known.knownNot(KnownFPClass::OrderedGreaterThanZeroMask); + } else { + // If at least one operand is known to be negative, the result must be + // negative. + if (KnownLHS.cannotBeOrderedGreaterThanZero() || + KnownRHS.cannotBeOrderedGreaterThanZero()) + Known.knownNot(KnownFPClass::OrderedGreaterThanZeroMask); + } + // Fixup zero handling if denormals could be returned as a zero. // // As there's no spec for denormal flushing, be conservative with the Index: llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll =================================================================== --- llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll +++ llvm/test/Transforms/Attributor/nofpclass-minimum-maximum.ll @@ -365,9 +365,9 @@ } define float @ret_minimum_nopos_nan__any(float nofpclass(pinf psub pnorm nan) %arg0, float %arg1) #3 { -; CHECK-LABEL: define float @ret_minimum_nopos_nan__any +; CHECK-LABEL: define nofpclass(pinf psub pnorm) float @ret_minimum_nopos_nan__any ; CHECK-SAME: (float nofpclass(nan pinf psub pnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.minimum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf psub pnorm) float @llvm.minimum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.minimum.f32(float %arg0, float %arg1) @@ -375,9 +375,9 @@ } define float @ret_minimum_any__nopos_nan(float %arg0, float nofpclass(pinf psub pnorm nan) %arg1) #3 { -; CHECK-LABEL: define float @ret_minimum_any__nopos_nan +; CHECK-LABEL: define nofpclass(pinf psub pnorm) float @ret_minimum_any__nopos_nan ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(nan pinf psub pnorm) [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.minimum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf psub pnorm) float @llvm.minimum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.minimum.f32(float %arg0, float %arg1) @@ -405,9 +405,9 @@ } define float @ret_minimum_nopos__any(float nofpclass(pinf psub pnorm) %arg0, float %arg1) #3 { -; CHECK-LABEL: define float @ret_minimum_nopos__any +; CHECK-LABEL: define nofpclass(pinf psub pnorm) float @ret_minimum_nopos__any ; CHECK-SAME: (float nofpclass(pinf psub pnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.minimum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf psub pnorm) float @llvm.minimum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.minimum.f32(float %arg0, float %arg1) @@ -415,9 +415,9 @@ } define float @ret_minimum_any__nopos(float %arg0, float nofpclass(pinf psub pnorm) %arg1) #3 { -; CHECK-LABEL: define float @ret_minimum_any__nopos +; CHECK-LABEL: define nofpclass(pinf psub pnorm) float @ret_minimum_any__nopos ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(pinf psub pnorm) [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.minimum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf psub pnorm) float @llvm.minimum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.minimum.f32(float %arg0, float %arg1) @@ -425,9 +425,9 @@ } define float @ret_maximum_noneg_nan__any(float nofpclass(ninf nsub nnorm nan) %arg0, float %arg1) #3 { -; CHECK-LABEL: define float @ret_maximum_noneg_nan__any +; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_maximum_noneg_nan__any ; CHECK-SAME: (float nofpclass(nan ninf nsub nnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.maximum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.maximum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.maximum.f32(float %arg0, float %arg1) @@ -435,9 +435,9 @@ } define float @ret_maximum_any__noneg_nan(float %arg0, float nofpclass(ninf nsub nnorm nan) %arg1) #3 { -; CHECK-LABEL: define float @ret_maximum_any__noneg_nan +; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_maximum_any__noneg_nan ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(nan ninf nsub nnorm) [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.maximum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.maximum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.maximum.f32(float %arg0, float %arg1) @@ -465,9 +465,9 @@ } define float @ret_maximum_noneg__any(float nofpclass(ninf nsub nnorm) %arg0, float %arg1) #3 { -; CHECK-LABEL: define float @ret_maximum_noneg__any +; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_maximum_noneg__any ; CHECK-SAME: (float nofpclass(ninf nsub nnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.maximum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.maximum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.maximum.f32(float %arg0, float %arg1) @@ -475,9 +475,9 @@ } define float @ret_maximum_any__noneg(float %arg0, float nofpclass(ninf nsub nnorm) %arg1) #3 { -; CHECK-LABEL: define float @ret_maximum_any__noneg +; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_maximum_any__noneg ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(ninf nsub nnorm) [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.maximum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf nsub nnorm) float @llvm.maximum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.maximum.f32(float %arg0, float %arg1) Index: llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll =================================================================== --- llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll +++ llvm/test/Transforms/Attributor/nofpclass-minnum-maxnum.ll @@ -365,9 +365,9 @@ } define float @ret_minnum_nopos_nan__any(float nofpclass(pinf psub pnorm nan) %arg0, float %arg1) #3 { -; CHECK-LABEL: define nofpclass(nan) float @ret_minnum_nopos_nan__any +; CHECK-LABEL: define nofpclass(nan pinf psub pnorm) float @ret_minnum_nopos_nan__any ; CHECK-SAME: (float nofpclass(nan pinf psub pnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan) float @llvm.minnum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan pinf psub pnorm) float @llvm.minnum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.minnum.f32(float %arg0, float %arg1) @@ -375,9 +375,9 @@ } define float @ret_minnum_any__nopos_nan(float %arg0, float nofpclass(pinf psub pnorm nan) %arg1) #3 { -; CHECK-LABEL: define nofpclass(nan) float @ret_minnum_any__nopos_nan +; CHECK-LABEL: define nofpclass(nan pinf psub pnorm) float @ret_minnum_any__nopos_nan ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(nan pinf psub pnorm) [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan) float @llvm.minnum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan pinf psub pnorm) float @llvm.minnum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.minnum.f32(float %arg0, float %arg1) @@ -425,9 +425,9 @@ } define float @ret_maxnum_noneg_nan__any(float nofpclass(ninf nsub nnorm nan) %arg0, float %arg1) #3 { -; CHECK-LABEL: define nofpclass(nan) float @ret_maxnum_noneg_nan__any +; CHECK-LABEL: define nofpclass(nan ninf nsub nnorm) float @ret_maxnum_noneg_nan__any ; CHECK-SAME: (float nofpclass(nan ninf nsub nnorm) [[ARG0:%.*]], float [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan) float @llvm.maxnum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan ninf nsub nnorm) float @llvm.maxnum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.maxnum.f32(float %arg0, float %arg1) @@ -435,9 +435,9 @@ } define float @ret_maxnum_any__noneg_nan(float %arg0, float nofpclass(ninf nsub nnorm nan) %arg1) #3 { -; CHECK-LABEL: define nofpclass(nan) float @ret_maxnum_any__noneg_nan +; CHECK-LABEL: define nofpclass(nan ninf nsub nnorm) float @ret_maxnum_any__noneg_nan ; CHECK-SAME: (float [[ARG0:%.*]], float nofpclass(nan ninf nsub nnorm) [[ARG1:%.*]]) #[[ATTR4]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan) float @llvm.maxnum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan ninf nsub nnorm) float @llvm.maxnum.f32(float [[ARG0]], float [[ARG1]]) #[[ATTR9]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.maxnum.f32(float %arg0, float %arg1)