Skip to content

Commit 75aa01b

Browse files
authored
Revert "LangRef: Clarify llvm.minnum and llvm.maxnum about sNaN and signed zero (#112852)" (#168838)
This reverts commit 363b059. This is a follow up of #166912. Sorry for not noticing the change at the beginning, but I disagree with both sNaN and signed zero semantics change. I have 3 justifications: - llvm.minnum and llvm.maxnum are common intrinsics, we cannot change the definition just because "some architectures" support the changed semantic. For example, X86 min/max instructions neither distinguish sNaN nor signed zero. We have to add couples of extra instructions to match with the new definition, which makes the intrinsics less efficient. But efficient is not the reason for the objection. I object because such cost is unnecessary; - As the example ``minnum(fadd(sNaN, -0.0), 1.0)`` shows, minnum/maxnum themself cannot guarantee consistent result if multiple FP arithmetic operations involved. It makes the sacrifice of performance totally unnecessary. `Behavior of Floating-Point NaN values` notes all NaNs can be treated as quiet NaNs unless using Constrained Floating-Point Intrinsics. So the cost is only worth for constrained minnum/maxnum ones if we want to define them; - Signed zero handling is unnecessary either, because even the C functions don't require it. If any other front ends require, they can use the existing fminnum_ieee/fmaxnum_ieee or define new intrinsics; Fixes: #138303 and #169122
1 parent ef37858 commit 75aa01b

File tree

2 files changed

+59
-71
lines changed

2 files changed

+59
-71
lines changed

llvm/docs/LangRef.rst

Lines changed: 54 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -17298,8 +17298,9 @@ LLVM Implementation:
1729817298
""""""""""""""""""""
1729917299

1730017300
LLVM implements all ISO C flavors as listed in this table, except in the
17301-
default floating-point environment exceptions are ignored. The constrained
17302-
versions of the intrinsics respect the exception behavior.
17301+
default floating-point environment exceptions are ignored and return value
17302+
is non-deterministic if one or both inputs are sNaN. The constrained
17303+
versions of the intrinsics respect the exception behavior and sNaN.
1730317304

1730417305
.. list-table::
1730517306
:header-rows: 1
@@ -17331,7 +17332,7 @@ versions of the intrinsics respect the exception behavior.
1733117332
- qNaN, invalid exception
1733217333

1733317334
* - ``+0.0 vs -0.0``
17334-
- +0.0(max)/-0.0(min)
17335+
- either one
1733517336
- +0.0(max)/-0.0(min)
1733617337
- +0.0(max)/-0.0(min)
1733717338

@@ -17375,30 +17376,22 @@ type.
1737517376

1737617377
Semantics:
1737717378
""""""""""
17378-
Follows the semantics of minNum in IEEE-754-2008, except that -0.0 < +0.0 for the purposes
17379-
of this intrinsic. As for signaling NaNs, per the minNum semantics, if either operand is sNaN,
17380-
the result is qNaN. This matches the recommended behavior for the libm
17381-
function ``fmin``, although not all implementations have implemented these recommended behaviors.
17382-
17383-
If either operand is a qNaN, returns the other non-NaN operand. Returns NaN only if both operands are
17384-
NaN or if either operand is sNaN. Note that arithmetic on an sNaN doesn't consistently produce a qNaN,
17385-
so arithmetic feeding into a minnum can produce inconsistent results. For example,
17386-
``minnum(fadd(sNaN, -0.0), 1.0)`` can produce qNaN or 1.0 depending on whether ``fadd`` is folded.
1738717379

17388-
IEEE-754-2008 defines minNum, and it was removed in IEEE-754-2019. As the replacement, IEEE-754-2019
17389-
defines :ref:`minimumNumber <i_minimumnum>`.
17380+
Follows the IEEE-754-2008 semantics for minNum, except for handling of
17381+
signaling NaNs. This matches the behavior of libm's fmin.
1739017382

17391-
If the intrinsic is marked with the nsz attribute, then the effect is as in the definition in C
17392-
and IEEE-754-2008: the result of ``minnum(-0.0, +0.0)`` may be either -0.0 or +0.0.
17383+
If either operand is a NaN, returns the other non-NaN operand. Returns
17384+
NaN only if both operands are NaN. If the operands compare equal,
17385+
returns either one of the operands. For example, this means that
17386+
fmin(+0.0, -0.0) non-deterministically returns either operand (-0.0
17387+
or 0.0).
1739317388

17394-
Some architectures, such as ARMv8 (FMINNM), LoongArch (fmin), MIPSr6 (min.fmt), PowerPC/VSX (xsmindp),
17395-
have instructions that match these semantics exactly; thus it is quite simple for these architectures.
17396-
Some architectures have similar ones while they are not exact equivalent. Such as x86 implements ``MINPS``,
17397-
which implements the semantics of C code ``a<b?a:b``: NUM vs qNaN always return qNaN. ``MINPS`` can be used
17398-
if ``nsz`` and ``nnan`` are given.
17399-
17400-
For existing libc implementations, the behaviors of fmin may be quite different on sNaN and signed zero behaviors,
17401-
even in the same release of a single libm implementation.
17389+
Unlike the IEEE-754-2008 behavior, this does not distinguish between
17390+
signaling and quiet NaN inputs. If a target's implementation follows
17391+
the standard and returns a quiet NaN if either input is a signaling
17392+
NaN, the intrinsic lowering is responsible for quieting the inputs to
17393+
correctly return the non-NaN input (e.g. by using the equivalent of
17394+
``llvm.canonicalize``).
1740217395

1740317396
.. _i_maxnum:
1740417397

@@ -17435,30 +17428,21 @@ type.
1743517428

1743617429
Semantics:
1743717430
""""""""""
17438-
Follows the semantics of maxNum in IEEE-754-2008, except that -0.0 < +0.0 for the purposes
17439-
of this intrinsic. As for signaling NaNs, per the maxNum semantics, if either operand is sNaN,
17440-
the result is qNaN. This matches the recommended behavior for the libm
17441-
function ``fmax``, although not all implementations have implemented these recommended behaviors.
17442-
17443-
If either operand is a qNaN, returns the other non-NaN operand. Returns NaN only if both operands are
17444-
NaN or if either operand is sNaN. Note that arithmetic on an sNaN doesn't consistently produce a qNaN,
17445-
so arithmetic feeding into a maxnum can produce inconsistent results. For example,
17446-
``maxnum(fadd(sNaN, -0.0), 1.0)`` can produce qNaN or 1.0 depending on whether ``fadd`` is folded.
17431+
Follows the IEEE-754-2008 semantics for maxNum except for the handling of
17432+
signaling NaNs. This matches the behavior of libm's fmax.
1744717433

17448-
IEEE-754-2008 defines maxNum, and it was removed in IEEE-754-2019. As the replacement, IEEE-754-2019
17449-
defines :ref:`maximumNumber <i_maximumnum>`.
17434+
If either operand is a NaN, returns the other non-NaN operand. Returns
17435+
NaN only if both operands are NaN. If the operands compare equal,
17436+
returns either one of the operands. For example, this means that
17437+
fmax(+0.0, -0.0) non-deterministically returns either operand (-0.0
17438+
or 0.0).
1745017439

17451-
If the intrinsic is marked with the nsz attribute, then the effect is as in the definition in C
17452-
and IEEE-754-2008: the result of maxnum(-0.0, +0.0) may be either -0.0 or +0.0.
17453-
17454-
Some architectures, such as ARMv8 (FMAXNM), LoongArch (fmax), MIPSr6 (max.fmt), PowerPC/VSX (xsmaxdp),
17455-
have instructions that match these semantics exactly; thus it is quite simple for these architectures.
17456-
Some architectures have similar ones while they are not exact equivalent. Such as x86 implements ``MAXPS``,
17457-
which implements the semantics of C code ``a>b?a:b``: NUM vs qNaN always return qNaN. ``MAXPS`` can be used
17458-
if ``nsz`` and ``nnan`` are given.
17459-
17460-
For existing libc implementations, the behaviors of fmin may be quite different on sNaN and signed zero behaviors,
17461-
even in the same release of a single libm implementation.
17440+
Unlike the IEEE-754-2008 behavior, this does not distinguish between
17441+
signaling and quiet NaN inputs. If a target's implementation follows
17442+
the standard and returns a quiet NaN if either input is a signaling
17443+
NaN, the intrinsic lowering is responsible for quieting the inputs to
17444+
correctly return the non-NaN input (e.g. by using the equivalent of
17445+
``llvm.canonicalize``).
1746217446

1746317447
.. _i_minimum:
1746417448

@@ -20342,8 +20326,12 @@ The '``llvm.vector.reduce.fmax.*``' intrinsics do a floating-point
2034220326
matches the element-type of the vector input.
2034320327

2034420328
This instruction has the same comparison semantics as the '``llvm.maxnum.*``'
20345-
intrinsic. If the intrinsic call has the ``nnan`` fast-math flag, then the
20346-
operation can assume that NaNs are not present in the input vector.
20329+
intrinsic. That is, the result will always be a number unless all elements of
20330+
the vector are NaN. For a vector with maximum element magnitude 0.0 and
20331+
containing both +0.0 and -0.0 elements, the sign of the result is unspecified.
20332+
20333+
If the intrinsic call has the ``nnan`` fast-math flag, then the operation can
20334+
assume that NaNs are not present in the input vector.
2034720335

2034820336
Arguments:
2034920337
""""""""""
@@ -20371,8 +20359,12 @@ The '``llvm.vector.reduce.fmin.*``' intrinsics do a floating-point
2037120359
matches the element-type of the vector input.
2037220360

2037320361
This instruction has the same comparison semantics as the '``llvm.minnum.*``'
20374-
intrinsic. If the intrinsic call has the ``nnan`` fast-math flag, then the
20375-
operation can assume that NaNs are not present in the input vector.
20362+
intrinsic. That is, the result will always be a number unless all elements of
20363+
the vector are NaN. For a vector with minimum element magnitude 0.0 and
20364+
containing both +0.0 and -0.0 elements, the sign of the result is unspecified.
20365+
20366+
If the intrinsic call has the ``nnan`` fast-math flag, then the operation can
20367+
assume that NaNs are not present in the input vector.
2037620368

2037720369
Arguments:
2037820370
""""""""""
@@ -22759,7 +22751,7 @@ This is an overloaded intrinsic.
2275922751
Overview:
2276022752
"""""""""
2276122753

22762-
Predicated floating-point IEEE-754-2008 minNum of two vectors of floating-point values.
22754+
Predicated floating-point IEEE-754 minNum of two vectors of floating-point values.
2276322755

2276422756

2276522757
Arguments:
@@ -22808,7 +22800,7 @@ This is an overloaded intrinsic.
2280822800
Overview:
2280922801
"""""""""
2281022802

22811-
Predicated floating-point IEEE-754-2008 maxNum of two vectors of floating-point values.
22803+
Predicated floating-point IEEE-754 maxNum of two vectors of floating-point values.
2281222804

2281322805

2281422806
Arguments:
@@ -24107,7 +24099,10 @@ result type. If only ``nnan`` is set then the neutral value is ``-Infinity``.
2410724099

2410824100
This instruction has the same comparison semantics as the
2410924101
:ref:`llvm.vector.reduce.fmax <int_vector_reduce_fmax>` intrinsic (and thus the
24110-
'``llvm.maxnum.*``' intrinsic).
24102+
'``llvm.maxnum.*``' intrinsic). That is, the result will always be a number
24103+
unless all elements of the vector and the starting value are ``NaN``. For a
24104+
vector with maximum element magnitude ``0.0`` and containing both ``+0.0`` and
24105+
``-0.0`` elements, the sign of the result is unspecified.
2411124106

