[llvm-branch-commits] [llvm] InstCombine: Handle exp/exp2/exp10 in SimplifyDemandedFPClass (PR #173432)
Matt Arsenault via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Dec 29 02:09:08 PST 2025
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 <Matthew.Arsenault at amd.com>
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 <Matthew.Arsenault at amd.com>
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 <Matthew.Arsenault at amd.com>
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 <Matthew.Arsenault at amd.com>
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.
More information about the llvm-branch-commits
mailing list