Skip to content

Conversation

@arsenm
Copy link
Contributor

@arsenm arsenm commented Dec 16, 2025

The base exp handling looks pretty incomplete.

The base exp handling looks pretty incomplete.
Copy link
Contributor Author

arsenm commented Dec 16, 2025

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

This stack of pull requests is managed by Graphite. Learn more about stacking.

@arsenm arsenm added backend:AMDGPU floating-point Floating-point math labels Dec 16, 2025 — with Graphite App
@arsenm arsenm marked this pull request as ready for review December 16, 2025 15:04
@arsenm arsenm requested a review from nikic as a code owner December 16, 2025 15:04
@llvmbot llvmbot added llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Dec 16, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 16, 2025

@llvm/pr-subscribers-llvm-analysis
@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-backend-amdgpu

Author: Matt Arsenault (arsenm)

Changes

The base exp handling looks pretty incomplete.


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

2 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+6-1)
  • (modified) llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-exp.ll (+24-24)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 045cbab221ac3..0b57e27c92331 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5374,7 +5374,8 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
     }
     case Intrinsic::exp:
     case Intrinsic::exp2:
-    case Intrinsic::exp10: {
+    case Intrinsic::exp10:
+    case Intrinsic::amdgcn_exp2: {
       Known.knownNot(fcNegative);
       if ((InterestedClasses & fcNan) == fcNone)
         break;
@@ -5387,6 +5388,10 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
         Known.signBitMustBeZero();
       }
 
+      Type *EltTy = II->getType()->getScalarType();
+      if (IID == Intrinsic::amdgcn_exp2 && EltTy->isFloatTy())
+        Known.knownNot(fcSubnormal);
+
       break;
     }
     case Intrinsic::fptrunc_round: {
diff --git a/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-exp.ll b/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-exp.ll
index babd1c68f161d..e7c41e9be7e33 100644
--- a/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-exp.ll
+++ b/llvm/test/Transforms/Attributor/AMDGPU/nofpclass-amdgcn-exp.ll
@@ -4,9 +4,9 @@
 declare float @llvm.amdgcn.exp2.f32(float)
 
 define half @ret_exp_f16(half %arg0) {
-; CHECK-LABEL: define half @ret_exp_f16(
+; CHECK-LABEL: define nofpclass(ninf nzero nsub nnorm) half @ret_exp_f16(
 ; CHECK-SAME: half [[ARG0:%.*]]) #[[ATTR1:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call half @llvm.amdgcn.exp2.f16(half [[ARG0]]) #[[ATTR2:[0-9]+]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero nsub nnorm) half @llvm.amdgcn.exp2.f16(half [[ARG0]]) #[[ATTR2:[0-9]+]]
 ; CHECK-NEXT:    ret half [[CALL]]
 ;
   %call = call half @llvm.amdgcn.exp2.f16(half %arg0)
@@ -14,9 +14,9 @@ define half @ret_exp_f16(half %arg0) {
 }
 
 define float @ret_exp_f32(float %arg0) {
-; CHECK-LABEL: define float @ret_exp_f32(
+; CHECK-LABEL: define nofpclass(ninf nzero sub nnorm) float @ret_exp_f32(
 ; CHECK-SAME: float [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.amdgcn.exp2.f32(float [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero sub nnorm) float @llvm.amdgcn.exp2.f32(float [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.amdgcn.exp2.f32(float %arg0)
@@ -24,9 +24,9 @@ define float @ret_exp_f32(float %arg0) {
 }
 
 define float @ret_exp_noinf(float nofpclass(inf) %arg0) {
-; CHECK-LABEL: define float @ret_exp_noinf(
+; CHECK-LABEL: define nofpclass(ninf nzero sub nnorm) float @ret_exp_noinf(
 ; CHECK-SAME: float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.amdgcn.exp2.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero sub nnorm) float @llvm.amdgcn.exp2.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.amdgcn.exp2.f32(float %arg0)
@@ -34,9 +34,9 @@ define float @ret_exp_noinf(float nofpclass(inf) %arg0) {
 }
 
 define float @ret_exp_nopinf(float nofpclass(pinf) %arg0) {
-; CHECK-LABEL: define float @ret_exp_nopinf(
+; CHECK-LABEL: define nofpclass(ninf nzero sub nnorm) float @ret_exp_nopinf(
 ; CHECK-SAME: float nofpclass(pinf) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.amdgcn.exp2.f32(float nofpclass(pinf) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero sub nnorm) float @llvm.amdgcn.exp2.f32(float nofpclass(pinf) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.amdgcn.exp2.f32(float %arg0)
@@ -44,9 +44,9 @@ define float @ret_exp_nopinf(float nofpclass(pinf) %arg0) {
 }
 
 define float @ret_exp_noninf(float nofpclass(ninf) %arg0) {
-; CHECK-LABEL: define float @ret_exp_noninf(
+; CHECK-LABEL: define nofpclass(ninf nzero sub nnorm) float @ret_exp_noninf(
 ; CHECK-SAME: float nofpclass(ninf) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.amdgcn.exp2.f32(float nofpclass(ninf) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero sub nnorm) float @llvm.amdgcn.exp2.f32(float nofpclass(ninf) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.amdgcn.exp2.f32(float %arg0)
@@ -54,9 +54,9 @@ define float @ret_exp_noninf(float nofpclass(ninf) %arg0) {
 }
 
 define float @ret_exp_nonan(float nofpclass(nan) %arg0) {
-; CHECK-LABEL: define float @ret_exp_nonan(
+; CHECK-LABEL: define nofpclass(nan ninf nzero sub nnorm) float @ret_exp_nonan(
 ; CHECK-SAME: float nofpclass(nan) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.amdgcn.exp2.f32(float nofpclass(nan) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero sub nnorm) float @llvm.amdgcn.exp2.f32(float nofpclass(nan) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.amdgcn.exp2.f32(float %arg0)
@@ -64,9 +64,9 @@ define float @ret_exp_nonan(float nofpclass(nan) %arg0) {
 }
 
 define float @ret_exp_nonan_noinf(float nofpclass(nan inf) %arg0) {
-; CHECK-LABEL: define float @ret_exp_nonan_noinf(
+; CHECK-LABEL: define nofpclass(nan ninf nzero sub nnorm) float @ret_exp_nonan_noinf(
 ; CHECK-SAME: float nofpclass(nan inf) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.amdgcn.exp2.f32(float nofpclass(nan inf) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero sub nnorm) float @llvm.amdgcn.exp2.f32(float nofpclass(nan inf) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.amdgcn.exp2.f32(float %arg0)
@@ -74,9 +74,9 @@ define float @ret_exp_nonan_noinf(float nofpclass(nan inf) %arg0) {
 }
 
 define float @ret_exp_nonan_noinf_nozero(float nofpclass(nan inf zero) %arg0) {
-; CHECK-LABEL: define float @ret_exp_nonan_noinf_nozero(
+; CHECK-LABEL: define nofpclass(nan ninf nzero sub nnorm) float @ret_exp_nonan_noinf_nozero(
 ; CHECK-SAME: float nofpclass(nan inf zero) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.amdgcn.exp2.f32(float nofpclass(nan inf zero) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero sub nnorm) float @llvm.amdgcn.exp2.f32(float nofpclass(nan inf zero) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.amdgcn.exp2.f32(float %arg0)
@@ -84,9 +84,9 @@ define float @ret_exp_nonan_noinf_nozero(float nofpclass(nan inf zero) %arg0) {
 }
 
 define float @ret_exp_noinf_nozero(float nofpclass(inf zero) %arg0) {
-; CHECK-LABEL: define float @ret_exp_noinf_nozero(
+; CHECK-LABEL: define nofpclass(ninf nzero sub nnorm) float @ret_exp_noinf_nozero(
 ; CHECK-SAME: float nofpclass(inf zero) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.amdgcn.exp2.f32(float nofpclass(inf zero) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero sub nnorm) float @llvm.amdgcn.exp2.f32(float nofpclass(inf zero) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.amdgcn.exp2.f32(float %arg0)
@@ -94,9 +94,9 @@ define float @ret_exp_noinf_nozero(float nofpclass(inf zero) %arg0) {
 }
 
 define float @ret_exp_noinf_nonegzero(float nofpclass(inf nzero) %arg0) {
-; CHECK-LABEL: define float @ret_exp_noinf_nonegzero(
+; CHECK-LABEL: define nofpclass(ninf nzero sub nnorm) float @ret_exp_noinf_nonegzero(
 ; CHECK-SAME: float nofpclass(inf nzero) [[ARG0:%.*]]) #[[ATTR1]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.amdgcn.exp2.f32(float nofpclass(inf nzero) [[ARG0]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(ninf nzero sub nnorm) float @llvm.amdgcn.exp2.f32(float nofpclass(inf nzero) [[ARG0]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.amdgcn.exp2.f32(float %arg0)
@@ -104,10 +104,10 @@ define float @ret_exp_noinf_nonegzero(float nofpclass(inf nzero) %arg0) {
 }
 
 define float @ret_exp_positive_source(i32 %arg) {
-; CHECK-LABEL: define float @ret_exp_positive_source(
+; CHECK-LABEL: define nofpclass(nan ninf nzero sub nnorm) float @ret_exp_positive_source(
 ; CHECK-SAME: i32 [[ARG:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[UITOFP:%.*]] = uitofp i32 [[ARG]] to float
-; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.amdgcn.exp2.f32(float [[UITOFP]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero sub nnorm) float @llvm.amdgcn.exp2.f32(float [[UITOFP]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %uitofp = uitofp i32 %arg to float
@@ -117,10 +117,10 @@ define float @ret_exp_positive_source(i32 %arg) {
 
 ; Could produce a nan because we don't know if the multiply is negative.
 define float @ret_exp_unknown_sign(float nofpclass(nan) %arg0, float nofpclass(nan) %arg1) {
-; CHECK-LABEL: define float @ret_exp_unknown_sign(
+; CHECK-LABEL: define nofpclass(nan ninf nzero sub nnorm) float @ret_exp_unknown_sign(
 ; CHECK-SAME: float nofpclass(nan) [[ARG0:%.*]], float nofpclass(nan) [[ARG1:%.*]]) #[[ATTR1]] {
 ; CHECK-NEXT:    [[UNKNOWN_SIGN_NOT_NAN:%.*]] = fmul nnan float [[ARG0]], [[ARG1]]
-; CHECK-NEXT:    [[CALL:%.*]] = call float @llvm.amdgcn.exp2.f32(float [[UNKNOWN_SIGN_NOT_NAN]]) #[[ATTR2]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(nan ninf nzero sub nnorm) float @llvm.amdgcn.exp2.f32(float [[UNKNOWN_SIGN_NOT_NAN]]) #[[ATTR2]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %unknown.sign.not.nan = fmul nnan float %arg0, %arg1

case Intrinsic::exp10:
case Intrinsic::amdgcn_exp2: {
Known.knownNot(fcNegative);
if ((InterestedClasses & fcNan) == fcNone)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if ((InterestedClasses & fcNan) == fcNone)
if ((InterestedClasses & (fcNan | fcSubnormal)) == fcNone)

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:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants