Index: llvm/include/llvm/Analysis/ValueTracking.h =================================================================== --- llvm/include/llvm/Analysis/ValueTracking.h +++ llvm/include/llvm/Analysis/ValueTracking.h @@ -254,6 +254,11 @@ return (KnownFPClasses & fcPosInf) == fcNone; } + /// Return true if it's known this can never be -infinity. + bool isKnownNeverNegInfinity() const { + return (KnownFPClasses & fcNegInf) == fcNone; + } + /// Return true if it's known this can never be a zero. bool isKnownNeverZero() const { return (KnownFPClasses & fcZero) == fcNone; Index: llvm/lib/Analysis/ValueTracking.cpp =================================================================== --- llvm/lib/Analysis/ValueTracking.cpp +++ llvm/lib/Analysis/ValueTracking.cpp @@ -4507,6 +4507,25 @@ break; } + case Intrinsic::trunc: { + KnownFPClass KnownSrc; + computeKnownFPClass(II->getArgOperand(0), DemandedElts, + InterestedClasses, KnownSrc, Depth + 1, Q, TLI); + + // Integer results cannot be subnormal. + Known.knownNot(fcSubnormal); + + // trunc passes through infinities. + if (KnownSrc.isKnownNeverPosInfinity()) + Known.knownNot(fcPosInf); + if (KnownSrc.isKnownNeverNegInfinity()) + Known.knownNot(fcNegInf); + + // Non-constrained intrinsics do not guarantee signaling nan quieting. + if (KnownSrc.isKnownNeverNaN()) + Known.knownNot(fcNan); + break; + } default: break; } Index: llvm/test/Transforms/Attributor/nofpclass-trunc.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/Attributor/nofpclass-trunc.ll @@ -4,9 +4,9 @@ declare float @llvm.trunc.f32(float) define float @ret_trunc(float %arg0) { -; CHECK-LABEL: define float @ret_trunc +; CHECK-LABEL: define nofpclass(sub) float @ret_trunc ; CHECK-SAME: (float [[ARG0:%.*]]) #[[ATTR1:[0-9]+]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2:[0-9]+]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2:[0-9]+]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -14,9 +14,9 @@ } define float @ret_trunc_noinf(float nofpclass(inf) %arg0) { -; CHECK-LABEL: define float @ret_trunc_noinf +; CHECK-LABEL: define nofpclass(inf sub) float @ret_trunc_noinf ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(inf sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -24,9 +24,9 @@ } define float @ret_trunc_nopinf(float nofpclass(pinf) %arg0) { -; CHECK-LABEL: define float @ret_trunc_nopinf +; CHECK-LABEL: define nofpclass(pinf sub) float @ret_trunc_nopinf ; CHECK-SAME: (float nofpclass(pinf) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(pinf sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -34,9 +34,9 @@ } define float @ret_trunc_noninf(float nofpclass(ninf) %arg0) { -; CHECK-LABEL: define float @ret_trunc_noninf +; CHECK-LABEL: define nofpclass(ninf sub) float @ret_trunc_noninf ; CHECK-SAME: (float nofpclass(ninf) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(ninf sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -44,9 +44,9 @@ } define float @ret_trunc_nonan(float nofpclass(nan) %arg0) { -; CHECK-LABEL: define float @ret_trunc_nonan +; CHECK-LABEL: define nofpclass(nan sub) float @ret_trunc_nonan ; CHECK-SAME: (float nofpclass(nan) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(nan sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -54,9 +54,9 @@ } define float @ret_trunc_noqnan(float nofpclass(qnan) %arg0) { -; CHECK-LABEL: define float @ret_trunc_noqnan +; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_noqnan ; CHECK-SAME: (float nofpclass(qnan) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -64,9 +64,9 @@ } define float @ret_trunc_nosnan(float nofpclass(snan) %arg0) { -; CHECK-LABEL: define float @ret_trunc_nosnan +; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nosnan ; CHECK-SAME: (float nofpclass(snan) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -74,9 +74,9 @@ } define float @ret_trunc_nozero(float nofpclass(zero) %arg0) { -; CHECK-LABEL: define float @ret_trunc_nozero +; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nozero ; CHECK-SAME: (float nofpclass(zero) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -84,9 +84,9 @@ } define float @ret_trunc_nopzero(float nofpclass(pzero) %arg0) { -; CHECK-LABEL: define float @ret_trunc_nopzero +; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nopzero ; CHECK-SAME: (float nofpclass(pzero) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -94,9 +94,9 @@ } define float @ret_trunc_nonzero(float nofpclass(nzero) %arg0) { -; CHECK-LABEL: define float @ret_trunc_nonzero +; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nonzero ; CHECK-SAME: (float nofpclass(nzero) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0) @@ -104,9 +104,9 @@ } define float @ret_trunc_nonorm(float nofpclass(norm) %arg0) { -; CHECK-LABEL: define float @ret_trunc_nonorm +; CHECK-LABEL: define nofpclass(sub) float @ret_trunc_nonorm ; CHECK-SAME: (float nofpclass(norm) [[ARG0:%.*]]) #[[ATTR1]] { -; CHECK-NEXT: [[CALL:%.*]] = call float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] +; CHECK-NEXT: [[CALL:%.*]] = call nofpclass(sub) float @llvm.trunc.f32(float [[ARG0]]) #[[ATTR2]] ; CHECK-NEXT: ret float [[CALL]] ; %call = call float @llvm.trunc.f32(float %arg0)