[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:42:50 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-support
Author: ZERICO2005
<details>
<summary>Changes</summary>
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 denormal mode. The `-0.0` case was handled in a prior commit.
- If `negative-subnormal` flushes to zero, then `sqrt(negative-subnormal)` is not `NaN`.
AI disclosure:
I used OpenAI Codex (GPT-5.6-sol) to help generate the test updates, which I reviewed and tested locally.
---
Full diff: https://github.com/llvm/llvm-project/pull/214987.diff
3 Files Affected:
- (modified) llvm/lib/Support/KnownFPClass.cpp (+12-5)
- (added) llvm/test/Transforms/Attributor/nofpclass-sqrt-denormal.ll (+142)
- (modified) llvm/test/Transforms/Attributor/nofpclass-sqrt.ll (+16-6)
``````````diff
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")
``````````
</details>
https://github.com/llvm/llvm-project/pull/214987
More information about the llvm-commits
mailing list