[llvm] LangRef: Clarify llvm.minnum and llvm.maxnum about sNaN (PR #112852)

YunQiang Su via llvm-commits llvm-commits at lists.llvm.org
Sun Oct 20 18:11:52 PDT 2024


https://github.com/wzssyqa updated https://github.com/llvm/llvm-project/pull/112852

>From dc09e4bdb3374f2efa43737a114a12d99e7e825f 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/5] 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 566d0d4e4e81a3..e0ea81ae8a2d10 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 fb6b8203c85429ea5c3469621dd32ed6f6850404 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/5] 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 e0ea81ae8a2d10..f3e2954aa0621d 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 f8b9bba9b51b50b511ea04c8fbc0d71b98c7df2b 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/5] 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 f3e2954aa0621d..6efb1ec64d05da 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 70b90f1d2d1f5db99ffe025f2242b19a28465a79 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/5] 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 6efb1ec64d05da..1d091038fad290 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 3cf9e55c626baeb963d5e98afb38f57909444ebd 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/5] 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 1d091038fad290..3211559cd97681 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:
 



More information about the llvm-commits mailing list