https://github.com/arsenm created 
https://github.com/llvm/llvm-project/pull/173430

Teach exp handling that positive inputs cannot introduce overflow,
and negative inputs cannot introduce underflow.

>From 9435649af90bab35c93101c29f7c8ce677ac8b1d Mon Sep 17 00:00:00 2001
From: Matt Arsenault <[email protected]>
Date: Sat, 20 Dec 2025 23:55:32 +0100
Subject: [PATCH] ValueTracking: Improve handling of exp intrinsic for overflow

Teach exp handling that positive inputs cannot introduce overflow,
and negative inputs cannot introduce underflow.
---
 llvm/lib/Analysis/ValueTracking.cpp           | 16 +++++++
 .../Transforms/Attributor/nofpclass-exp.ll    | 48 +++++++++----------
 2 files changed, 40 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp 
b/llvm/lib/Analysis/ValueTracking.cpp
index d866446051fe2..9a3d11eaa38c8 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5362,6 +5362,22 @@ void computeKnownFPClass(const Value *V, const APInt 
&DemandedElts,
         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);
+      }
+
       break;
     }
     case Intrinsic::fptrunc_round: {
diff --git a/llvm/test/Transforms/Attributor/nofpclass-exp.ll 
b/llvm/test/Transforms/Attributor/nofpclass-exp.ll
index ea5c26d101186..941e1d3981af5 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-exp.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-exp.ll
@@ -96,10 +96,10 @@ define float @ret_exp_noinf_nonegzero(float nofpclass(inf 
nzero) %arg0) {
 }
 
 define float @ret_exp_positive_source(i32 %arg) {
-; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) float 
@ret_exp_positive_source
+; CHECK-LABEL: define nofpclass(nan ninf zero sub nnorm) float 
@ret_exp_positive_source
 ; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[UITOFP:%.*]] = uitofp i32 [[ARG]] to float
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero nsub nnorm) 
float @llvm.exp.f32(float [[UITOFP]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf zero sub nnorm) float 
@llvm.exp.f32(float [[UITOFP]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %uitofp = uitofp i32 %arg to float
@@ -211,10 +211,10 @@ define float @ret_exp2_noinf_nonegzero(float 
nofpclass(inf nzero) %arg0) {
 }
 
 define float @ret_exp2_positive_source(i32 %arg) {
-; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) float 
@ret_exp2_positive_source
+; CHECK-LABEL: define nofpclass(nan ninf zero sub nnorm) float 
@ret_exp2_positive_source
 ; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[UITOFP:%.*]] = uitofp i32 [[ARG]] to float
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero nsub nnorm) 
float @llvm.exp2.f32(float [[UITOFP]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf zero sub nnorm) float 
@llvm.exp2.f32(float [[UITOFP]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %uitofp = uitofp i32 %arg to float
@@ -326,10 +326,10 @@ define float @ret_exp10_noinf_nonegzero(float 
nofpclass(inf nzero) %arg0) {
 }
 
 define float @ret_exp10_positive_source(i32 %arg) {
-; CHECK-LABEL: define nofpclass(nan ninf nzero nsub nnorm) float 
@ret_exp10_positive_source
+; CHECK-LABEL: define nofpclass(nan ninf zero sub nnorm) float 
@ret_exp10_positive_source
 ; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[UITOFP:%.*]] = uitofp i32 [[ARG]] to float
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero nsub nnorm) 
float @llvm.exp10.f32(float [[UITOFP]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf zero sub nnorm) float 
@llvm.exp10.f32(float [[UITOFP]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %uitofp = uitofp i32 %arg to float
@@ -352,9 +352,9 @@ define float @ret_exp10_unknown_sign(float nofpclass(nan) 
%arg0, float nofpclass
 
 ; Can infer this doesn't return any inf
 define float @ret_exp_src_known_negative_or_zero_or_nan(float nofpclass(pinf 
psub pnorm) %arg0) {
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float 
@ret_exp_src_known_negative_or_zero_or_nan
+; CHECK-LABEL: define nofpclass(inf nzero nsub nnorm) float 
@ret_exp_src_known_negative_or_zero_or_nan
 ; CHECK-SAME: (float nofpclass(pinf psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(pinf psub pnorm) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf nzero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(pinf psub pnorm) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.exp.f32(float %arg0)
@@ -363,9 +363,9 @@ define float 
@ret_exp_src_known_negative_or_zero_or_nan(float nofpclass(pinf psu
 
 ; Can infer this doesn't return any inf
 define float @ret_exp_src_known_negative_nonzero_or_nan(float nofpclass(pinf 
psub pnorm zero) %arg0) {
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float 
@ret_exp_src_known_negative_nonzero_or_nan
+; CHECK-LABEL: define nofpclass(inf nzero nsub nnorm) float 
@ret_exp_src_known_negative_nonzero_or_nan
 ; CHECK-SAME: (float nofpclass(pinf zero psub pnorm) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(pinf zero psub pnorm) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf nzero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(pinf zero psub pnorm) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.exp.f32(float %arg0)
@@ -373,9 +373,9 @@ define float 
@ret_exp_src_known_negative_nonzero_or_nan(float nofpclass(pinf psu
 }
 
 define float @ret_exp_src_known_positive_or_zero_or_nan(float nofpclass(ninf 
nsub nnorm) %arg0) {
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float 
@ret_exp_src_known_positive_or_zero_or_nan
+; CHECK-LABEL: define nofpclass(ninf zero nsub nnorm) float 
@ret_exp_src_known_positive_or_zero_or_nan
 ; CHECK-SAME: (float nofpclass(ninf nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(ninf nsub nnorm) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf zero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(ninf nsub nnorm) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.exp.f32(float %arg0)
@@ -383,9 +383,9 @@ define float 
@ret_exp_src_known_positive_or_zero_or_nan(float nofpclass(ninf nsu
 }
 
 define float @ret_exp_src_known_positive_non0_or_nan(float nofpclass(ninf nsub 
nnorm zero) %arg0) {
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float 
@ret_exp_src_known_positive_non0_or_nan
+; CHECK-LABEL: define nofpclass(ninf zero nsub nnorm) float 
@ret_exp_src_known_positive_non0_or_nan
 ; CHECK-SAME: (float nofpclass(ninf zero nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(ninf zero nsub nnorm) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf zero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(ninf zero nsub nnorm) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.exp.f32(float %arg0)
@@ -394,9 +394,9 @@ define float @ret_exp_src_known_positive_non0_or_nan(float 
nofpclass(ninf nsub n
 
 ; Can't underflow to 0, can't be denormal.
 define float @ret_exp_src_known_positive_non0_nonsub_or_nan(float 
nofpclass(ninf sub nnorm zero) %arg0) {
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float 
@ret_exp_src_known_positive_non0_nonsub_or_nan
+; CHECK-LABEL: define nofpclass(ninf zero sub nnorm) float 
@ret_exp_src_known_positive_non0_nonsub_or_nan
 ; CHECK-SAME: (float nofpclass(ninf zero sub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(ninf zero sub nnorm) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf zero sub nnorm) float 
@llvm.exp.f32(float nofpclass(ninf zero sub nnorm) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.exp.f32(float %arg0)
@@ -405,9 +405,9 @@ define float 
@ret_exp_src_known_positive_non0_nonsub_or_nan(float nofpclass(ninf
 
 ; Can't underflow to denormal, but can have an input denormal stay denormal.
 define float @ret_exp_src_known_positive_or_nan(float nofpclass(ninf nsub 
nnorm) %arg0) {
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float 
@ret_exp_src_known_positive_or_nan
+; CHECK-LABEL: define nofpclass(ninf zero nsub nnorm) float 
@ret_exp_src_known_positive_or_nan
 ; CHECK-SAME: (float nofpclass(ninf nsub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(ninf nsub nnorm) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf zero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(ninf nsub nnorm) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.exp.f32(float %arg0)
@@ -415,9 +415,9 @@ define float @ret_exp_src_known_positive_or_nan(float 
nofpclass(ninf nsub nnorm)
 }
 
 define float @ret_exp_src_known_positive_nonsub_or_nan(float nofpclass(ninf 
sub nnorm) %arg0) {
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float 
@ret_exp_src_known_positive_nonsub_or_nan
+; CHECK-LABEL: define nofpclass(ninf zero sub nnorm) float 
@ret_exp_src_known_positive_nonsub_or_nan
 ; CHECK-SAME: (float nofpclass(ninf sub nnorm) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float 
@llvm.exp.f32(float nofpclass(ninf sub nnorm) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf zero sub nnorm) float 
@llvm.exp.f32(float nofpclass(ninf sub nnorm) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.exp.f32(float %arg0)
@@ -425,10 +425,10 @@ define float 
@ret_exp_src_known_positive_nonsub_or_nan(float nofpclass(ninf sub
 }
 
 define float @ret_exp_fabs(float %arg) {
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_exp_fabs
+; CHECK-LABEL: define nofpclass(ninf zero nsub nnorm) float @ret_exp_fabs
 ; CHECK-SAME: (float [[ARG:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[ARG_FABS:%.*]] = call float @llvm.fabs.f32(float [[ARG]]) 
#[[ATTR2]]
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float 
@llvm.exp.f32(float [[ARG_FABS]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf zero nsub nnorm) float 
@llvm.exp.f32(float [[ARG_FABS]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %arg.fabs = call float @llvm.fabs.f32(float %arg)
@@ -437,11 +437,11 @@ define float @ret_exp_fabs(float %arg) {
 }
 
 define float @ret_exp_fneg_fabs(float %arg) {
-; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) float @ret_exp_fneg_fabs
+; CHECK-LABEL: define nofpclass(inf nzero nsub nnorm) float @ret_exp_fneg_fabs
 ; CHECK-SAME: (float [[ARG:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[ARG_FABS:%.*]] = call float @llvm.fabs.f32(float [[ARG]]) 
#[[ATTR2]]
 ; CHECK-NEXT:    [[ARG_NEG_FABS:%.*]] = fneg float [[ARG_FABS]]
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) float 
@llvm.exp.f32(float [[ARG_NEG_FABS]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf nzero nsub nnorm) float 
@llvm.exp.f32(float [[ARG_NEG_FABS]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %arg.fabs = call float @llvm.fabs.f32(float %arg)

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

Reply via email to