[llvm] LangRef: Clarify llvm.minnum and llvm.maxnum about sNaN (PR #112852)
YunQiang Su via llvm-commits
llvm-commits at lists.llvm.org
Tue Oct 22 18:25:46 PDT 2024
https://github.com/wzssyqa updated https://github.com/llvm/llvm-project/pull/112852
>From 508c33a9673ab129395e53cfa21b179c86ff3dae Mon Sep 17 00:00:00 2001
From: YunQiang Su <yunqiang at isrc.iscas.ac.cn>
Date: Fri, 18 Oct 2024 16:16:43 +0800
Subject: [PATCH 1/6] LangRef: Clarify llvm.minnum and llvm.maxnum about sNaN
The documents claims that it ignores sNaN, while in the code it may
be different.
- as the finally callback, it use libc call fmin(3)/fmax(3).
while C23 clarify that fmin(3)/fmax(3) should return NaN for sNaN vs NUM.
- on some architectures, such as aarch64, it converts to `fmaxnm`,
which returns qNaN for sNaN vs NUM.
- on RISC-V (SPEC 2019+), it converts to `fmax`, which returns NUM
for sNaN vs NUM.
Since we have introduced llvm.minimumnum and llvm.maximumnum, which
follow IEEE 754-2019's minimumNumber/maximumNumber.
So, it's time for use to clarify llvm.minnum and llvm.maxnum.
Let's define it to the libc's defination.
---
llvm/docs/LangRef.rst | 75 ++++++++++++++++++-------------------------
1 file changed, 31 insertions(+), 44 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index b83675c6ed97aa..2df01362a27ec6 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -16464,21 +16464,13 @@ type.
Semantics:
""""""""""
+Follows the IEEE754 2008 semantics for minNum, except for handling of
++0.0 vs -0.0. This matches the behavior of libm's fmin.
-Follows the IEEE-754 semantics for minNum, except for handling of
-signaling NaNs. This match's the behavior of libm's fmin.
-
-If either operand is a NaN, returns the other non-NaN operand. Returns
-NaN only if both operands are NaN. If the operands compare equal,
-returns either one of the operands. For example, this means that
-fmin(+0.0, -0.0) returns either operand.
-
-Unlike the IEEE-754 2008 behavior, this does not distinguish between
-signaling and quiet NaN inputs. If a target's implementation follows
-the standard and returns a quiet NaN if either input is a signaling
-NaN, the intrinsic lowering is responsible for quieting the inputs to
-correctly return the non-NaN input (e.g. by using the equivalent of
-``llvm.canonicalize``).
+If either operand is a qNaN, returns the other non-NaN operand. Returns
+NaN only if both operands are NaN or either operand is sNaN.
+If the operands compare equal, returns either one of the operands.
+For example, this means that fmin(+0.0, -0.0) returns either operand.
.. _i_maxnum:
@@ -16515,20 +16507,13 @@ type.
Semantics:
""""""""""
-Follows the IEEE-754 semantics for maxNum except for the handling of
-signaling NaNs. This matches the behavior of libm's fmax.
+Follows the IEEE754 2008 semantics for maxNum, except for handling of
++0.0 vs -0.0. This matches the behavior of libm's fmax.
If either operand is a NaN, returns the other non-NaN operand. Returns
-NaN only if both operands are NaN. If the operands compare equal,
-returns either one of the operands. For example, this means that
-fmax(+0.0, -0.0) returns either -0.0 or 0.0.
-
-Unlike the IEEE-754 2008 behavior, this does not distinguish between
-signaling and quiet NaN inputs. If a target's implementation follows
-the standard and returns a quiet NaN if either input is a signaling
-NaN, the intrinsic lowering is responsible for quieting the inputs to
-correctly return the non-NaN input (e.g. by using the equivalent of
-``llvm.canonicalize``).
+NaN only if both operands are NaN or either operand is sNaN.
+If the operands compare equal, returns either one of the operands.
+For example, this means that fmin(+0.0, -0.0) returns either operand.
.. _i_minimum:
@@ -19407,12 +19392,12 @@ The '``llvm.vector.reduce.fmax.*``' intrinsics do a floating-point
matches the element-type of the vector input.
This instruction has the same comparison semantics as the '``llvm.maxnum.*``'
-intrinsic. That is, the result will always be a number unless all elements of
-the vector are NaN. For a vector with maximum element magnitude 0.0 and
-containing both +0.0 and -0.0 elements, the sign of the result is unspecified.
+intrinsic. If the intrinsic call has the ``nnan`` fast-math flag, then the
+operation can assume that NaNs are not present in the input vector.
-If the intrinsic call has the ``nnan`` fast-math flag, then the operation can
-assume that NaNs are not present in the input vector.
+It is deprecated, since the different order of inputs may produce different
+outputs, and it is hard to optimize with Vector or SIMD extensions.
+Use '``llvm.vector.reduce.fmaximum``' or '``llvm.vector.reduce.fmaximumnum``' instead.
Arguments:
""""""""""
@@ -19440,12 +19425,12 @@ The '``llvm.vector.reduce.fmin.*``' intrinsics do a floating-point
matches the element-type of the vector input.
This instruction has the same comparison semantics as the '``llvm.minnum.*``'
-intrinsic. That is, the result will always be a number unless all elements of
-the vector are NaN. For a vector with minimum element magnitude 0.0 and
-containing both +0.0 and -0.0 elements, the sign of the result is unspecified.
+intrinsic. If the intrinsic call has the ``nnan`` fast-math flag, then the
+operation can assume that NaNs are not present in the input vector.
-If the intrinsic call has the ``nnan`` fast-math flag, then the operation can
-assume that NaNs are not present in the input vector.
+It is deprecated, since the different order of inputs may produce different
+outputs, and it is hard to optimize with Vector or SIMD extensions.
+Use '``llvm.vector.reduce.fminimum``' or '``llvm.vector.reduce.fminimumnum``' instead.
Arguments:
""""""""""
@@ -22994,13 +22979,14 @@ result type. If only ``nnan`` is set then the neutral value is ``-Infinity``.
This instruction has the same comparison semantics as the
:ref:`llvm.vector.reduce.fmax <int_vector_reduce_fmax>` intrinsic (and thus the
-'``llvm.maxnum.*``' intrinsic). That is, the result will always be a number
-unless all elements of the vector and the starting value are ``NaN``. For a
-vector with maximum element magnitude ``0.0`` and containing both ``+0.0`` and
-``-0.0`` elements, the sign of the result is unspecified.
+'``llvm.maxnum.*``' intrinsic).
To ignore the start value, the neutral value can be used.
+It is deprecated, since the different order of inputs may produce different
+outputs, and it is hard to optimize with Vector or SIMD extensions.
+Use '``llvm.vp.vector.reduce.fmaximum``' or '``llvm.vp.vector.reduce.fmaximumnum``' instead.
+
Examples:
"""""""""
@@ -23064,13 +23050,14 @@ result type. If only ``nnan`` is set then the neutral value is ``+Infinity``.
This instruction has the same comparison semantics as the
:ref:`llvm.vector.reduce.fmin <int_vector_reduce_fmin>` intrinsic (and thus the
-'``llvm.minnum.*``' intrinsic). That is, the result will always be a number
-unless all elements of the vector and the starting value are ``NaN``. For a
-vector with maximum element magnitude ``0.0`` and containing both ``+0.0`` and
-``-0.0`` elements, the sign of the result is unspecified.
+'``llvm.minnum.*``' intrinsic).
To ignore the start value, the neutral value can be used.
+It is deprecated, since the different order of inputs may produce different
+outputs, and it is hard to optimize with Vector or SIMD extensions.
+Use '``llvm.vp.vector.reduce.fminimum``' or '``llvm.vp.vector.reduce.fminimumnum``' instead.
+
Examples:
"""""""""
>From ea6a89bd0fe9d8c5071fef7667efd9b08d0abbde Mon Sep 17 00:00:00 2001
From: YunQiang Su <wzssyqa at gmail.com>
Date: Sat, 19 Oct 2024 01:33:13 +0800
Subject: [PATCH 2/6] minNum doesn't care about +0 vs -0
---
llvm/docs/LangRef.rst | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 2df01362a27ec6..fb52141d62038e 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -16464,8 +16464,8 @@ type.
Semantics:
""""""""""
-Follows the IEEE754 2008 semantics for minNum, except for handling of
-+0.0 vs -0.0. This matches the behavior of libm's fmin.
+Follows the IEEE754 2008 semantics for minNum.
+This also matches the behavior of libm's fmin.
If either operand is a qNaN, returns the other non-NaN operand. Returns
NaN only if both operands are NaN or either operand is sNaN.
@@ -16507,8 +16507,8 @@ type.
Semantics:
""""""""""
-Follows the IEEE754 2008 semantics for maxNum, except for handling of
-+0.0 vs -0.0. This matches the behavior of libm's fmax.
+Follows the IEEE754 2008 semantics for maxNum.
+This also matches the behavior of libm's fmax.
If either operand is a NaN, returns the other non-NaN operand. Returns
NaN only if both operands are NaN or either operand is sNaN.
>From b653061afd2481d1581302f6c3af0e3b3d0d5d34 Mon Sep 17 00:00:00 2001
From: YunQiang Su <syq at debian.org>
Date: Sun, 20 Oct 2024 12:13:21 +0800
Subject: [PATCH 3/6] add history about libm
---
llvm/docs/LangRef.rst | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index fb52141d62038e..bde5d4c23ce32c 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -16465,7 +16465,11 @@ type.
Semantics:
""""""""""
Follows the IEEE754 2008 semantics for minNum.
-This also matches the behavior of libm's fmin.
+This also matches the current (C23) behavior of libm's fmin.
+
+Historically, libc returns NUM for NUM vs (sNaN or qNaN), and may return
+sNaN for qNaN vs sNaN. Withe recent libc versions, libc follows IEEE754-2008:
+NUM vs sNaN -> qNaN; NUM vs qNaN -> NUM; qNaN vs sNaN -> qNaN; sNaN vs sNaN -> qNaN.
If either operand is a qNaN, returns the other non-NaN operand. Returns
NaN only if both operands are NaN or either operand is sNaN.
@@ -16508,7 +16512,11 @@ type.
Semantics:
""""""""""
Follows the IEEE754 2008 semantics for maxNum.
-This also matches the behavior of libm's fmax.
+This also matches the current (C23) behavior of libm's fmax.
+
+Historically, libc returns NUM for NUM vs (sNaN or qNaN), and may return
+sNaN for qNaN vs sNaN. Withe recent libc versions, libc follows IEEE754-2008:
+NUM vs sNaN -> qNaN; NUM vs qNaN -> NUM; qNaN vs sNaN -> qNaN; sNaN vs sNaN -> qNaN.
If either operand is a NaN, returns the other non-NaN operand. Returns
NaN only if both operands are NaN or either operand is sNaN.
>From 02b738fbbcbc9567b172e38b95929f55a4849376 Mon Sep 17 00:00:00 2001
From: YunQiang Su <yunqiang at isrc.iscas.ac.cn>
Date: Mon, 21 Oct 2024 08:56:22 +0800
Subject: [PATCH 4/6] fmin requires +0>-0
---
llvm/docs/LangRef.rst | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index bde5d4c23ce32c..01f71779872315 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -16420,7 +16420,7 @@ versions of the intrinsics respect the exception behavior.
- qNaN, invalid exception
* - ``+0.0 vs -0.0``
- - either one
+ - +0.0(max)/-0.0(min)
- +0.0(max)/-0.0(min)
- +0.0(max)/-0.0(min)
@@ -16464,8 +16464,14 @@ type.
Semantics:
""""""""""
-Follows the IEEE754 2008 semantics for minNum.
-This also matches the current (C23) behavior of libm's fmin.
+Follows the IEEE754 2008 semantics for minNum with +0.0>-0.0.
+This is more strict than current (C23) behavior of libm's fmin.
+Some applications like Clang, can call '``llvm.minnum.*``' with '``nsz``' attribute
+to archive the same behaivor of libm's fmin.
+
+For some architecturs, such as ARMv8, LoongArch, MIPSr6, PowerPC/VSX, they have the
+strict same instructions; thus it is quite simple for these architectures.
+For other architectures, the custom or expand methods may provide '``nsz``' flavor.
Historically, libc returns NUM for NUM vs (sNaN or qNaN), and may return
sNaN for qNaN vs sNaN. Withe recent libc versions, libc follows IEEE754-2008:
@@ -16511,12 +16517,14 @@ type.
Semantics:
""""""""""
-Follows the IEEE754 2008 semantics for maxNum.
-This also matches the current (C23) behavior of libm's fmax.
+Follows the IEEE754 2008 semantics for maxNum with +0.0>-0.0.
+This is more strict than current (C23) behavior of libm's fmax.
+Some applications like Clang, can call '``llvm.maxnum.*``' with '``nsz``' attribute
+to archive the same behaivor of libm's fmax.
-Historically, libc returns NUM for NUM vs (sNaN or qNaN), and may return
-sNaN for qNaN vs sNaN. Withe recent libc versions, libc follows IEEE754-2008:
-NUM vs sNaN -> qNaN; NUM vs qNaN -> NUM; qNaN vs sNaN -> qNaN; sNaN vs sNaN -> qNaN.
+For some architecturs, such as ARMv8, LoongArch, MIPSr6, PowerPC/VSX, they have the
+strict same instructions; thus it is quite simple for these architectures.
+For other architectures, the custom or expand methods may provide '``nsz``' flavor.
If either operand is a NaN, returns the other non-NaN operand. Returns
NaN only if both operands are NaN or either operand is sNaN.
>From 1d50663bcea3a903bbd32dc59156053dd41b607e Mon Sep 17 00:00:00 2001
From: YunQiang Su <yunqiang at isrc.iscas.ac.cn>
Date: Mon, 21 Oct 2024 09:11:35 +0800
Subject: [PATCH 5/6] some fix
---
llvm/docs/LangRef.rst | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 01f71779872315..11c536b0314365 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -16470,17 +16470,16 @@ Some applications like Clang, can call '``llvm.minnum.*``' with '``nsz``' attrib
to archive the same behaivor of libm's fmin.
For some architecturs, such as ARMv8, LoongArch, MIPSr6, PowerPC/VSX, they have the
-strict same instructions; thus it is quite simple for these architectures.
+strictly same instructions; thus it is quite simple for these architectures.
For other architectures, the custom or expand methods may provide '``nsz``' flavor.
Historically, libc returns NUM for NUM vs (sNaN or qNaN), and may return
-sNaN for qNaN vs sNaN. Withe recent libc versions, libc follows IEEE754-2008:
+sNaN for qNaN vs sNaN. With the recent libc versions, libc follows IEEE754-2008:
NUM vs sNaN -> qNaN; NUM vs qNaN -> NUM; qNaN vs sNaN -> qNaN; sNaN vs sNaN -> qNaN.
If either operand is a qNaN, returns the other non-NaN operand. Returns
NaN only if both operands are NaN or either operand is sNaN.
If the operands compare equal, returns either one of the operands.
-For example, this means that fmin(+0.0, -0.0) returns either operand.
.. _i_maxnum:
@@ -16523,13 +16522,16 @@ Some applications like Clang, can call '``llvm.maxnum.*``' with '``nsz``' attrib
to archive the same behaivor of libm's fmax.
For some architecturs, such as ARMv8, LoongArch, MIPSr6, PowerPC/VSX, they have the
-strict same instructions; thus it is quite simple for these architectures.
+strictly same instructions; thus it is quite simple for these architectures.
For other architectures, the custom or expand methods may provide '``nsz``' flavor.
+Historically, libc returns NUM for NUM vs (sNaN or qNaN), and may return
+sNaN for qNaN vs sNaN. With the recent libc versions, libc follows IEEE754-2008:
+NUM vs sNaN -> qNaN; NUM vs qNaN -> NUM; qNaN vs sNaN -> qNaN; sNaN vs sNaN -> qNaN.
+
If either operand is a NaN, returns the other non-NaN operand. Returns
NaN only if both operands are NaN or either operand is sNaN.
If the operands compare equal, returns either one of the operands.
-For example, this means that fmin(+0.0, -0.0) returns either operand.
.. _i_minimum:
>From d9c5bcf603e7721338e91b4cc12214beee9d94e6 Mon Sep 17 00:00:00 2001
From: YunQiang Su <yunqiang at isrc.iscas.ac.cn>
Date: Wed, 23 Oct 2024 09:25:07 +0800
Subject: [PATCH 6/6] add inconsistent note
---
llvm/docs/LangRef.rst | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 11c536b0314365..010d8b0ceb0f5d 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -16477,6 +16477,11 @@ Historically, libc returns NUM for NUM vs (sNaN or qNaN), and may return
sNaN for qNaN vs sNaN. With the recent libc versions, libc follows IEEE754-2008:
NUM vs sNaN -> qNaN; NUM vs qNaN -> NUM; qNaN vs sNaN -> qNaN; sNaN vs sNaN -> qNaN.
+Note that that arithmetic on an sNaN doesn't consistently produce a qNaN,
+so arithmetic feeding into a minnum can produce inconsistent results.
+Such as `fmin(sNaN+0.0, 1.0)` can produce qNaN or 1.0 depending on whether `+0.0`
+is optimized out.
+
If either operand is a qNaN, returns the other non-NaN operand. Returns
NaN only if both operands are NaN or either operand is sNaN.
If the operands compare equal, returns either one of the operands.
@@ -16529,6 +16534,11 @@ Historically, libc returns NUM for NUM vs (sNaN or qNaN), and may return
sNaN for qNaN vs sNaN. With the recent libc versions, libc follows IEEE754-2008:
NUM vs sNaN -> qNaN; NUM vs qNaN -> NUM; qNaN vs sNaN -> qNaN; sNaN vs sNaN -> qNaN.
+Note that that arithmetic on an sNaN doesn't consistently produce a qNaN,
+so arithmetic feeding into a maxnum can produce inconsistent results.
+Such as `fmax(sNaN+0.0, 1.0)` can produce qNaN or 1.0 depending on whether `+0.0`
+is optimized out.
+
If either operand is a NaN, returns the other non-NaN operand. Returns
NaN only if both operands are NaN or either operand is sNaN.
If the operands compare equal, returns either one of the operands.
More information about the llvm-commits
mailing list