[llvm] [KnownFPClass] Fix canonicalize incorrectly dropping fcNegZero under positive-zero denormal mode (PR #202268)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jun 9 08:06:13 PDT 2026


https://github.com/lijinpei-amd updated https://github.com/llvm/llvm-project/pull/202268

>From a3ed55fae62fa6b8a8cc50e6aa57d757774e7f3d Mon Sep 17 00:00:00 2001
From: Li Jinpei <jinpli at amd.com>
Date: Sat, 6 Jun 2026 21:39:31 +0800
Subject: [PATCH 1/2] [KnownFPClass] Fix canonicalize incorrectly dropping
 fcNegZero under positive-zero denormal mode

The denormal mode only flushes *denormal* (subnormal) values; -0.0 is not
a denormal, and per LangRef canonicalize must conserve the sign of zero
(canonicalize(-0.0) == -0.0).

Alive2 (InstCombine fold of canonicalize on a {+/-0, nan} value):
  before (miscompiles -0.0 -> +0.0): https://alive2.llvm.org/ce/z/ZRK-sr
  after  (verifies):                 https://alive2.llvm.org/ce/z/L3tPu3
---
 llvm/lib/Support/KnownFPClass.cpp             | 11 ++++--
 .../Attributor/nofpclass-canonicalize.ll      | 36 +++++++++----------
 .../simplify-demanded-fpclass-canonicalize.ll |  2 +-
 3 files changed, 28 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index 3e2f6a1704b01..b260567ea950f 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -228,8 +228,15 @@ KnownFPClass KnownFPClass::canonicalize(const KnownFPClass &KnownSrc,
 
   if (DenormMode.Input == DenormalMode::PositiveZero ||
       (DenormMode.Output == DenormalMode::PositiveZero &&
-       DenormMode.Input == DenormalMode::IEEE))
-    Known.knownNot(fcNegZero);
+       DenormMode.Input == DenormalMode::IEEE)) {
+    // -0.0 is not a subnormal, not governed by DenormalMode and canonicalize
+    // conserves its sign (canonicalize(-0.0) == -0.0).
+    if (KnownSrc.isKnownNever(fcNegZero))
+      Known.knownNot(fcNegZero);
+
+    if (KnownSrc.isKnownNever(fcPosZero | fcSubnormal))
+      Known.knownNot(fcPosZero);
+  }
 
   return Known;
 }
diff --git a/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll b/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll
index 3ff0505e868a6..f934cdc899b43 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-canonicalize.ll
@@ -44,9 +44,9 @@ define float @ret_canonicalize_daz_denormal(float nofpclass(inf) %arg0) denormal
 }
 
 define float @ret_canonicalize_dapz_denormal(float nofpclass(inf) %arg0) denormal_fpenv(dynamic|positivezero) {
-; CHECK-LABEL: define nofpclass(snan inf nzero sub) float @ret_canonicalize_dapz_denormal
+; CHECK-LABEL: define nofpclass(snan inf sub) float @ret_canonicalize_dapz_denormal
 ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR4:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf nzero sub) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf sub) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float %arg0)
