Skip to content

Conversation

@arsenm
Copy link
Contributor

@arsenm arsenm commented Dec 20, 2025

If the source value is known not subnormal and not zero with the
same sign, we can infer the result is also not zero with the same
sign.

If the source value is known not subnormal and not zero with the
same sign, we can infer the result is also not zero with the same
sign.
Copy link
Contributor Author

arsenm commented Dec 20, 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 the floating-point Floating-point math label Dec 20, 2025 — with Graphite App
@arsenm arsenm marked this pull request as ready for review December 20, 2025 19:58
@arsenm arsenm requested a review from nikic as a code owner December 20, 2025 19:58
@llvmbot llvmbot added llvm:analysis Includes value tracking, cost tables and constant folding llvm:transforms labels Dec 20, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 20, 2025

@llvm/pr-subscribers-llvm-analysis

@llvm/pr-subscribers-llvm-transforms

Author: Matt Arsenault (arsenm)

Changes

If the source value is known not subnormal and not zero with the
same sign, we can infer the result is also not zero with the same
sign.


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

2 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+8)
  • (modified) llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll (+26-26)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index d3d2a3db66ddc..0321c08f55776 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -5308,6 +5308,14 @@ void computeKnownFPClass(const Value *V, const APInt &DemandedElts,
       if (DenormMode.inputsAreZero() || DenormMode.outputsAreZero())
         Known.knownNot(fcSubnormal);
 
+      if (DenormMode == DenormalMode::getPreserveSign()) {
+        if (KnownSrc.isKnownNever(fcPosZero | fcPosSubnormal))
+          Known.knownNot(fcPosZero);
+        if (KnownSrc.isKnownNever(fcNegZero | fcNegSubnormal))
+          Known.knownNot(fcNegZero);
+        break;
+      }
+
       if (DenormMode.Input == DenormalMode::PositiveZero ||
           (DenormMode.Output == DenormalMode::PositiveZero &&
            DenormMode.Input == DenormalMode::IEEE))
diff --git a/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll b/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll
index 8a31bef83b68e..d1879cbb9add9 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll
@@ -204,9 +204,9 @@ define float @ret_canonicalize_ieee_constant_snan() "denormal-fp-math"="ieee,iee
 }
 
 define float @ret_canonicalize_daz_constant_pos_denormal() "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define noundef nofpclass(nan inf sub norm) float @ret_canonicalize_daz_constant_pos_denormal
+; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @ret_canonicalize_daz_constant_pos_denormal
 ; CHECK-SAME: () #[[ATTR10:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf sub norm) float @llvm.canonicalize.f32(float noundef 0x36A0000000000000) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0x36A0000000000000) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 0x36A0000000000000)
@@ -214,9 +214,9 @@ define float @ret_canonicalize_daz_constant_pos_denormal() "denormal-fp-math"="p
 }
 
 define float @ret_canonicalize_daz_constant_neg_denormal() "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define noundef nofpclass(nan inf sub norm) float @ret_canonicalize_daz_constant_neg_denormal
+; CHECK-LABEL: define noundef nofpclass(nan inf pzero sub norm) float @ret_canonicalize_daz_constant_neg_denormal
 ; CHECK-SAME: () #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf sub norm) float @llvm.canonicalize.f32(float noundef 0xB6A0000000000000) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf pzero sub norm) float @llvm.canonicalize.f32(float noundef 0xB6A0000000000000) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 0xb6A0000000000000)
@@ -224,9 +224,9 @@ define float @ret_canonicalize_daz_constant_neg_denormal() "denormal-fp-math"="p
 }
 
 define float @ret_canonicalize_daz_constant_pos_zero() "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define noundef nofpclass(nan inf sub norm) float @ret_canonicalize_daz_constant_pos_zero
+; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @ret_canonicalize_daz_constant_pos_zero
 ; CHECK-SAME: () #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf sub norm) float @llvm.canonicalize.f32(float noundef 0.000000e+00) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef 0.000000e+00) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 0.0)
@@ -234,9 +234,9 @@ define float @ret_canonicalize_daz_constant_pos_zero() "denormal-fp-math"="prese
 }
 
 define float @ret_canonicalize_daz_constant_neg_zero() "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define noundef nofpclass(nan inf sub norm) float @ret_canonicalize_daz_constant_neg_zero
+; CHECK-LABEL: define noundef nofpclass(nan inf pzero sub norm) float @ret_canonicalize_daz_constant_neg_zero
 ; CHECK-SAME: () #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf sub norm) float @llvm.canonicalize.f32(float noundef -0.000000e+00) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf pzero sub norm) float @llvm.canonicalize.f32(float noundef -0.000000e+00) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float -0.0)
@@ -244,9 +244,9 @@ define float @ret_canonicalize_daz_constant_neg_zero() "denormal-fp-math"="prese
 }
 
 define float @ret_canonicalize_daz_constant_pos_normal() "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define noundef nofpclass(nan inf sub nnorm) float @ret_canonicalize_daz_constant_pos_normal
+; CHECK-LABEL: define noundef nofpclass(nan inf zero sub nnorm) float @ret_canonicalize_daz_constant_pos_normal
 ; CHECK-SAME: () #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf sub nnorm) float @llvm.canonicalize.f32(float noundef 8.000000e+00) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf zero sub nnorm) float @llvm.canonicalize.f32(float noundef 8.000000e+00) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 8.0)
@@ -254,9 +254,9 @@ define float @ret_canonicalize_daz_constant_pos_normal() "denormal-fp-math"="pre
 }
 
 define float @ret_canonicalize_daz_constant_neg_normal() "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define noundef nofpclass(nan inf sub pnorm) float @ret_canonicalize_daz_constant_neg_normal
