https://github.com/arsenm updated 
https://github.com/llvm/llvm-project/pull/173432

>From f4781b0811cae9218cdc9c9e26340f794e71daf6 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <[email protected]>
Date: Sat, 20 Dec 2025 12:29:20 +0100
Subject: [PATCH 1/2] 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           | 23 +----
 llvm/lib/Support/KnownFPClass.cpp             | 26 ++++++
 .../InstCombineSimplifyDemanded.cpp           | 90 +++++++++++++++++++
 .../simplify-demanded-fpclass-exp.ll          | 81 +++++++----------
 5 files changed, 155 insertions(+), 68 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownFPClass.h 
b/llvm/include/llvm/Support/KnownFPClass.h
index 7fe6197cb84aa..9f7cd3aa8bd25 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -161,6 +161,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 9a3d11eaa38c8..9f2f6f2d7491f 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5357,27 +5357,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, and cannot be ~0, this cannot underflow.
-        Known.knownNot(fcPosZero);
-
-        // Cannot introduce new denormal values.
-        if (KnownSrc.isKnownNever(fcPosSubnormal))
-          Known.knownNot(fcPosSubnormal);
-      }
-
-      if (KnownSrc.cannotBeOrderedGreaterThanZero()) {
-        // If the source is negative, and cannot be infinity, this cannot
-        // overflow to infinity.
-        if (KnownSrc.isKnownNeverPosInfinity())
-          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 556a3b165d80d..b5c7b8ab4ceda 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -137,6 +137,32 @@ 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, and cannot be ~0, this cannot underflow.
+    knownNot(fcPosZero);
+
+    // Cannot introduce new denormal values.
+    if (KnownSrc.isKnownNever(fcPosSubnormal))
+      knownNot(fcPosSubnormal);
+  }
+
+  if (KnownSrc.cannotBeOrderedGreaterThanZero()) {
+    // If the source is negative, and cannot be infinity, this cannot
+    // overflow to infinity.
+    if (KnownSrc.isKnownNeverPosInfinity())
+      knownNot(fcPosInf);
+  }
+}
+
 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 579cbd07fbc0f..b74aab3007fd4 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 c585322216aef4ee5f80399cefe8d50a3777ba82 Mon Sep 17 00:00:00 2001
From: Matt Arsenault <[email protected]>
Date: Tue, 23 Dec 2025 22:43:11 +0100
Subject: [PATCH 2/2] Make KnownFPClass::exp not side-effecting

---
 llvm/include/llvm/Support/KnownFPClass.h       |  2 +-
 llvm/lib/Analysis/ValueTracking.cpp            | 14 +++++---------
 llvm/lib/Support/KnownFPClass.cpp              | 18 ++++++++++--------
 .../InstCombineSimplifyDemanded.cpp            | 16 +++++++++-------
 4 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/llvm/include/llvm/Support/KnownFPClass.h 
b/llvm/include/llvm/Support/KnownFPClass.h
index 9f7cd3aa8bd25..3f55ae5e08f9e 100644
--- a/llvm/include/llvm/Support/KnownFPClass.h
+++ b/llvm/include/llvm/Support/KnownFPClass.h
@@ -162,7 +162,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 9f2f6f2d7491f..cb99ec0194d42 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5345,20 +5345,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 b5c7b8ab4ceda..1676196d073b4 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -137,30 +137,32 @@ 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()) {
     // If the source is positive, and cannot be ~0, this cannot underflow.
-    knownNot(fcPosZero);
+    Known.knownNot(fcPosZero);
 
     // Cannot introduce new denormal values.
     if (KnownSrc.isKnownNever(fcPosSubnormal))
-      knownNot(fcPosSubnormal);
+      Known.knownNot(fcPosSubnormal);
   }
 
   if (KnownSrc.cannotBeOrderedGreaterThanZero()) {
     // If the source is negative, and cannot be infinity, this cannot
     // overflow to infinity.
     if (KnownSrc.isKnownNeverPosInfinity())
-      knownNot(fcPosInf);
+      Known.knownNot(fcPosInf);
   }
+
+  return Known;
 }
 
 void KnownFPClass::propagateCanonicalizingSrc(const KnownFPClass &Src,
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp 
b/llvm/lib/Transforms/InstCombine/InstCombineSimplifyDemanded.cpp
index b74aab3007fd4..7fa7ecad33bb1 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: {

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to