https://github.com/arsenm updated https://github.com/llvm/llvm-project/pull/173432
>From 34a3106281981a455064c315ad9e8f7d6bf48ea2 Mon Sep 17 00:00:00 2001 From: Matt Arsenault <[email protected]> Date: Sat, 20 Dec 2025 12:29:20 +0100 Subject: [PATCH 1/4] InstCombine: Handle exp/exp2/exp10 in SimplifyDemandedFPClass I'm working on optimizing out the tail sequences in the implementations of the 4 different flavors of pow. These include chains of selects on the various edge cases. Related to #64870 --- llvm/include/llvm/Support/KnownFPClass.h | 3 + llvm/lib/Analysis/ValueTracking.cpp | 19 +--- llvm/lib/Support/KnownFPClass.cpp | 24 +++++ .../InstCombineSimplifyDemanded.cpp | 90 +++++++++++++++++++ .../simplify-demanded-fpclass-exp.ll | 81 +++++++---------- 5 files changed, 153 insertions(+), 64 deletions(-) diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h index 1670e76f2e8c5..5adb95117503e 100644 --- a/llvm/include/llvm/Support/KnownFPClass.h +++ b/llvm/include/llvm/Support/KnownFPClass.h @@ -165,6 +165,9 @@ struct KnownFPClass { canonicalize(const KnownFPClass &Src, DenormalMode DenormMode = DenormalMode::getDynamic()); + /// Report known values for exp, exp2 and exp10. + LLVM_ABI void exp(); + /// Return true if the sign bit must be 0, ignoring the sign of nans. bool signBitIsZeroOrNaN() const { return isKnownNever(fcNegative); } diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index ec7fe89848d41..090d31167c08a 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -5356,23 +5356,8 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts, KnownFPClass KnownSrc; computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedClasses, KnownSrc, Q, Depth + 1); - if (KnownSrc.isKnownNeverNaN()) { - Known.knownNot(fcNan); - Known.signBitMustBeZero(); - } - - if (KnownSrc.cannotBeOrderedLessThanZero()) { - // If the source is positive this cannot underflow. - Known.knownNot(fcPosZero); - - // Cannot introduce denormal values. - Known.knownNot(fcPosSubnormal); - } - - // If the source is negative, this cannot overflow to infinity. - if (KnownSrc.cannotBeOrderedGreaterThanZero()) - Known.knownNot(fcPosInf); - + Known = KnownSrc; + Known.exp(); break; } case Intrinsic::fptrunc_round: { diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp index a4cdf0d253b69..4b8a176704060 100644 --- a/llvm/lib/Support/KnownFPClass.cpp +++ b/llvm/lib/Support/KnownFPClass.cpp @@ -141,6 +141,30 @@ KnownFPClass KnownFPClass::canonicalize(const KnownFPClass &KnownSrc, return Known; } +void KnownFPClass::exp() { + KnownFPClass KnownSrc = *this; + *this = KnownFPClass(); + + if (KnownSrc.isKnownNeverNaN()) { + knownNot(fcNan); + signBitMustBeZero(); + } + + if (KnownSrc.cannotBeOrderedLessThanZero()) { + // If the source is positive this cannot underflow. + Known.knownNot(fcPosZero); + + // Cannot introduce denormal values. + Known.knownNot(fcPosSubnormal); + } + + // If the source is negative, this cannot overflow to infinity. + if (KnownSrc.cannotBeOrderedGreaterThanZero()) + Known.knownNot(fcPosInf); + + return Known; +} + void KnownFPClass::propagateCanonicalizingSrc(const KnownFPClass &Src, DenormalMode Mode) { propagateDenormal(Src, Mode); diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp index ddf3b17322cbb..0e40510dc9bc3 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp @@ -2106,6 +2106,96 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Value *V, Known.copysign(KnownSign); break; } + case Intrinsic::exp: + case Intrinsic::exp2: + case Intrinsic::exp10: { + if ((DemandedMask & + (fcPosZero | fcPosSubnormal | fcPosNormal | fcPosInf)) == fcNone) { + // Only returns positive values or nans. + if ((DemandedMask & fcNan) == fcNone) + return PoisonValue::get(VTy); + + // Only need nan propagation. + // Note: Dropping snan quieting. + return CI->getArgOperand(0); + } + + FPClassTest SrcDemandedMask = DemandedMask & fcNan; + + if (DemandedMask & fcZero) { + // exp(-infinity) = 0 + SrcDemandedMask |= fcNegInf; + + // exp(-largest_normal) = 0 + // + // Negative numbers of sufficiently large magnitude underflow to 0. No + // subnormal input has a 0 result. + SrcDemandedMask |= fcNegNormal; + } + + if (DemandedMask & fcPosSubnormal) { + // Negative numbers of sufficiently large magnitude underflow to 0. No + // subnormal input has a 0 result. + SrcDemandedMask |= fcNegNormal; + } + + if (DemandedMask & fcPosNormal) { + // exp(0) = 1 + // exp(+/- smallest_normal) = 1 + // exp(+/- largest_denormal) = 1 + // exp(+/- smallest_denormal) = 1 + SrcDemandedMask |= fcPosNormal | fcSubnormal | fcZero; + } + + // exp(inf), exp(largest_normal) = inf + if (DemandedMask & fcPosInf) + SrcDemandedMask |= fcPosInf | fcPosNormal; + + // TODO: This could really make use of KnownFPClass of specific value + // range, (i.e., close enough to 1) + if (SimplifyDemandedFPClass(I, 0, SrcDemandedMask, Known, Depth + 1)) + return I; + + /// Propagate nnan-ness to simplify edge case checks. + if ((DemandedMask & fcNan) == fcNone) + Known.knownNot(fcNan); + + // exp(+/-0) = 1 + if (Known.isKnownAlways(fcZero)) + return ConstantFP::get(VTy, 1.0); + + // exp(0 | nan) => x == 0.0 ? 1.0 : x + if (Known.isKnownAlways(fcZero | fcNan)) { + IRBuilderBase::InsertPointGuard Guard(Builder); + Builder.SetInsertPoint(CI); + + // fadd +/-0, 1.0 => 1.0 + // fadd nan, 1.0 => nan + return Builder.CreateFAdd(CI->getArgOperand(0), + ConstantFP::get(VTy, 1.0)); + } + + if (Known.isKnownAlways(fcInf | fcNan)) { + // exp(-inf) = 0 + // exp(+inf) = +inf + IRBuilderBase::InsertPointGuard Guard(Builder); + Builder.SetInsertPoint(CI); + + // Note: Dropping canonicalize / quiet of signaling nan. + Value *X = CI->getArgOperand(0); + Value *IsPosInfOrNan = + Builder.CreateFCmpUEQ(X, ConstantFP::getInfinity(VTy)); + return Builder.CreateSelect(IsPosInfOrNan, X, ConstantFP::getZero(VTy)); + } + + // Only perform nan propagation. + // Note: Dropping canonicalize / quiet of signaling nan. + if (Known.isKnownAlways(fcNan)) + return CI->getArgOperand(0); + + Known.exp(); + break; + } case Intrinsic::canonicalize: { Type *EltTy = VTy->getScalarType(); diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-exp.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-exp.ll index 7706d2de68f16..8311243e45580 100644 --- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-exp.ll +++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-exp.ll @@ -54,8 +54,7 @@ define nofpclass(nan) float @ret_nofpclass_nan__exp2_select_maybe_inf_or_not_nan define nofpclass(nan) float @ret_nofpclass_nan__exp2_select_maybe_inf_or_nan(i1 %cond, float %maybe.nan, float nofpclass(inf zero sub norm) %only.nan) { ; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__exp2_select_maybe_inf_or_nan( ; CHECK-SAME: i1 [[COND:%.*]], float [[MAYBE_NAN:%.*]], float nofpclass(inf zero sub norm) [[ONLY_NAN:%.*]]) { -; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], float [[MAYBE_NAN]], float [[ONLY_NAN]] -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[SELECT]]) +; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MAYBE_NAN]]) ; CHECK-NEXT: ret float [[EXP]] ; %select = select i1 %cond, float %maybe.nan, float %only.nan @@ -67,8 +66,7 @@ define nofpclass(nan) float @ret_nofpclass_nan__exp2_select_maybe_inf_or_nan(i1 define nofpclass(pinf zero psub pnorm) float @ret_nofpclass_no_positives__exp2(float %x) { ; CHECK-LABEL: define nofpclass(pinf zero psub pnorm) float @ret_nofpclass_no_positives__exp2( ; CHECK-SAME: float [[X:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[X]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float [[X]] ; %exp = call float @llvm.exp2.f32(float %x) ret float %exp @@ -88,8 +86,7 @@ define nofpclass(nan pinf zero psub pnorm) float @ret_nofpclass_no_positives_no_ define nofpclass(pzero pinf psub pnorm) float @ret_nofpclass_no_positives_except_neg0__exp2(float %x) { ; CHECK-LABEL: define nofpclass(pinf pzero psub pnorm) float @ret_nofpclass_no_positives_except_neg0__exp2( ; CHECK-SAME: float [[X:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[X]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float [[X]] ; %exp = call float @llvm.exp2.f32(float %x) ret float %exp @@ -131,8 +128,7 @@ define nofpclass(pinf psub pnorm) float @ret_nofpclass_no_positives_except_0__ex define nofpclass(nan) float @handle_exp(i1 %cond, float %maybe.nan, float nofpclass(inf zero sub norm) %only.nan) { ; CHECK-LABEL: define nofpclass(nan) float @handle_exp( ; CHECK-SAME: i1 [[COND:%.*]], float [[MAYBE_NAN:%.*]], float nofpclass(inf zero sub norm) [[ONLY_NAN:%.*]]) { -; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], float [[MAYBE_NAN]], float [[ONLY_NAN]] -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp.f32(float [[SELECT]]) +; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp.f32(float [[MAYBE_NAN]]) ; CHECK-NEXT: ret float [[EXP]] ; %select = select i1 %cond, float %maybe.nan, float %only.nan @@ -144,8 +140,7 @@ define nofpclass(nan) float @handle_exp(i1 %cond, float %maybe.nan, float nofpcl define nofpclass(nan) float @handle_exp10(i1 %cond, float %maybe.nan, float nofpclass(inf zero sub norm) %only.nan) { ; CHECK-LABEL: define nofpclass(nan) float @handle_exp10( ; CHECK-SAME: i1 [[COND:%.*]], float [[MAYBE_NAN:%.*]], float nofpclass(inf zero sub norm) [[ONLY_NAN:%.*]]) { -; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], float [[MAYBE_NAN]], float [[ONLY_NAN]] -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp10.f32(float [[SELECT]]) +; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp10.f32(float [[MAYBE_NAN]]) ; CHECK-NEXT: ret float [[EXP]] ; %select = select i1 %cond, float %maybe.nan, float %only.nan @@ -158,7 +153,7 @@ define nofpclass(nan) float @handle_exp10(i1 %cond, float %maybe.nan, float nofp define nofpclass(inf norm nan) float @ret_nofpclass_only_subzero__exp2_select_unknown_or_not_norm(i1 %cond, float %unknown, float nofpclass(norm) %not.norm) { ; CHECK-LABEL: define nofpclass(nan inf norm) float @ret_nofpclass_only_subzero__exp2_select_unknown_or_not_norm( ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]], float nofpclass(norm) [[NOT_NORM:%.*]]) { -; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], float [[UNKNOWN]], float [[NOT_NORM]] +; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], float [[UNKNOWN]], float 0xFFF0000000000000 ; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[SELECT]]) ; CHECK-NEXT: ret float [[EXP]] ; @@ -170,8 +165,7 @@ define nofpclass(inf norm nan) float @ret_nofpclass_only_subzero__exp2_select_un define nofpclass(inf norm nan zero) float @ret_nofpclass_only_sub__exp2_select_unknown_or_not_norm(i1 %cond, float %unknown, float nofpclass(norm) %not.norm) { ; CHECK-LABEL: define nofpclass(nan inf zero norm) float @ret_nofpclass_only_sub__exp2_select_unknown_or_not_norm( ; CHECK-SAME: i1 [[COND:%.*]], float [[UNKNOWN:%.*]], float nofpclass(norm) [[NOT_NORM:%.*]]) { -; CHECK-NEXT: [[SELECT:%.*]] = select i1 [[COND]], float [[UNKNOWN]], float [[NOT_NORM]] -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[SELECT]]) +; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[UNKNOWN]]) ; CHECK-NEXT: ret float [[EXP]] ; %select = select i1 %cond, float %unknown, float %not.norm @@ -195,8 +189,7 @@ define nofpclass(inf norm nan sub) float @ret_nofpclass_only_zero__exp2_select_u define nofpclass(ninf norm zero sub) float @pinf_result_implies_pnorm_source(float nofpclass(pinf nan) %maybe.pnorm) { ; CHECK-LABEL: define nofpclass(ninf zero sub norm) float @pinf_result_implies_pnorm_source( ; CHECK-SAME: float nofpclass(nan pinf) [[MAYBE_PNORM:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MAYBE_PNORM]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float 0x7FF0000000000000 ; %exp = call float @llvm.exp2.f32(float %maybe.pnorm) ret float %exp @@ -215,8 +208,7 @@ define nofpclass(ninf norm zero sub) float @pinf_result_implies_pnorm_source_nan define nofpclass(pinf norm zero sub) float @ninf_result_implies_poison(float nofpclass(ninf nan) %maybe.nnorm) { ; CHECK-LABEL: define nofpclass(pinf zero sub norm) float @ninf_result_implies_poison( ; CHECK-SAME: float nofpclass(nan ninf) [[MAYBE_NNORM:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MAYBE_NNORM]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float poison ; %exp = call float @llvm.exp2.f32(float %maybe.nnorm) ret float %exp @@ -275,8 +267,7 @@ define nofpclass(inf norm nan zero) float @sub_result_implies_nnorm_source_valid define nofpclass(inf norm nan zero) float @sub_result_implies_nsub_source_valid(float nofpclass(norm psub nan) %maybe.nsub) { ; CHECK-LABEL: define nofpclass(nan inf zero norm) float @sub_result_implies_nsub_source_valid( ; CHECK-SAME: float nofpclass(nan psub norm) [[MAYBE_NSUB:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MAYBE_NSUB]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float poison ; %exp = call float @llvm.exp2.f32(float %maybe.nsub) ret float %exp @@ -323,8 +314,7 @@ define nofpclass(inf nnorm nan zero) float @pnorm_result_implies_possible_0_sour define nofpclass(inf nnorm nan zero sub) float @pnorm_result_implies_possible_0_source_no_inf(float nofpclass(inf norm sub) %maybe.zero.or.nan) { ; CHECK-LABEL: define nofpclass(nan inf zero sub nnorm) float @pnorm_result_implies_possible_0_source_no_inf( ; CHECK-SAME: float nofpclass(inf sub norm) [[MAYBE_ZERO_OR_NAN:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MAYBE_ZERO_OR_NAN]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float 1.000000e+00 ; %exp = call float @llvm.exp2.f32(float %maybe.zero.or.nan) ret float %exp @@ -344,8 +334,7 @@ define nofpclass(inf nnorm nan zero sub) float @pnorm_result_implies_possible_su define nofpclass(pzero) float @source_is_known_zero(float nofpclass(nan inf norm sub) %must.be.zero) { ; CHECK-LABEL: define nofpclass(pzero) float @source_is_known_zero( ; CHECK-SAME: float nofpclass(nan inf sub norm) [[MUST_BE_ZERO:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_ZERO]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float 1.000000e+00 ; %exp = call float @llvm.exp2.f32(float %must.be.zero) ret float %exp @@ -354,8 +343,7 @@ define nofpclass(pzero) float @source_is_known_zero(float nofpclass(nan inf norm define nofpclass(pzero) <2 x float> @source_is_known_zero_vec(<2 x float> nofpclass(nan inf norm sub) %must.be.zero) { ; CHECK-LABEL: define nofpclass(pzero) <2 x float> @source_is_known_zero_vec( ; CHECK-SAME: <2 x float> nofpclass(nan inf sub norm) [[MUST_BE_ZERO:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call <2 x float> @llvm.exp2.v2f32(<2 x float> [[MUST_BE_ZERO]]) -; CHECK-NEXT: ret <2 x float> [[EXP]] +; CHECK-NEXT: ret <2 x float> splat (float 1.000000e+00) ; %exp = call <2 x float> @llvm.exp2.v2f32(<2 x float> %must.be.zero) ret <2 x float> %exp @@ -364,8 +352,7 @@ define nofpclass(pzero) <2 x float> @source_is_known_zero_vec(<2 x float> nofpcl define nofpclass(pzero) float @source_is_known_pzero(float nofpclass(nan inf norm sub nzero) %must.be.pzero) { ; CHECK-LABEL: define nofpclass(pzero) float @source_is_known_pzero( ; CHECK-SAME: float nofpclass(nan inf nzero sub norm) [[MUST_BE_PZERO:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_PZERO]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float 1.000000e+00 ; %exp = call float @llvm.exp2.f32(float %must.be.pzero) ret float %exp @@ -374,8 +361,7 @@ define nofpclass(pzero) float @source_is_known_pzero(float nofpclass(nan inf nor define nofpclass(pzero) float @source_is_known_nzero(float nofpclass(nan inf norm sub pzero) %must.be.nzero) { ; CHECK-LABEL: define nofpclass(pzero) float @source_is_known_nzero( ; CHECK-SAME: float nofpclass(nan inf pzero sub norm) [[MUST_BE_NZERO:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_NZERO]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float 1.000000e+00 ; %exp = call float @llvm.exp2.f32(float %must.be.nzero) ret float %exp @@ -384,7 +370,8 @@ define nofpclass(pzero) float @source_is_known_nzero(float nofpclass(nan inf nor define nofpclass(nzero) float @source_is_known_inf(float nofpclass(nan norm sub zero) %must.be.inf) { ; CHECK-LABEL: define nofpclass(nzero) float @source_is_known_inf( ; CHECK-SAME: float nofpclass(nan zero sub norm) [[MUST_BE_INF:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_INF]]) +; CHECK-NEXT: [[TMP1:%.*]] = fcmp ueq float [[MUST_BE_INF]], 0x7FF0000000000000 +; CHECK-NEXT: [[EXP:%.*]] = select i1 [[TMP1]], float [[MUST_BE_INF]], float 0.000000e+00 ; CHECK-NEXT: ret float [[EXP]] ; %exp = call float @llvm.exp2.f32(float %must.be.inf) @@ -394,7 +381,8 @@ define nofpclass(nzero) float @source_is_known_inf(float nofpclass(nan norm sub define nofpclass(nzero) <2 x float> @source_is_known_inf_vec(<2 x float> nofpclass(nan norm sub zero) %must.be.inf) { ; CHECK-LABEL: define nofpclass(nzero) <2 x float> @source_is_known_inf_vec( ; CHECK-SAME: <2 x float> nofpclass(nan zero sub norm) [[MUST_BE_INF:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call <2 x float> @llvm.exp2.v2f32(<2 x float> [[MUST_BE_INF]]) +; CHECK-NEXT: [[TMP1:%.*]] = fcmp ueq <2 x float> [[MUST_BE_INF]], splat (float 0x7FF0000000000000) +; CHECK-NEXT: [[EXP:%.*]] = select <2 x i1> [[TMP1]], <2 x float> [[MUST_BE_INF]], <2 x float> zeroinitializer ; CHECK-NEXT: ret <2 x float> [[EXP]] ; %exp = call <2 x float> @llvm.exp2.v2f32(<2 x float> %must.be.inf) @@ -404,8 +392,7 @@ define nofpclass(nzero) <2 x float> @source_is_known_inf_vec(<2 x float> nofpcla define nofpclass(nzero) float @source_is_known_pinf(float nofpclass(ninf nan norm sub zero) %must.be.pinf) { ; CHECK-LABEL: define nofpclass(nzero) float @source_is_known_pinf( ; CHECK-SAME: float nofpclass(nan ninf zero sub norm) [[MUST_BE_PINF:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_PINF]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float 0x7FF0000000000000 ; %exp = call float @llvm.exp2.f32(float %must.be.pinf) ret float %exp @@ -414,8 +401,7 @@ define nofpclass(nzero) float @source_is_known_pinf(float nofpclass(ninf nan nor define nofpclass(nzero) float @source_is_known_ninf(float nofpclass(pinf nan norm sub zero) %must.be.ninf) { ; CHECK-LABEL: define nofpclass(nzero) float @source_is_known_ninf( ; CHECK-SAME: float nofpclass(nan pinf zero sub norm) [[MUST_BE_NINF:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_NINF]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float 0.000000e+00 ; %exp = call float @llvm.exp2.f32(float %must.be.ninf) ret float %exp @@ -424,8 +410,8 @@ define nofpclass(nzero) float @source_is_known_ninf(float nofpclass(pinf nan nor define nofpclass(nzero) float @source_is_known_nan(float nofpclass(inf norm sub zero) %must.be.nan) { ; CHECK-LABEL: define nofpclass(nzero) float @source_is_known_nan( ; CHECK-SAME: float nofpclass(inf zero sub norm) [[MUST_BE_NAN:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_NAN]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: [[TMP1:%.*]] = fadd float [[MUST_BE_NAN]], 1.000000e+00 +; CHECK-NEXT: ret float [[TMP1]] ; %exp = call float @llvm.exp2.f32(float %must.be.nan) ret float %exp @@ -434,7 +420,8 @@ define nofpclass(nzero) float @source_is_known_nan(float nofpclass(inf norm sub define nofpclass(nzero) float @source_is_known_inf_or_nan(float nofpclass(norm sub zero) %must.be.inf.or.nan) { ; CHECK-LABEL: define nofpclass(nzero) float @source_is_known_inf_or_nan( ; CHECK-SAME: float nofpclass(zero sub norm) [[MUST_BE_INF_OR_NAN:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_INF_OR_NAN]]) +; CHECK-NEXT: [[TMP1:%.*]] = fcmp ueq float [[MUST_BE_INF_OR_NAN]], 0x7FF0000000000000 +; CHECK-NEXT: [[EXP:%.*]] = select i1 [[TMP1]], float [[MUST_BE_INF_OR_NAN]], float 0.000000e+00 ; CHECK-NEXT: ret float [[EXP]] ; %exp = call float @llvm.exp2.f32(float %must.be.inf.or.nan) @@ -444,7 +431,8 @@ define nofpclass(nzero) float @source_is_known_inf_or_nan(float nofpclass(norm s define nofpclass(nzero nan) float @source_is_known_inf_or_nan__nnan_result(float nofpclass(norm sub zero) %must.be.inf.or.nan) { ; CHECK-LABEL: define nofpclass(nan nzero) float @source_is_known_inf_or_nan__nnan_result( ; CHECK-SAME: float nofpclass(zero sub norm) [[MUST_BE_INF_OR_NAN:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_INF_OR_NAN]]) +; CHECK-NEXT: [[TMP1:%.*]] = fcmp ueq float [[MUST_BE_INF_OR_NAN]], 0x7FF0000000000000 +; CHECK-NEXT: [[EXP:%.*]] = select i1 [[TMP1]], float [[MUST_BE_INF_OR_NAN]], float 0.000000e+00 ; CHECK-NEXT: ret float [[EXP]] ; %exp = call float @llvm.exp2.f32(float %must.be.inf.or.nan) @@ -454,7 +442,7 @@ define nofpclass(nzero nan) float @source_is_known_inf_or_nan__nnan_result(float define nofpclass(pzero) float @source_is_known_zero_or_nan(float nofpclass(inf norm sub) %must.be.zero.or.nan) { ; CHECK-LABEL: define nofpclass(pzero) float @source_is_known_zero_or_nan( ; CHECK-SAME: float nofpclass(inf sub norm) [[MUST_BE_ZERO_OR_NAN:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_ZERO_OR_NAN]]) +; CHECK-NEXT: [[EXP:%.*]] = fadd float [[MUST_BE_ZERO_OR_NAN]], 1.000000e+00 ; CHECK-NEXT: ret float [[EXP]] ; %exp = call float @llvm.exp2.f32(float %must.be.zero.or.nan) @@ -464,7 +452,7 @@ define nofpclass(pzero) float @source_is_known_zero_or_nan(float nofpclass(inf n define nofpclass(pzero) <2 x float> @source_is_known_zero_or_nan_vec(<2 x float> nofpclass(inf norm sub) %must.be.zero.or.nan) { ; CHECK-LABEL: define nofpclass(pzero) <2 x float> @source_is_known_zero_or_nan_vec( ; CHECK-SAME: <2 x float> nofpclass(inf sub norm) [[MUST_BE_ZERO_OR_NAN:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call <2 x float> @llvm.exp2.v2f32(<2 x float> [[MUST_BE_ZERO_OR_NAN]]) +; CHECK-NEXT: [[EXP:%.*]] = fadd <2 x float> [[MUST_BE_ZERO_OR_NAN]], splat (float 1.000000e+00) ; CHECK-NEXT: ret <2 x float> [[EXP]] ; %exp = call <2 x float> @llvm.exp2.v2f32(<2 x float> %must.be.zero.or.nan) @@ -474,8 +462,8 @@ define nofpclass(pzero) <2 x float> @source_is_known_zero_or_nan_vec(<2 x float> define nofpclass(nzero) float @source_is_known_snan(float nofpclass(inf norm sub zero qnan) %must.be.snan) { ; CHECK-LABEL: define nofpclass(nzero) float @source_is_known_snan( ; CHECK-SAME: float nofpclass(qnan inf zero sub norm) [[MUST_BE_SNAN:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_SNAN]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: [[TMP1:%.*]] = fadd float [[MUST_BE_SNAN]], 1.000000e+00 +; CHECK-NEXT: ret float [[TMP1]] ; %exp = call float @llvm.exp2.f32(float %must.be.snan) ret float %exp @@ -484,8 +472,8 @@ define nofpclass(nzero) float @source_is_known_snan(float nofpclass(inf norm sub define nofpclass(nzero) float @source_is_known_qnan(float nofpclass(inf norm sub zero snan) %must.be.qnan) { ; CHECK-LABEL: define nofpclass(nzero) float @source_is_known_qnan( ; CHECK-SAME: float nofpclass(snan inf zero sub norm) [[MUST_BE_QNAN:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_QNAN]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: [[TMP1:%.*]] = fadd float [[MUST_BE_QNAN]], 1.000000e+00 +; CHECK-NEXT: ret float [[TMP1]] ; %exp = call float @llvm.exp2.f32(float %must.be.qnan) ret float %exp @@ -494,8 +482,7 @@ define nofpclass(nzero) float @source_is_known_qnan(float nofpclass(inf norm sub define nofpclass(pzero nan) float @source_is_known_zero_or_nan__nnan_result(float nofpclass(inf norm sub) %must.be.zero.or.nan) { ; CHECK-LABEL: define nofpclass(nan pzero) float @source_is_known_zero_or_nan__nnan_result( ; CHECK-SAME: float nofpclass(inf sub norm) [[MUST_BE_ZERO_OR_NAN:%.*]]) { -; CHECK-NEXT: [[EXP:%.*]] = call float @llvm.exp2.f32(float [[MUST_BE_ZERO_OR_NAN]]) -; CHECK-NEXT: ret float [[EXP]] +; CHECK-NEXT: ret float 1.000000e+00 ; %exp = call float @llvm.exp2.f32(float %must.be.zero.or.nan) ret float %exp >From 956adbd86ce2be1b7e4785f4e9e9ea061d6e5d4c Mon Sep 17 00:00:00 2001 From: Matt Arsenault <[email protected]> Date: Tue, 23 Dec 2025 22:43:11 +0100 Subject: [PATCH 2/4] Make KnownFPClass::exp not side-effecting --- llvm/include/llvm/Support/KnownFPClass.h | 2 +- llvm/lib/Analysis/ValueTracking.cpp | 14 +++++--------- llvm/lib/Support/KnownFPClass.cpp | 10 +++++----- .../InstCombine/InstCombineSimplifyDemanded.cpp | 16 +++++++++------- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/llvm/include/llvm/Support/KnownFPClass.h b/llvm/include/llvm/Support/KnownFPClass.h index 5adb95117503e..bf70ddb272e29 100644 --- a/llvm/include/llvm/Support/KnownFPClass.h +++ b/llvm/include/llvm/Support/KnownFPClass.h @@ -166,7 +166,7 @@ struct KnownFPClass { DenormalMode DenormMode = DenormalMode::getDynamic()); /// Report known values for exp, exp2 and exp10. - LLVM_ABI void exp(); + LLVM_ABI static KnownFPClass exp(const KnownFPClass &Src); /// Return true if the sign bit must be 0, ignoring the sign of nans. bool signBitIsZeroOrNaN() const { return isKnownNever(fcNegative); } diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 090d31167c08a..cf7c6796f76c7 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -5344,20 +5344,16 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts, case Intrinsic::exp2: case Intrinsic::exp10: case Intrinsic::amdgcn_exp2: { - Known.knownNot(fcNegative); + KnownFPClass KnownSrc; + computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedClasses, + KnownSrc, Q, Depth + 1); + + Known = KnownFPClass::exp(KnownSrc); Type *EltTy = II->getType()->getScalarType(); if (IID == Intrinsic::amdgcn_exp2 && EltTy->isFloatTy()) Known.knownNot(fcSubnormal); - if ((InterestedClasses & fcNan) == fcNone) - break; - - KnownFPClass KnownSrc; - computeKnownFPClass(II->getArgOperand(0), DemandedElts, InterestedClasses, - KnownSrc, Q, Depth + 1); - Known = KnownSrc; - Known.exp(); break; } case Intrinsic::fptrunc_round: { diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp index 4b8a176704060..28274aeef5f68 100644 --- a/llvm/lib/Support/KnownFPClass.cpp +++ b/llvm/lib/Support/KnownFPClass.cpp @@ -141,13 +141,13 @@ KnownFPClass KnownFPClass::canonicalize(const KnownFPClass &KnownSrc, return Known; } -void KnownFPClass::exp() { - KnownFPClass KnownSrc = *this; - *this = KnownFPClass(); +KnownFPClass KnownFPClass::exp(const KnownFPClass &KnownSrc) { + KnownFPClass Known; + Known.knownNot(fcNegative); if (KnownSrc.isKnownNeverNaN()) { - knownNot(fcNan); - signBitMustBeZero(); + Known.knownNot(fcNan); + Known.signBitMustBeZero(); } if (KnownSrc.cannotBeOrderedLessThanZero()) { diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp index 0e40510dc9bc3..f79d8f42a131e 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp @@ -2151,21 +2151,23 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Value *V, if (DemandedMask & fcPosInf) SrcDemandedMask |= fcPosInf | fcPosNormal; + KnownFPClass KnownSrc; + // TODO: This could really make use of KnownFPClass of specific value // range, (i.e., close enough to 1) - if (SimplifyDemandedFPClass(I, 0, SrcDemandedMask, Known, Depth + 1)) + if (SimplifyDemandedFPClass(I, 0, SrcDemandedMask, KnownSrc, Depth + 1)) return I; /// Propagate nnan-ness to simplify edge case checks. if ((DemandedMask & fcNan) == fcNone) - Known.knownNot(fcNan); + KnownSrc.knownNot(fcNan); // exp(+/-0) = 1 - if (Known.isKnownAlways(fcZero)) + if (KnownSrc.isKnownAlways(fcZero)) return ConstantFP::get(VTy, 1.0); // exp(0 | nan) => x == 0.0 ? 1.0 : x - if (Known.isKnownAlways(fcZero | fcNan)) { + if (KnownSrc.isKnownAlways(fcZero | fcNan)) { IRBuilderBase::InsertPointGuard Guard(Builder); Builder.SetInsertPoint(CI); @@ -2175,7 +2177,7 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Value *V, ConstantFP::get(VTy, 1.0)); } - if (Known.isKnownAlways(fcInf | fcNan)) { + if (KnownSrc.isKnownAlways(fcInf | fcNan)) { // exp(-inf) = 0 // exp(+inf) = +inf IRBuilderBase::InsertPointGuard Guard(Builder); @@ -2190,10 +2192,10 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Value *V, // Only perform nan propagation. // Note: Dropping canonicalize / quiet of signaling nan. - if (Known.isKnownAlways(fcNan)) + if (KnownSrc.isKnownAlways(fcNan)) return CI->getArgOperand(0); - Known.exp(); + Known = KnownFPClass::exp(KnownSrc); break; } case Intrinsic::canonicalize: { >From 0f528e92844c838591409f2356a517369475442f Mon Sep 17 00:00:00 2001 From: Matt Arsenault <[email protected]> Date: Mon, 29 Dec 2025 10:34:42 +0100 Subject: [PATCH 3/4] Use fcPositive --- .../lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp index f79d8f42a131e..519e321572072 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp @@ -2109,8 +2109,7 @@ Value *InstCombinerImpl::SimplifyDemandedUseFPClass(Value *V, case Intrinsic::exp: case Intrinsic::exp2: case Intrinsic::exp10: { - if ((DemandedMask & - (fcPosZero | fcPosSubnormal | fcPosNormal | fcPosInf)) == fcNone) { + if ((DemandedMask & fcPositive) == fcNone) { // Only returns positive values or nans. if ((DemandedMask & fcNan) == fcNone) return PoisonValue::get(VTy); >From 6fdbe2540e935edb36fc9c2116be0cf8e39224fb Mon Sep 17 00:00:00 2001 From: Matt Arsenault <[email protected]> Date: Mon, 29 Dec 2025 11:01:58 +0100 Subject: [PATCH 4/4] Use propagateNaN --- llvm/lib/Support/KnownFPClass.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp index 28274aeef5f68..9ca040366b611 100644 --- a/llvm/lib/Support/KnownFPClass.cpp +++ b/llvm/lib/Support/KnownFPClass.cpp @@ -145,10 +145,7 @@ KnownFPClass KnownFPClass::exp(const KnownFPClass &KnownSrc) { KnownFPClass Known; Known.knownNot(fcNegative); - if (KnownSrc.isKnownNeverNaN()) { - Known.knownNot(fcNan); - Known.signBitMustBeZero(); - } + Known.propagateNaN(KnownSrc); if (KnownSrc.cannotBeOrderedLessThanZero()) { // If the source is positive this cannot underflow. _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
