[llvm] [KnownFPClass] Refine positive zero and NaN result classes for sqrt (PR #214987)

via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 8 09:38:47 PDT 2026


https://github.com/ZERICO2005 created https://github.com/llvm/llvm-project/pull/214987

Refines `KnownFPClass::sqrt` for positive zero and NaN result classes:
- Only `sqrt(x) == +0.0` iff `x` is +0.0 or `x` flushes to `+0.0` in the current  


>From 33992f90e44f52d472b1aba813b4f501c004aca0 Mon Sep 17 00:00:00 2001
From: zerico <zerico2005 at gmail.com>
Date: Sat, 8 Aug 2026 10:30:05 -0600
Subject: [PATCH] [KnownFPClass] Refine positive zero and NaN result classes
 for sqrt

Account for the input denormal mode when determining whether sqrt may
produce NaN or positive zero. This allows positive zero to be excluded when
the input cannot be a logical positive zero and NaN to be excluded when
negative subnormals are treated as zero. (logical negative zero was
handled in a prior commit)
---
 llvm/lib/Support/KnownFPClass.cpp             |  17 ++-
 .../Attributor/nofpclass-sqrt-denormal.ll     | 142 ++++++++++++++++++
 .../Transforms/Attributor/nofpclass-sqrt.ll   |  22 ++-
 3 files changed, 170 insertions(+), 11 deletions(-)
 create mode 100644 llvm/test/Transforms/Attributor/nofpclass-sqrt-denormal.ll

diff --git a/llvm/lib/Support/KnownFPClass.cpp b/llvm/lib/Support/KnownFPClass.cpp
index eccd83451a05f..5de62645ced54 100644
--- a/llvm/lib/Support/KnownFPClass.cpp
+++ b/llvm/lib/Support/KnownFPClass.cpp
@@ -556,15 +556,22 @@ KnownFPClass KnownFPClass::sqrt(const KnownFPClass &KnownSrc,
   if (KnownSrc.isKnownNever(fcSNan))
     Known.knownNot(fcSNan);
 
-  // Any negative value besides -0 returns a nan.
-  if (KnownSrc.isKnownNeverNaN() && KnownSrc.cannotBeOrderedLessThanZero())
+  // Any value < -0.0 returns NaN. However, a negative subnormal may be treated
+  // as zero depending on the input denormal mode.
+  if (KnownSrc.isKnownNever(fcNan | fcNegInf | fcNegNormal) &&
+      (KnownSrc.isKnownNever(fcNegSubnormal) || Mode.inputsAreZero()))
     Known.knownNot(fcNan);
 
-  // The only negative value that can be returned is -0 for -0 inputs.
+  // The only negative value that can be returned is -0.0 for -0.0 inputs.
   Known.knownNot(fcNegInf | fcNegSubnormal | fcNegNormal);
 
-  // If the input denormal mode could be PreserveSign, a negative
-  // subnormal input could produce a negative zero output.
+  // Only sqrt(+0.0) == +0.0. However, subnormals may also be treated as +0.0
+  // depending on the input denormal mode.
+  if (KnownSrc.isKnownNeverLogicalPosZero(Mode))
+    Known.knownNot(fcPosZero);
+
+  // Only sqrt(-0.0) == -0.0. However, negative subnormals may also be treated
+  // as -0.0 depending on the input denormal mode.
   if (KnownSrc.isKnownNeverLogicalNegZero(Mode))
     Known.knownNot(fcNegZero);
 
diff --git a/llvm/test/Transforms/Attributor/nofpclass-sqrt-denormal.ll b/llvm/test/Transforms/Attributor/nofpclass-sqrt-denormal.ll
new file mode 100644
index 0000000000000..e09e2ff0d69fb
--- /dev/null
+++ b/llvm/test/Transforms/Attributor/nofpclass-sqrt-denormal.ll
@@ -0,0 +1,142 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
+; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal -S < %s | FileCheck %s
+
+declare float @llvm.sqrt.f32(float)
+
+; sqrt cannot produce a subnormal result, so only the input denormal mode
+; affects the following tests.
+
+define float @ret_sqrt_ieee_psub(float nofpclass(nan inf zero nsub norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(nan inf zero sub nnorm) float @ret_sqrt_ieee_psub
+; CHECK-SAME: (float nofpclass(nan inf zero nsub norm) [[ARG:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(nan inf zero sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero nsub norm) [[ARG]]) #[[ATTR5:[0-9]+]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_ieee_nsub(float nofpclass(nan inf zero psub norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(snan inf zero sub nnorm) float @ret_sqrt_ieee_nsub
+; CHECK-SAME: (float nofpclass(nan inf zero psub norm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(snan inf zero sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero psub norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_ieee_sub(float nofpclass(nan inf zero norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(snan inf zero sub nnorm) float @ret_sqrt_ieee_sub
+; CHECK-SAME: (float nofpclass(nan inf zero norm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(snan inf zero sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_ieee_pzero(float nofpclass(nan inf nzero sub norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(nan inf nzero sub nnorm) float @ret_sqrt_ieee_pzero
+; CHECK-SAME: (float nofpclass(nan inf nzero sub norm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(nan inf nzero sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf nzero sub norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_ieee_nzero(float nofpclass(nan inf pzero sub norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(nan inf pzero sub nnorm) float @ret_sqrt_ieee_nzero
+; CHECK-SAME: (float nofpclass(nan inf pzero sub norm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(nan inf pzero sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf pzero sub norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_ieee_zero(float nofpclass(nan inf sub norm) %arg) #0 {
+; CHECK-LABEL: define nofpclass(nan inf sub nnorm) float @ret_sqrt_ieee_zero
+; CHECK-SAME: (float nofpclass(nan inf sub norm) [[ARG:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(nan inf sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf sub norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_daz_psub(float nofpclass(nan inf zero nsub norm) %arg) #1 {
+; CHECK-LABEL: define nofpclass(nan inf nzero sub nnorm) float @ret_sqrt_daz_psub
+; CHECK-SAME: (float nofpclass(nan inf zero nsub norm) [[ARG:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(nan inf nzero sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero nsub norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_daz_nsub(float nofpclass(nan inf zero psub norm) %arg) #1 {
+; CHECK-LABEL: define nofpclass(nan inf pzero sub nnorm) float @ret_sqrt_daz_nsub
+; CHECK-SAME: (float nofpclass(nan inf zero psub norm) [[ARG:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(nan inf pzero sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero psub norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_daz_sub(float nofpclass(nan inf zero norm) %arg) #1 {
+; CHECK-LABEL: define nofpclass(nan inf sub nnorm) float @ret_sqrt_daz_sub
+; CHECK-SAME: (float nofpclass(nan inf zero norm) [[ARG:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(nan inf sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_dapz_sub(float nofpclass(nan inf zero norm) %arg) #2 {
+; CHECK-LABEL: define nofpclass(nan inf nzero sub nnorm) float @ret_sqrt_dapz_sub
+; CHECK-SAME: (float nofpclass(nan inf zero norm) [[ARG:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(nan inf nzero sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_dynamic_psub(float nofpclass(nan inf zero nsub norm) %arg) #3 {
+; CHECK-LABEL: define nofpclass(nan inf nzero sub nnorm) float @ret_sqrt_dynamic_psub
+; CHECK-SAME: (float nofpclass(nan inf zero nsub norm) [[ARG:%.*]]) #[[ATTR4:[0-9]+]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(nan inf nzero sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero nsub norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_dynamic_nsub(float nofpclass(nan inf zero psub norm) %arg) #3 {
+; CHECK-LABEL: define nofpclass(snan inf sub nnorm) float @ret_sqrt_dynamic_nsub
+; CHECK-SAME: (float nofpclass(nan inf zero psub norm) [[ARG:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(snan inf sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero psub norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+define float @ret_sqrt_dynamic_sub(float nofpclass(nan inf zero norm) %arg) #3 {
+; CHECK-LABEL: define nofpclass(snan inf sub nnorm) float @ret_sqrt_dynamic_sub
+; CHECK-SAME: (float nofpclass(nan inf zero norm) [[ARG:%.*]]) #[[ATTR4]] {
+; CHECK-NEXT:    [[SQRT:%.*]] = call nofpclass(snan inf sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero norm) [[ARG]]) #[[ATTR5]]
+; CHECK-NEXT:    ret float [[SQRT]]
+;
+  %sqrt = call float @llvm.sqrt.f32(float %arg)
+  ret float %sqrt
+}
+
+attributes #0 = { denormal_fpenv(ieee|ieee) }
+attributes #1 = { denormal_fpenv(ieee|preservesign) }
+attributes #2 = { denormal_fpenv(ieee|positivezero) }
+attributes #3 = { denormal_fpenv(ieee|dynamic) }
diff --git a/llvm/test/Transforms/Attributor/nofpclass-sqrt.ll b/llvm/test/Transforms/Attributor/nofpclass-sqrt.ll
index aa62e807cddb9..25519529bb1e7 100644
--- a/llvm/test/Transforms/Attributor/nofpclass-sqrt.ll
+++ b/llvm/test/Transforms/Attributor/nofpclass-sqrt.ll
@@ -66,9 +66,9 @@ define float @ret_sqrt_nonan_noinf(float nofpclass(nan inf) %arg0) #0 {
 }
 
 define float @ret_sqrt_nonan_noinf_nozero(float nofpclass(nan inf zero) %arg0) #0 {
-; CHECK-LABEL: define nofpclass(snan inf nzero sub nnorm) float @ret_sqrt_nonan_noinf_nozero
+; CHECK-LABEL: define nofpclass(snan inf zero sub nnorm) float @ret_sqrt_nonan_noinf_nozero
 ; CHECK-SAME: (float nofpclass(nan inf zero) [[ARG0:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf nzero sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero) [[ARG0]]) #[[ATTR10]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(snan inf zero sub nnorm) float @llvm.sqrt.f32(float nofpclass(nan inf zero) [[ARG0]]) #[[ATTR10]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.sqrt.f32(float %arg0)
@@ -76,9 +76,9 @@ define float @ret_sqrt_nonan_noinf_nozero(float nofpclass(nan inf zero) %arg0) #
 }
 
 define float @ret_sqrt_noinf_nozero(float nofpclass(inf zero) %arg0) #0 {
-; CHECK-LABEL: define nofpclass(inf nzero sub nnorm) float @ret_sqrt_noinf_nozero
+; CHECK-LABEL: define nofpclass(inf zero sub nnorm) float @ret_sqrt_noinf_nozero
 ; CHECK-SAME: (float nofpclass(inf zero) [[ARG0:%.*]]) #[[ATTR2]] {
-; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf nzero sub nnorm) float @llvm.sqrt.f32(float nofpclass(inf zero) [[ARG0]]) #[[ATTR10]]
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf zero sub nnorm) float @llvm.sqrt.f32(float nofpclass(inf zero) [[ARG0]]) #[[ATTR10]]
 ; CHECK-NEXT:    ret float [[CALL]]
 ;
   %call = call float @llvm.sqrt.f32(float %arg0)
@@ -95,6 +95,16 @@ define float @ret_sqrt_noinf_nonegzero(float nofpclass(inf nzero) %arg0) #0 {
   ret float %call
 }
 
+define float @ret_sqrt_noinf_noposzero(float nofpclass(inf pzero) %arg0) #0 {
+; CHECK-LABEL: define nofpclass(inf pzero sub nnorm) float @ret_sqrt_noinf_noposzero
+; CHECK-SAME: (float nofpclass(inf pzero) [[ARG0:%.*]]) #[[ATTR2]] {
+; CHECK-NEXT:    [[CALL:%.*]] = call nofpclass(inf pzero sub nnorm) float @llvm.sqrt.f32(float nofpclass(inf pzero) [[ARG0]]) #[[ATTR10]]
+; CHECK-NEXT:    ret float [[CALL]]
+;
+  %call = call float @llvm.sqrt.f32(float %arg0)
+  ret float %call
+}
+
 define float @ret_sqrt_positive_source(i32 %arg) #0 {
 ; CHECK-LABEL: define nofpclass(nan inf nzero sub nnorm) float @ret_sqrt_positive_source
 ; CHECK-SAME: (i32 [[ARG:%.*]]) #[[ATTR2]] {
@@ -261,9 +271,9 @@ define float @constrained_sqrt_nonegzero(float nofpclass(nzero) %arg) strictfp {
 }
 
 define float @constrained_sqrt_nozero(float nofpclass(zero) %arg) strictfp {
-; CHECK-LABEL: define nofpclass(ninf nzero sub nnorm) float @constrained_sqrt_nozero
+; CHECK-LABEL: define nofpclass(ninf zero sub nnorm) float @constrained_sqrt_nozero
 ; CHECK-SAME: (float nofpclass(zero) [[ARG:%.*]]) #[[ATTR9]] {
-; CHECK-NEXT:    [[VAL:%.*]] = call nofpclass(ninf nzero sub nnorm) float @llvm.experimental.constrained.sqrt.f32(float nofpclass(zero) [[ARG]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR11]]
+; CHECK-NEXT:    [[VAL:%.*]] = call nofpclass(ninf zero sub nnorm) float @llvm.experimental.constrained.sqrt.f32(float nofpclass(zero) [[ARG]], metadata !"round.dynamic", metadata !"fpexcept.strict") #[[ATTR11]]
 ; CHECK-NEXT:    ret float [[VAL]]
 ;
   %val = call float @llvm.experimental.constrained.sqrt.f32(float %arg, metadata !"round.dynamic", metadata !"fpexcept.strict")



More information about the llvm-commits mailing list