@@ -64,9 +64,9 @@ define float @ret_canonicalize_ftpz_dapz_denormal(float nofpclass(inf) %arg0) de
 }
 
 define float @ret_canonicalize_ftpz_ieee_denormal(float nofpclass(inf) %arg0) denormal_fpenv(positivezero|ieee) {
-; CHECK-LABEL: define nofpclass(snan inf nzero sub) float @ret_canonicalize_ftpz_ieee_denormal
+; CHECK-LABEL: define nofpclass(snan inf sub) float @ret_canonicalize_ftpz_ieee_denormal
 ; CHECK-SAME: (float nofpclass(inf) [[ARG0:%.*]]) #[[ATTR6:[0-9]+]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf nzero sub) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf sub) float @llvm.canonicalize.f32(float nofpclass(inf) [[ARG0]]) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float %arg0)
@@ -334,9 +334,9 @@ define float @ret_canonicalize_dapz_constant_pos_zero() denormal_fpenv(positivez
 }
 
 define float @ret_canonicalize_dapz_constant_neg_zero() denormal_fpenv(positivezero|positivezero) {
-; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub norm) float @ret_canonicalize_dapz_constant_neg_zero
+; CHECK-LABEL: define noundef nofpclass(nan inf pzero sub norm) float @ret_canonicalize_dapz_constant_neg_zero
 ; CHECK-SAME: () #[[ATTR11]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero 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)
@@ -344,9 +344,9 @@ define float @ret_canonicalize_dapz_constant_neg_zero() denormal_fpenv(positivez
 }
 
 define float @ret_canonicalize_dapz_constant_pos_normal() denormal_fpenv(positivezero|positivezero) {
-; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub nnorm) float @ret_canonicalize_dapz_constant_pos_normal
+; CHECK-LABEL: define noundef nofpclass(nan inf zero sub nnorm) float @ret_canonicalize_dapz_constant_pos_normal
 ; CHECK-SAME: () #[[ATTR11]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero 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)
@@ -354,9 +354,9 @@ define float @ret_canonicalize_dapz_constant_pos_normal() denormal_fpenv(positiv
 }
 
 define float @ret_canonicalize_dapz_constant_neg_normal() denormal_fpenv(positivezero|positivezero) {
-; CHECK-LABEL: define noundef nofpclass(nan inf nzero sub pnorm) float @ret_canonicalize_dapz_constant_neg_normal
+; CHECK-LABEL: define noundef nofpclass(nan inf zero sub pnorm) float @ret_canonicalize_dapz_constant_neg_normal
 ; CHECK-SAME: () #[[ATTR11]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan inf nzero 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)
@@ -364,9 +364,9 @@ define float @ret_canonicalize_dapz_constant_neg_normal() denormal_fpenv(positiv
 }
 
 define float @ret_canonicalize_dapz_constant_pos_inf() denormal_fpenv(positivezero|positivezero) {
-; CHECK-LABEL: define noundef nofpclass(nan ninf nzero sub norm) float @ret_canonicalize_dapz_constant_pos_inf
+; CHECK-LABEL: define noundef nofpclass(nan ninf zero sub norm) float @ret_canonicalize_dapz_constant_pos_inf
 ; CHECK-SAME: () #[[ATTR11]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan ninf nzero sub norm) float @llvm.canonicalize.f32(float noundef +inf) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan ninf zero sub norm) float @llvm.canonicalize.f32(float noundef +inf) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 0x7FF0000000000000)
@@ -374,9 +374,9 @@ define float @ret_canonicalize_dapz_constant_pos_inf() denormal_fpenv(positiveze
 }
 
 define float @ret_canonicalize_dapz_constant_neg_inf() denormal_fpenv(positivezero|positivezero) {
-; CHECK-LABEL: define noundef nofpclass(nan pinf nzero sub norm) float @ret_canonicalize_dapz_constant_neg_inf
+; CHECK-LABEL: define noundef nofpclass(nan pinf zero sub norm) float @ret_canonicalize_dapz_constant_neg_inf
 ; CHECK-SAME: () #[[ATTR11]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan pinf nzero sub norm) float @llvm.canonicalize.f32(float noundef -inf) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(nan pinf zero sub norm) float @llvm.canonicalize.f32(float noundef -inf) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 0xFFF0000000000000)
@@ -384,9 +384,9 @@ define float @ret_canonicalize_dapz_constant_neg_inf() denormal_fpenv(positiveze
 }
 
 define float @ret_canonicalize_dapz_constant_qnan() denormal_fpenv(positivezero|positivezero) {
-; CHECK-LABEL: define noundef nofpclass(snan inf nzero sub norm) float @ret_canonicalize_dapz_constant_qnan
+; CHECK-LABEL: define noundef nofpclass(snan inf zero sub norm) float @ret_canonicalize_dapz_constant_qnan
 ; CHECK-SAME: () #[[ATTR11]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef +qnan) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf zero sub norm) float @llvm.canonicalize.f32(float noundef +qnan) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 0x7FF8000000000000)
@@ -394,9 +394,9 @@ define float @ret_canonicalize_dapz_constant_qnan() denormal_fpenv(positivezero|
 }
 
 define float @ret_canonicalize_dapz_constant_snan() denormal_fpenv(positivezero|positivezero) {
-; CHECK-LABEL: define noundef nofpclass(snan inf nzero sub norm) float @ret_canonicalize_dapz_constant_snan
+; CHECK-LABEL: define noundef nofpclass(snan inf zero sub norm) float @ret_canonicalize_dapz_constant_snan
 ; CHECK-SAME: () #[[ATTR11]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf nzero sub norm) float @llvm.canonicalize.f32(float noundef +snan(0x80000)) #[[ATTR12]]
+; CHECK-NEXT:    [[CALL:%.*]] = call noundef nofpclass(snan inf zero sub norm) float @llvm.canonicalize.f32(float noundef +snan(0x80000)) #[[ATTR12]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.canonicalize.f32(float 0x7FF1000000000000)
diff --git a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll
index 8a380567b61ec..f7a4348685f4c 100644
--- a/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll
+++ b/llvm/test/Transforms/InstCombine/simplify-demanded-fpclass-canonicalize.ll
@@ -561,7 +561,7 @@ define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_zero__dynamic(
 define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_zero__dapz(i1 %cond, float nofpclass(sub norm inf) %must.be.zero.or.nan) #2 {
 ; CHECK-LABEL: define nofpclass(nan) float @ret_nofpclass_nan__canonicalize_only_zero__dapz(
 ; CHECK-SAME: i1 [[COND:%.*]], float nofpclass(inf sub norm) [[MUST_BE_ZERO_OR_NAN:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    ret float 0.000000e+00
+; CHECK-NEXT:    ret float [[MUST_BE_ZERO_OR_NAN]]
 ;
   %canon = call float @llvm.canonicalize.f32(float %must.be.zero.or.nan)
   ret float %canon

>From fadfaf606375a107e824a39fbebdf5345a8c9090 Mon Sep 17 00:00:00 2001
From: lijinpei-amd <jinpei.li at amd.com>
Date: Tue, 9 Jun 2026 23:06:03 +0800
Subject: [PATCH 2/2] Update llvm/lib/Support/KnownFPClass.cpp

Co-authored-by: Matt Arsenault <arsenm2 at gmail.com>
---
 llvm/lib/Support/KnownFPClass.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index b260567ea950f..0aca7b7afbe29 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -229,8 +229,7 @@ KnownFPClass KnownFPClass::canonicalize(const KnownFPClass &KnownSrc,
   if (DenormMode.Input == DenormalMode::PositiveZero ||
       (DenormMode.Output == DenormalMode::PositiveZero &&
        DenormMode.Input == DenormalMode::IEEE)) {
-    // -0.0 is not a subnormal, not governed by DenormalMode and canonicalize
-    // conserves its sign (canonicalize(-0.0) == -0.0).
+    // -0.0 is not a subnormal and should not be flushed.
     if (KnownSrc.isKnownNever(fcNegZero))
       Known.knownNot(fcNegZero);
 



More information about the llvm-commits mailing list