Skip to content

ValueTracking: Improve handling of exp intrinsic for overflow#173430

Merged
arsenm merged 4 commits intomainfrom
users/arsenm/valuetracking/teach-computeKnownFPClass-exp-sign-overflow
Dec 29, 2025
Merged

ValueTracking: Improve handling of exp intrinsic for overflow#173430
arsenm merged 4 commits intomainfrom
users/arsenm/valuetracking/teach-computeKnownFPClass-exp-sign-overflow

Conversation

@arsenm
Copy link
Contributor

@arsenm arsenm commented Dec 24, 2025

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

@arsenm arsenm added floating-point Floating-point math llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:support llvm:transforms labels Dec 24, 2025 — with Graphite App
@arsenm arsenm marked this pull request as ready for review December 24, 2025 00:12
@llvmbot
Copy link
Member

llvmbot commented Dec 24, 2025

@llvm/pr-subscribers-backend-amdgpu
@llvm/pr-subscribers-llvm-support

@llvm/pr-subscribers-llvm-transforms

Author: Matt Arsenault (arsenm)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/173430.diff

2 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+16)
  • (modified) llvm/test/Transforms/Attributor/nofpclass-exp.ll (+24-24)
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)

@llvmbot
Copy link
Member

llvmbot commented Dec 24, 2025

@llvm/pr-subscribers-llvm-analysis

Author: Matt Arsenault (arsenm)

Changes

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


Full diff: https://github.com/llvm/llvm-project/pull/173430.diff

2 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+16)
  • (modified) llvm/test/Transforms/Attributor/nofpclass-exp.ll (+24-24)
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)

@github-actions
Copy link

github-actions bot commented Dec 24, 2025

🪟 Windows x64 Test Results

  • 128970 tests passed
  • 2842 tests skipped

✅ The build succeeded and all tests passed.

@github-actions
Copy link

github-actions bot commented Dec 24, 2025

🐧 Linux x64 Test Results

  • 187911 tests passed
  • 4989 tests skipped

✅ The build succeeded and all tests passed.

@arsenm arsenm force-pushed the users/arsenm/valuetracking/add-tests-computeknownfpclass-exp-positive-negative branch from d8af1dc to 9632f32 Compare December 24, 2025 10:15
@arsenm arsenm force-pushed the users/arsenm/valuetracking/teach-computeKnownFPClass-exp-sign-overflow branch from 9435649 to 9c56f96 Compare December 24, 2025 10:15
@arsenm arsenm force-pushed the users/arsenm/valuetracking/add-tests-computeknownfpclass-exp-positive-negative branch from 9632f32 to bb978d6 Compare December 24, 2025 21:48
@arsenm arsenm force-pushed the users/arsenm/valuetracking/teach-computeKnownFPClass-exp-sign-overflow branch from 9c56f96 to 174db36 Compare December 24, 2025 21:48
Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG

@arsenm arsenm force-pushed the users/arsenm/valuetracking/teach-computeKnownFPClass-exp-sign-overflow branch from 174db36 to 650471c Compare December 29, 2025 10:08
@arsenm arsenm force-pushed the users/arsenm/valuetracking/add-tests-computeknownfpclass-exp-positive-negative branch from bb978d6 to 50298d2 Compare December 29, 2025 10:08
Base automatically changed from users/arsenm/valuetracking/add-tests-computeknownfpclass-exp-positive-negative to main December 29, 2025 13:45
Teach exp handling that positive inputs cannot introduce overflow,
and negative inputs cannot introduce underflow.
@arsenm arsenm force-pushed the users/arsenm/valuetracking/teach-computeKnownFPClass-exp-sign-overflow branch from 650471c to d45cd7d Compare December 29, 2025 13:46
@arsenm arsenm enabled auto-merge (squash) December 29, 2025 13:50
@arsenm arsenm merged commit 3b09719 into main Dec 29, 2025
9 of 10 checks passed
@arsenm arsenm deleted the users/arsenm/valuetracking/teach-computeKnownFPClass-exp-sign-overflow branch December 29, 2025 14:24
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Jan 6, 2026
…73430)

Teach exp handling that positive inputs cannot introduce overflow,
and negative inputs cannot introduce underflow.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend:AMDGPU floating-point Floating-point math llvm:analysis Includes value tracking, cost tables and constant folding llvm:instcombine Covers the InstCombine, InstSimplify and AggressiveInstCombine passes llvm:support llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants