Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -298,6 +298,9 @@ bool isKnownNeverLogicalNegZero(const Function &F, Type *Ty) const; + static constexpr FPClassTest OrderedLessThanZeroMask = + fcNegSubnormal | fcNegNormal | fcNegInf; + /// Return true if we can prove that the analyzed floating-point value is /// either NaN or never less than -0.0. /// @@ -307,8 +310,7 @@ /// x > +0 --> true /// x < -0 --> false bool cannotBeOrderedLessThanZero() const { - const FPClassTest OrderedNegMask = fcNegSubnormal | fcNegNormal | fcNegInf; - return (KnownFPClasses & OrderedNegMask) == fcNone; + return isKnownNever(OrderedLessThanZeroMask); } KnownFPClass &operator|=(const KnownFPClass &RHS) { @@ -364,6 +366,18 @@ KnownFPClasses = KnownFPClasses & fcPositive; } + // Propagate knowledge that a non-NaN source implies the result can also not + // be a NaN. For unconstrained operations, signaling nans are not guaranteed + // to be quieted but cannot be introduced. + void propagateNaN(const KnownFPClass &Src, bool PreserveSign = false) { + if (Src.isKnownNever(fcNan)) { + knownNot(fcNan); + if (PreserveSign) + SignBit = Src.SignBit; + } else if (Src.isKnownNever(fcSNan)) + knownNot(fcSNan); + } + void resetAll() { *this = KnownFPClass(); } }; Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -4071,17 +4071,22 @@ KnownFPClass &Known, unsigned Depth, const Query &Q, const TargetLibraryInfo *TLI) { - if ((InterestedClasses & fcNan) == fcNone) + if ((InterestedClasses & + (KnownFPClass::OrderedLessThanZeroMask | fcNan)) == fcNone) return; KnownFPClass KnownSrc; computeKnownFPClass(Op->getOperand(0), DemandedElts, InterestedClasses, KnownSrc, Depth + 1, Q, TLI); - if (KnownSrc.isKnownNeverNaN()) - Known.knownNot(fcNan); + + // Sign should be preserved + // TODO: Handle cannot be ordered greater than zero + if (KnownSrc.cannotBeOrderedLessThanZero()) + Known.knownNot(KnownFPClass::OrderedLessThanZeroMask); + + Known.propagateNaN(KnownSrc, true); // Infinity needs a range check. - // TODO: Sign bit should be preserved } // TODO: Merge implementation of cannotBeOrderedLessThanZero into here. Index: llvm/test/Transforms/Attributor/nofpclass-fptrunc.ll =================================================================== --- llvm/test/Transforms/Attributor/nofpclass-fptrunc.ll +++ llvm/test/Transforms/Attributor/nofpclass-fptrunc.ll @@ -34,7 +34,7 @@ } define float @ret_fptrunc_nosnan(double nofpclass(snan) %arg0) { -; CHECK-LABEL: define float @ret_fptrunc_nosnan +; CHECK-LABEL: define nofpclass(snan) float @ret_fptrunc_nosnan ; CHECK-SAME: (double nofpclass(snan) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[EXT:%.*]] = fptrunc double [[ARG0]] to float ; CHECK-NEXT: ret float [[EXT]] @@ -134,8 +134,8 @@ } define float @ret_fptrunc_posonly(double nofpclass(ninf nnorm nsub nzero) %arg0) { -; CHECK-LABEL: define float @ret_fptrunc_posonly -; CHECK-SAME: (double nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_fptrunc_posonly +; CHECK-SAME: (double nofpclass(ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[EXT:%.*]] = fptrunc double [[ARG0]] to float ; CHECK-NEXT: ret float [[EXT]] ; @@ -144,8 +144,8 @@ } define float @ret_fptrunc_posonly_zero(double nofpclass(ninf nnorm nsub) %arg0) { -; CHECK-LABEL: define float @ret_fptrunc_posonly_zero -; CHECK-SAME: (double nofpclass(ninf nsub nnorm) [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-LABEL: define nofpclass(ninf nsub nnorm) float @ret_fptrunc_posonly_zero +; CHECK-SAME: (double nofpclass(ninf nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[EXT:%.*]] = fptrunc double [[ARG0]] to float ; CHECK-NEXT: ret float [[EXT]] ; @@ -154,8 +154,8 @@ } define float @ret_fptrunc_posonly_zero_nan(double nofpclass(ninf nnorm nsub nan) %arg0) { -; CHECK-LABEL: define float @ret_fptrunc_posonly_zero_nan -; CHECK-SAME: (double nofpclass(nan ninf nsub nnorm) [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-LABEL: define nofpclass(nan ninf nsub nnorm) float @ret_fptrunc_posonly_zero_nan +; CHECK-SAME: (double nofpclass(nan ninf nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[EXT:%.*]] = fptrunc double [[ARG0]] to float ; CHECK-NEXT: ret float [[EXT]] ; @@ -164,8 +164,8 @@ } define float @ret_fptrunc_posonly_nan(double nofpclass(ninf nnorm nsub nzero nan) %arg0) { -; CHECK-LABEL: define float @ret_fptrunc_posonly_nan -; CHECK-SAME: (double nofpclass(nan ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-LABEL: define nofpclass(nan ninf nsub nnorm) float @ret_fptrunc_posonly_nan +; CHECK-SAME: (double nofpclass(nan ninf nzero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[EXT:%.*]] = fptrunc double [[ARG0]] to float ; CHECK-NEXT: ret float [[EXT]] ; @@ -175,7 +175,7 @@ define float @ret_fptrunc_negonly(double nofpclass(pinf pnorm psub pzero) %arg0) { ; CHECK-LABEL: define float @ret_fptrunc_negonly -; CHECK-SAME: (double nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-SAME: (double nofpclass(pinf pzero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[EXT:%.*]] = fptrunc double [[ARG0]] to float ; CHECK-NEXT: ret float [[EXT]] ; @@ -185,7 +185,7 @@ define float @ret_fptrunc_negonly_zero(double nofpclass(pinf pnorm psub) %arg0) { ; CHECK-LABEL: define float @ret_fptrunc_negonly_zero -; CHECK-SAME: (double nofpclass(pinf psub pnorm) [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-SAME: (double nofpclass(pinf psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[EXT:%.*]] = fptrunc double [[ARG0]] to float ; CHECK-NEXT: ret float [[EXT]] ; @@ -194,8 +194,8 @@ } define float @ret_fptrunc_negonly_zero_nan(double nofpclass(pinf pnorm psub nan) %arg0) { -; CHECK-LABEL: define float @ret_fptrunc_negonly_zero_nan -; CHECK-SAME: (double nofpclass(nan pinf psub pnorm) [[ARG0:%.*]]) #[[ATTR0]] { +; CHECK-LABEL: define nofpclass(nan) float @ret_fptrunc_negonly_zero_nan +; CHECK-SAME: (double nofpclass(nan pinf psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { ; CHECK-NEXT: [[EXT:%.*]] = fptrunc double [[ARG0]] to float ; CHECK-NEXT: ret float [[EXT]] ; @@ -234,9 +234,9 @@ } define float @ret_fptrunc_round_nosnan(double nofpclass(snan) %arg0) { -; CHECK-LABEL: define float @ret_fptrunc_round_nosnan +; CHECK-LABEL: define nofpclass(snan) float @ret_fptrunc_round_nosnan ; CHECK-SAME: (double nofpclass(snan) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[EXT:%.*]] = call float @llvm.fptrunc.round.f32.f64(double [[ARG0]], metadata !"round.downward") #[[ATTR2]] +; CHECK-NEXT: [[EXT:%.*]] = call nofpclass(snan) float @llvm.fptrunc.round.f32.f64(double [[ARG0]], metadata !"round.downward") #[[ATTR2]] ; CHECK-NEXT: ret float [[EXT]] ; %ext = call float @llvm.fptrunc.round.f32.f64(double %arg0, metadata !"round.downward")