2411224107
To ignore the start value, the neutral value can be used.
2411324108

@@ -24174,7 +24169,10 @@ result type. If only ``nnan`` is set then the neutral value is ``+Infinity``.
2417424169

2417524170
This instruction has the same comparison semantics as the
2417624171
:ref:`llvm.vector.reduce.fmin <int_vector_reduce_fmin>` intrinsic (and thus the
24177-
'``llvm.minnum.*``' intrinsic).
24172+
'``llvm.minnum.*``' intrinsic). That is, the result will always be a number
24173+
unless all elements of the vector and the starting value are ``NaN``. For a
24174+
vector with maximum element magnitude ``0.0`` and containing both ``+0.0`` and
24175+
``-0.0`` elements, the sign of the result is unspecified.
2417824176

2417924177
To ignore the start value, the neutral value can be used.
2418024178

@@ -29046,7 +29044,7 @@ The third argument specifies the exception behavior as described above.
2904629044
Semantics:
2904729045
""""""""""
2904829046

29049-
This function follows the IEEE-754-2008 semantics for maxNum.
29047+
This function follows the IEEE-754 semantics for maxNum.
2905029048

2905129049

2905229050
'``llvm.experimental.constrained.minnum``' Intrinsic
@@ -29078,7 +29076,7 @@ The third argument specifies the exception behavior as described above.
2907829076
Semantics:
2907929077
""""""""""
2908029078