+; CHECK-LABEL: define noundef nofpclass(nan inf zero sub pnorm) float @ret_canonicalize_daz_constant_neg_normal
 ; CHECK-SAME: () #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf sub pnorm) float @llvm.canonicalize.f32(float noundef -8.000000e+00) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf zero sub pnorm) float @llvm.canonicalize.f32(float noundef -8.000000e+00) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float -8.0)
@@ -264,9 +264,9 @@ define float @ret_canonicalize_daz_constant_neg_normal() "denormal-fp-math"="pre
 }
 
 define float @ret_canonicalize_daz_constant_pos_inf() "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define noundef nofpclass(nan ninf sub norm) float @ret_canonicalize_daz_constant_pos_inf
+; CHECK-LABEL: define noundef nofpclass(nan ninf zero sub norm) float @ret_canonicalize_daz_constant_pos_inf
 ; CHECK-SAME: () #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan ninf sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF0000000000000) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan ninf zero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF0000000000000) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 0x7FF0000000000000)
@@ -274,9 +274,9 @@ define float @ret_canonicalize_daz_constant_pos_inf() "denormal-fp-math"="preser
 }
 
 define float @ret_canonicalize_daz_constant_neg_inf() "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define noundef nofpclass(nan pinf sub norm) float @ret_canonicalize_daz_constant_neg_inf
+; CHECK-LABEL: define noundef nofpclass(nan pinf zero sub norm) float @ret_canonicalize_daz_constant_neg_inf
 ; CHECK-SAME: () #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan pinf sub norm) float @llvm.canonicalize.f32(float noundef 0xFFF0000000000000) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan pinf zero sub norm) float @llvm.canonicalize.f32(float noundef 0xFFF0000000000000) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 0xFFF0000000000000)
@@ -284,9 +284,9 @@ define float @ret_canonicalize_daz_constant_neg_inf() "denormal-fp-math"="preser
 }
 
 define float @ret_canonicalize_daz_constant_qnan() "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define noundef nofpclass(snan inf sub norm) float @ret_canonicalize_daz_constant_qnan
+; CHECK-LABEL: define noundef nofpclass(snan inf zero sub norm) float @ret_canonicalize_daz_constant_qnan
 ; CHECK-SAME: () #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF8000000000000) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf zero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF8000000000000) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 0x7FF8000000000000)
@@ -294,9 +294,9 @@ define float @ret_canonicalize_daz_constant_qnan() "denormal-fp-math"="preserve-
 }
 
 define float @ret_canonicalize_daz_constant_snan() "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define noundef nofpclass(snan inf sub norm) float @ret_canonicalize_daz_constant_snan
+; CHECK-LABEL: define noundef nofpclass(snan inf zero sub norm) float @ret_canonicalize_daz_constant_snan
 ; CHECK-SAME: () #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF1000000000000) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf zero sub norm) float @llvm.canonicalize.f32(float noundef 0x7FF1000000000000) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 0x7FF1000000000000)
@@ -504,9 +504,9 @@ define float @ret_canonicalize_dynamic_constant_snan() "denormal-fp-math"="dynam
 }
 
 define float @ret_canonicalize_daz_not_nzero_not_nsub(float nofpclass(nzero nsub) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define nofpclass(snan sub) float @ret_canonicalize_daz_not_nzero_not_nsub
+; CHECK-LABEL: define nofpclass(snan nzero sub) float @ret_canonicalize_daz_not_nzero_not_nsub
 ; CHECK-SAME: (float nofpclass(nzero nsub) [[X:%.*]]) #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan sub) float @llvm.canonicalize.f32(float nofpclass(nzero nsub) [[X]]) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan nzero sub) float @llvm.canonicalize.f32(float nofpclass(nzero nsub) [[X]]) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float %x)
@@ -534,9 +534,9 @@ define float @ret_canonicalize_daz_not_nzero(float nofpclass(nzero) %x) "denorma
 }
 
 define float @ret_canonicalize_daz_not_pzero_not_psub(float nofpclass(pzero psub) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define nofpclass(snan sub) float @ret_canonicalize_daz_not_pzero_not_psub
+; CHECK-LABEL: define nofpclass(snan pzero sub) float @ret_canonicalize_daz_not_pzero_not_psub
 ; CHECK-SAME: (float nofpclass(pzero psub) [[X:%.*]]) #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan sub) float @llvm.canonicalize.f32(float nofpclass(pzero psub) [[X]]) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan pzero sub) float @llvm.canonicalize.f32(float nofpclass(pzero psub) [[X]]) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float %x)
@@ -584,9 +584,9 @@ define float @ret_canonicalize_daz_not_sub(float nofpclass(sub) %x) "denormal-fp
 }
 
 define float @ret_canonicalize_daz_not_sub_not_nzero(float nofpclass(sub nzero) %x) "denormal-fp-math"="preserve-sign,preserve-sign" {
-; CHECK-LABEL: define nofpclass(snan sub) float @ret_canonicalize_daz_not_sub_not_nzero
+; CHECK-LABEL: define nofpclass(snan nzero sub) float @ret_canonicalize_daz_not_sub_not_nzero
 ; CHECK-SAME: (float nofpclass(nzero sub) [[X:%.*]]) #[[ATTR10]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan sub) float @llvm.canonicalize.f32(float nofpclass(nzero sub) [[X]]) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan nzero sub) float @llvm.canonicalize.f32(float nofpclass(nzero sub) [[X]]) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float %x)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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.

3 participants