Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -238,6 +238,10 @@ /// definitely set or false if the sign bit is definitely unset. std::optional SignBit; + /// Return true if it's known this can never be one of the mask entries. + bool isKnownNever(FPClassTest Mask) const { + return (KnownFPClasses & Mask) == fcNone; + } bool isUnknown() const { return KnownFPClasses == fcAllFlags && !SignBit; @@ -245,43 +249,43 @@ /// Return true if it's known this can never be a nan. bool isKnownNeverNaN() const { - return (KnownFPClasses & fcNan) == fcNone; + return isKnownNever(fcNan); } /// Return true if it's known this can never be an infinity. bool isKnownNeverInfinity() const { - return (KnownFPClasses & fcInf) == fcNone; + return isKnownNever(fcInf); } /// Return true if it's known this can never be +infinity. bool isKnownNeverPosInfinity() const { - return (KnownFPClasses & fcPosInf) == fcNone; + return isKnownNever(fcPosInf); } /// Return true if it's known this can never be -infinity. bool isKnownNeverNegInfinity() const { - return (KnownFPClasses & fcNegInf) == fcNone; + return isKnownNever(fcNegInf); } /// Return true if it's known this can never be a subnormal bool isKnownNeverSubnormal() const { - return (KnownFPClasses & fcSubnormal) == fcNone; + return isKnownNever(fcSubnormal); } /// Return true if it's known this can never be a negativesubnormal bool isKnownNeverNegSubnormal() const { - return (KnownFPClasses & fcNegSubnormal) == fcNone; + return isKnownNever(fcNegSubnormal); } /// Return true if it's known this can never be a zero. This means a literal /// [+-]0, and does not include denormal inputs implicitly treated as [+-]0. bool isKnownNeverZero() const { - return (KnownFPClasses & fcZero) == fcNone; + return isKnownNever(fcZero); } /// Return true if it's known this can never be a literal negative zero. bool isKnownNeverNegZero() const { - return (KnownFPClasses & fcNegZero) == fcNone; + return isKnownNever(fcNegZero); } /// Return true if it's know this can never be interpreted as a zero. This Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -4709,6 +4709,21 @@ // Non-constrained intrinsics do not guarantee signaling nan quieting. if (KnownSrc.isKnownNeverNaN()) Known.knownNot(fcNan); + + if (KnownSrc.isKnownNever(fcPosNormal)) + Known.knownNot(fcPosNormal); + + if (KnownSrc.isKnownNever(fcNegNormal)) + Known.knownNot(fcNegNormal); + + if (KnownSrc.isKnownNever(fcPosZero | fcPosSubnormal | fcPosNormal)) + Known.knownNot(fcPosZero); + + if (KnownSrc.isKnownNever(fcNegZero | fcNegSubnormal | fcNegNormal)) + Known.knownNot(fcNegZero); + + // Sign should be preserved + Known.SignBit = KnownSrc.SignBit; break; } case Intrinsic::exp: Index: llvm/test/Transforms/Attributor/nofpclass-trunc.ll =================================================================== --- llvm/test/Transforms/Attributor/nofpclass-trunc.ll +++ llvm/test/Transforms/Attributor/nofpclass-trunc.ll @@ -105,9 +105,9 @@ } define float @ret_trunc_nonorm(float nofpclass(norm) %arg0) { -; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nonorm +; CHECK-LABEL: define nofpclass(sub norm) float @ret_trunc_nonorm ; CHECK-SAME: (float nofpclass(norm) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub norm) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -115,9 +115,9 @@ } define float @ret_trunc_nonnorm(float nofpclass(nnorm) %arg0) { -; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nonnorm +; CHECK-LABEL: define nofpclass(sub nnorm) float @ret_trunc_nonnorm ; CHECK-SAME: (float nofpclass(nnorm) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub nnorm) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -125,9 +125,9 @@ } define float @ret_trunc_nopnorm(float nofpclass(pnorm) %arg0) { -; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nopnorm +; CHECK-LABEL: define nofpclass(sub pnorm) float @ret_trunc_nopnorm ; CHECK-SAME: (float nofpclass(pnorm) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub pnorm) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -155,9 +155,9 @@ } define float @ret_trunc_nonorm_nosub(float nofpclass(norm sub) %arg0) { -; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nonorm_nosub +; CHECK-LABEL: define nofpclass(sub norm) float @ret_trunc_nonorm_nosub ; CHECK-SAME: (float nofpclass(sub norm) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub norm) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -165,9 +165,9 @@ } define float @ret_trunc_nopnorm_nopsub(float nofpclass(pnorm psub) %arg0) { -; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nopnorm_nopsub +; CHECK-LABEL: define nofpclass(sub pnorm) float @ret_trunc_nopnorm_nopsub ; CHECK-SAME: (float nofpclass(psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub pnorm) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -175,9 +175,9 @@ } define float @ret_trunc_nonnorm_nonsub(float nofpclass(nnorm nsub) %arg0) { -; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nonnorm_nonsub +; CHECK-LABEL: define nofpclass(sub nnorm) float @ret_trunc_nonnorm_nonsub ; CHECK-SAME: (float nofpclass(nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub nnorm) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -185,9 +185,9 @@ } define float @ret_trunc_nopnorm_nonsub(float nofpclass(pnorm nsub) %arg0) { -; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nopnorm_nonsub +; CHECK-LABEL: define nofpclass(sub pnorm) float @ret_trunc_nopnorm_nonsub ; CHECK-SAME: (float nofpclass(nsub pnorm) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub pnorm) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0)