29081-
This function follows the IEEE-754-2008 semantics for minNum.
29079+
This function follows the IEEE-754 semantics for minNum.
2908229080

2908329081

2908429082
'``llvm.experimental.constrained.maximum``' Intrinsic

llvm/include/llvm/CodeGen/ISDOpcodes.h

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,20 +1048,13 @@ enum NodeType {
10481048
LRINT,
10491049
LLRINT,
10501050

1051-
/// FMINNUM/FMAXNUM - Perform floating-point minimum maximum on two values,
1052-
/// following IEEE-754 definitions except for signed zero behavior.
1051+
/// FMINNUM/FMAXNUM - Perform floating-point minimum or maximum on two
1052+
/// values.
10531053
///
1054-
/// If one input is a signaling NaN, returns a quiet NaN. This matches
1055-
/// IEEE-754 2008's minNum/maxNum behavior for signaling NaNs (which differs
1056-
/// from 2019).
1054+
/// In the case where a single input is a NaN (either signaling or quiet),
1055+
/// the non-NaN input is returned.
10571056
///
1058-
/// These treat -0 as ordered less than +0, matching the behavior of IEEE-754
1059-
/// 2019's minimumNumber/maximumNumber.
1060-
///
1061-
/// Note that that arithmetic on an sNaN doesn't consistently produce a qNaN,
1062-
/// so arithmetic feeding into a minnum/maxnum can produce inconsistent
1063-
/// results. FMAXIMUN/FMINIMUM or FMAXIMUMNUM/FMINIMUMNUM may be better choice
1064-
/// for non-distinction of sNaN/qNaN handling.
1057+
/// The return value of (FMINNUM 0.0, -0.0) could be either 0.0 or -0.0.
10651058
FMINNUM,
10661059
FMAXNUM,
10671060

@@ -1075,9 +1068,6 @@ enum NodeType {
10751068
///
10761069
/// These treat -0 as ordered less than +0, matching the behavior of IEEE-754
10771070
/// 2019's minimumNumber/maximumNumber.
1078-
///
1079-
/// Deprecated, and will be removed soon, as FMINNUM/FMAXNUM have the same
1080-
/// semantics now.
10811071
FMINNUM_IEEE,
10821072
FMAXNUM_IEEE,
10831073

0 commit comments

Comments
 (0)