Skip to content

Conversation

@phoebewang
Copy link
Contributor

@phoebewang phoebewang commented Nov 20, 2025

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

…igned zero (llvm#112852)"

This reverts commit 363b059.

This is a follow up of llvm#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;
@llvmbot
Copy link
Member

llvmbot commented Nov 20, 2025

@llvm/pr-subscribers-llvm-selectiondag

@llvm/pr-subscribers-llvm-ir

Author: Phoebe Wang (phoebewang)

Changes

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;

Full diff: https://github.com/llvm/llvm-project/pull/168838.diff

2 Files Affected:

  • (modified) llvm/docs/LangRef.rst (+49-54)
  • (modified) llvm/include/llvm/CodeGen/ISDOpcodes.h (+5-15)
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 734778f73af5f..d88846a416a52 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -17290,7 +17290,7 @@ versions of the intrinsics respect the exception behavior.
      - qNaN, invalid exception
 
    * - ``+0.0 vs -0.0``
-     - +0.0(max)/-0.0(min)
+     - either one
      - +0.0(max)/-0.0(min)
      - +0.0(max)/-0.0(min)
 
@@ -17334,30 +17334,21 @@ type.
 
 Semantics:
 """"""""""
-Follows the semantics of minNum in IEEE-754-2008, except that -0.0 < +0.0 for the purposes
-of this intrinsic. As for signaling NaNs, per the minNum semantics, if either operand is sNaN,
-the result is qNaN. This matches the recommended behavior for the libm
-function ``fmin``, although not all implementations have implemented these recommended behaviors.
-
-If either operand is a qNaN, returns the other non-NaN operand. Returns NaN only if both operands are
-NaN or if either operand is sNaN. Note that arithmetic on an sNaN doesn't consistently produce a qNaN,
-so arithmetic feeding into a minnum can produce inconsistent results. For example,
-``minnum(fadd(sNaN, -0.0), 1.0)`` can produce qNaN or 1.0 depending on whether ``fadd`` is folded.
 
-IEEE-754-2008 defines minNum, and it was removed in IEEE-754-2019. As the replacement, IEEE-754-2019
-defines :ref:`minimumNumber <i_minimumnum>`.
+Follows the IEEE-754 semantics for minNum, except for handling of
+signaling NaNs. This match's the behavior of libm's fmin.
 
-If the intrinsic is marked with the nsz attribute, then the effect is as in the definition in C
-and IEEE-754-2008: the result of ``minnum(-0.0, +0.0)`` may be either -0.0 or +0.0.
+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.
 
-Some architectures, such as ARMv8 (FMINNM), LoongArch (fmin), MIPSr6 (min.fmt), PowerPC/VSX (xsmindp),
-have instructions that match these semantics exactly; thus it is quite simple for these architectures.
-Some architectures have similar ones while they are not exact equivalent. Such as x86 implements ``MINPS``,
-which implements the semantics of C code ``a<b?a:b``: NUM vs qNaN always return qNaN. ``MINPS`` can be used
-if ``nsz`` and ``nnan`` are given.
-
-For existing libc implementations, the behaviors of fmin may be quite different on sNaN and signed zero behaviors,
-even in the same release of a single libm implementation.
+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``).
 
 .. _i_maxnum:
 
@@ -17394,30 +17385,20 @@ type.
 
 Semantics:
 """"""""""
-Follows the semantics of maxNum in IEEE-754-2008, except that -0.0 < +0.0 for the purposes
-of this intrinsic. As for signaling NaNs, per the maxNum semantics, if either operand is sNaN,
-the result is qNaN. This matches the recommended behavior for the libm
-function ``fmax``, although not all implementations have implemented these recommended behaviors.
-
-If either operand is a qNaN, returns the other non-NaN operand. Returns NaN only if both operands are
-NaN or if either operand is sNaN. Note that arithmetic on an sNaN doesn't consistently produce a qNaN,
-so arithmetic feeding into a maxnum can produce inconsistent results. For example,
-``maxnum(fadd(sNaN, -0.0), 1.0)`` can produce qNaN or 1.0 depending on whether ``fadd`` is folded.
+Follows the IEEE-754 semantics for maxNum except for the handling of
+signaling NaNs. This matches the behavior of libm's fmax.
 
-IEEE-754-2008 defines maxNum, and it was removed in IEEE-754-2019. As the replacement, IEEE-754-2019
-defines :ref:`maximumNumber <i_maximumnum>`.
+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.
 
-If the intrinsic is marked with the nsz attribute, then the effect is as in the definition in C
-and IEEE-754-2008: the result of maxnum(-0.0, +0.0) may be either -0.0 or +0.0.
-
-Some architectures, such as ARMv8 (FMAXNM), LoongArch (fmax), MIPSr6 (max.fmt), PowerPC/VSX (xsmaxdp),
-have instructions that match these semantics exactly; thus it is quite simple for these architectures.
-Some architectures have similar ones while they are not exact equivalent. Such as x86 implements ``MAXPS``,
-which implements the semantics of C code ``a>b?a:b``: NUM vs qNaN always return qNaN. ``MAXPS`` can be used
-if ``nsz`` and ``nnan`` are given.
-
-For existing libc implementations, the behaviors of fmin may be quite different on sNaN and signed zero behaviors,
-even in the same release of a single libm implementation.
+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``).
 
 .. _i_minimum:
 
@@ -20301,8 +20282,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.  If the intrinsic call has the ``nnan`` fast-math flag, then the
-operation can assume that NaNs are not present in the input vector.
+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.
+
+If the intrinsic call has the ``nnan`` fast-math flag, then the operation can
+assume that NaNs are not present in the input vector.
 
 Arguments:
 """"""""""
@@ -20330,8 +20315,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. If the intrinsic call has the ``nnan`` fast-math flag, then the
-operation can assume that NaNs are not present in the input vector.
+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.
+
+If the intrinsic call has the ``nnan`` fast-math flag, then the operation can
+assume that NaNs are not present in the input vector.
 
 Arguments:
 """"""""""
@@ -22712,7 +22701,7 @@ This is an overloaded intrinsic.
 Overview:
 """""""""
 
-Predicated floating-point IEEE-754-2008 minNum of two vectors of floating-point values.
+Predicated floating-point IEEE-754 minNum of two vectors of floating-point values.
 
 
 Arguments:
@@ -22761,7 +22750,7 @@ This is an overloaded intrinsic.
 Overview:
 """""""""
 
-Predicated floating-point IEEE-754-2008 maxNum of two vectors of floating-point values.
+Predicated floating-point IEEE-754 maxNum of two vectors of floating-point values.
 
 
 Arguments:
@@ -24060,7 +24049,10 @@ 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).
+'``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.
 
 To ignore the start value, the neutral value can be used.
 
@@ -24127,7 +24119,10 @@ 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).
+'``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.
 
 To ignore the start value, the neutral value can be used.
 
@@ -28999,7 +28994,7 @@ The third argument specifies the exception behavior as described above.
 Semantics:
 """"""""""
 
-This function follows the IEEE-754-2008 semantics for maxNum.
+This function follows the IEEE-754 semantics for maxNum.
 
 
 '``llvm.experimental.constrained.minnum``' Intrinsic
@@ -29031,7 +29026,7 @@ The third argument specifies the exception behavior as described above.
 Semantics:
 """"""""""
 
-This function follows the IEEE-754-2008 semantics for minNum.
+This function follows the IEEE-754 semantics for minNum.
 
 
 '``llvm.experimental.constrained.maximum``' Intrinsic
diff --git a/llvm/include/llvm/CodeGen/ISDOpcodes.h b/llvm/include/llvm/CodeGen/ISDOpcodes.h
index cdaa916548c25..6b6bfd4a48fa2 100644
--- a/llvm/include/llvm/CodeGen/ISDOpcodes.h
+++ b/llvm/include/llvm/CodeGen/ISDOpcodes.h
@@ -1048,20 +1048,13 @@ enum NodeType {
   LRINT,
   LLRINT,
 
-  /// FMINNUM/FMAXNUM - Perform floating-point minimum maximum on two values,
-  /// following IEEE-754 definitions except for signed zero behavior.
+  /// FMINNUM/FMAXNUM - Perform floating-point minimum or maximum on two
+  /// values.
   ///
-  /// If one input is a signaling NaN, returns a quiet NaN. This matches
-  /// IEEE-754 2008's minNum/maxNum behavior for signaling NaNs (which differs
-  /// from 2019).
+  /// In the case where a single input is a NaN (either signaling or quiet),
+  /// the non-NaN input is returned.
   ///
-  /// These treat -0 as ordered less than +0, matching the behavior of IEEE-754
-  /// 2019's minimumNumber/maximumNumber.
-  ///
-  /// Note that that arithmetic on an sNaN doesn't consistently produce a qNaN,
-  /// so arithmetic feeding into a minnum/maxnum can produce inconsistent
-  /// results. FMAXIMUN/FMINIMUM or FMAXIMUMNUM/FMINIMUMNUM may be better choice
-  /// for non-distinction of sNaN/qNaN handling.
+  /// The return value of (FMINNUM 0.0, -0.0) could be either 0.0 or -0.0.
   FMINNUM,
   FMAXNUM,
 
@@ -1075,9 +1068,6 @@ enum NodeType {
   ///
   /// These treat -0 as ordered less than +0, matching the behavior of IEEE-754
   /// 2019's minimumNumber/maximumNumber.
-  ///
-  /// Deprecated, and will be removed soon, as FMINNUM/FMAXNUM have the same
-  /// semantics now.
   FMINNUM_IEEE,
   FMAXNUM_IEEE,
 

@phoebewang phoebewang requested a review from nikic November 20, 2025 08:11
@github-actions
Copy link

github-actions bot commented Nov 20, 2025

🐧 Linux x64 Test Results

  • 186664 tests passed
  • 4885 tests skipped

@RalfJung
Copy link
Contributor

#112852 said it was "clarifying" the semantics, so does reverting that PR make things less clear? Though looking at the actual text, it seems fairly clear, at least about the signed zero case -- either result is returned. It may be worth explicitly stating that the result is picked non-deterministically so e.g. constant folding doesn't have to try to match whatever the hardware would do, similar to the non-determinism around the exact NaN bit pattern produced e.g. by 0.0 / 0.0.

Comment on lines 17338 to 17340
Follows the IEEE-754 semantics for minNum, except for handling of
signaling NaNs. This match's the behavior of libm's fmin.

Copy link
Contributor

@RalfJung RalfJung Nov 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-introducing this sentence here seems confusing: it's not correct that this "follows the IEEE-754 semantics for minNum, except for handling of signaling NaNs". The handling of signed zeros also does not match IEEE-754 minNum. This is clarified later, but it's not great when the first sentence describing the semantics is contradicted by later sentences.

Copy link
Contributor Author

@phoebewang phoebewang Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would IEEE-754-2008 make it more clear?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, the old version of the standard allows both outcomes for signed zeros... TIL. Yeah that would help.

@phoebewang
Copy link
Contributor Author

#112852 said it was "clarifying" the semantics, so does reverting that PR make things less clear? Though looking at the actual text, it seems fairly clear, at least about the signed zero case -- either result is returned.

I think it's just "clarifying" for some targets and making the language semantics more confusing and conflict.

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 non-deterministic value (either -0.0 or 0.0).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fmin(+0.0, -0.0) returns non-deterministic value (either -0.0 or 0.0).
fmin(+0.0, -0.0) non-deterministically returns either operand (-0.0 or 0.0).

just a grammar fix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

IEEE-754-2008 defines minNum, and it was removed in IEEE-754-2019. As the replacement, IEEE-754-2019
defines :ref:`minimumNumber <i_minimumnum>`.
Follows the IEEE-754-2008 semantics for minNum, except for handling of
signaling NaNs. This match's the behavior of libm's fmin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
signaling NaNs. This match's the behavior of libm's fmin.
signaling NaNs. This matches the behavior of libm's fmin.

Copy link
Contributor

@RalfJung RalfJung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense to me -- at least from a Rust perspective, this is the spec we expect and desire. It seems like #112852 changed the contract for these intrinsics without actually updating all backends to match the new contract, and some backends really don't like the new contract, so we should revert to something more backend-friendly.

@RalfJung
Copy link
Contributor

@phoebewang IIUC this fixes both #138303 and #169122; that could be added to the PR description.

@phoebewang
Copy link
Contributor Author

Thanks @RalfJung ! I'll merge it in the next week if no other objections.

@RalfJung
Copy link
Contributor

FWIW I have no approval rights in LLVM, so my review doesn't really count for much I think. :)

@phoebewang
Copy link
Contributor Author

FWIW I have no approval rights in LLVM, so my review doesn't really count for much I think. :)

No worries. IIUC, everyone's review counts according to LLVM's policy. The vote not shown in green is the limitation of Github rather than limitation of llorg :)

@RalfJung
Copy link
Contributor

RalfJung commented Nov 28, 2025

I also found #138451 which proposes the same thing and has a bunch of discussion.
Cc @nikic @arsenm

@arsenm
Copy link
Contributor

arsenm commented Dec 1, 2025

llvm.minnum and llvm.maxnum are common intrinsics, we cannot change the definition just because "some architectures" support the changed semantic.

This is confused. The new definition better matches IEEE, which is what was actually built into hardware. We need the IEEE operation to avoid un-elidable canonicalizes deeper in the backend. You cannot hand wave away signaling nan behavior on these like other operations

As the example minnum(fadd(sNaN, -0.0), 1.0) shows, minnum/maxnum themself cannot guarantee consistent result

Correct, the operation is semantically garbage but this is the curse inflicted on us by IEEE-754

Basically we need these garbage operations, but should avoid using them up front from source languages

@llvm-ci
Copy link
Collaborator

llvm-ci commented Dec 1, 2025

LLVM Buildbot has detected a new failure on builder sanitizer-x86_64-linux-android running on sanitizer-buildbot-android while building llvm at step 2 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/186/builds/14375

Here is the relevant piece of the build log for the reference
Step 2 (annotate) failure: 'python ../sanitizer_buildbot/sanitizers/zorg/buildbot/builders/sanitizers/buildbot_selector.py' (failure)
...
[ RUN      ] AddressSanitizer.ReallocTest
[       OK ] AddressSanitizer.ReallocTest (41 ms)
[ RUN      ] AddressSanitizer.WrongFreeTest
[       OK ] AddressSanitizer.WrongFreeTest (137 ms)
[ RUN      ] AddressSanitizer.LongJmpTest
[       OK ] AddressSanitizer.LongJmpTest (0 ms)
[ RUN      ] AddressSanitizer.ThreadStackReuseTest
[       OK ] AddressSanitizer.ThreadStackReuseTest (8 ms)
[ DISABLED ] AddressSanitizer.DISABLED_MemIntrinsicUnalignedAccessTest
[ DISABLED ] AddressSanitizer.DISABLED_LargeFunctionSymbolizeTest
[ DISABLED ] AddressSanitizer.DISABLED_MallocFreeUnwindAndSymbolizeTest
[ RUN      ] AddressSanitizer.UseThenFreeThenUseTest
[       OK ] AddressSanitizer.UseThenFreeThenUseTest (114 ms)
[ RUN      ] AddressSanitizer.FileNameInGlobalReportTest
[       OK ] AddressSanitizer.FileNameInGlobalReportTest (115 ms)
[ DISABLED ] AddressSanitizer.DISABLED_StressStackReuseAndExceptionsTest
[ RUN      ] AddressSanitizer.MlockTest
[       OK ] AddressSanitizer.MlockTest (0 ms)
[ DISABLED ] AddressSanitizer.DISABLED_DemoThreadedTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoStackTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoThreadStackTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowIn
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowLeft
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowRight
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFHigh
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOM
[ DISABLED ] AddressSanitizer.DISABLED_DemoDoubleFreeTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoNullDerefTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoFunctionStaticTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoTooMuchMemoryTest
[ RUN      ] AddressSanitizer.LongDoubleNegativeTest
[       OK ] AddressSanitizer.LongDoubleNegativeTest (0 ms)
[----------] 19 tests from AddressSanitizer (25713 ms total)

[----------] Global test environment tear-down
[==========] 22 tests from 2 test suites ran. (25717 ms total)
[  PASSED  ] 22 tests.

  YOU HAVE 1 DISABLED TEST

skipping tests on aarch64

How to reproduce locally: https://github.com/google/sanitizers/wiki/SanitizerBotReproduceBuild


@@@STEP_FAILURE@@@

@@@STEP_FAILURE@@@

@@@STEP_FAILURE@@@
Step 9 (run cmake) failure: run cmake (failure)
...
-- Performing Test HAVE_CXX_FLAG_COVERAGE - Failed
-- Compiling and running to test HAVE_GNU_POSIX_REGEX
-- Performing Test HAVE_POSIX_REGEX -- compiled but failed to run
CMake Warning at /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm-project/third-party/benchmark/CMakeLists.txt:319 (message):
  Using std::regex with exceptions disabled is not fully supported
-- Compiling and running to test HAVE_STEADY_CLOCK
-- Performing Test HAVE_STEADY_CLOCK -- compiled but failed to run
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed
-- Check if compiler accepts -pthread
-- Performing Test HAVE_GNU_POSIX_REGEX -- failed to compile
-- Compiling and running to test HAVE_POSIX_REGEX
-- Performing Test HAVE_POSIX_REGEX -- compiled but failed to run
CMake Warning at /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm-project/third-party/benchmark/CMakeLists.txt:319 (message):
  Using std::regex with exceptions disabled is not fully supported
-- Compiling and running to test HAVE_STEADY_CLOCK
-- Check if compiler accepts -pthread - yes
-- Found Threads: TRUE  
-- Compiling and running to test HAVE_PTHREAD_AFFINITY
-- Performing Test HAVE_PTHREAD_AFFINITY -- failed to compile
-- Configuring done (23.5s)
-- Performing Test HAVE_STEADY_CLOCK -- compiled but failed to run
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed
-- Check if compiler accepts -pthread
-- Check if compiler accepts -pthread - yes
-- Found Threads: TRUE  
-- Compiling and running to test HAVE_PTHREAD_AFFINITY
-- Performing Test HAVE_POSIX_REGEX -- compiled but failed to run
CMake Warning at /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm-project/third-party/benchmark/CMakeLists.txt:319 (message):
  Using std::regex with exceptions disabled is not fully supported
-- Compiling and running to test HAVE_STEADY_CLOCK
-- Performing Test HAVE_PTHREAD_AFFINITY -- failed to compile
-- Configuring done (24.1s)
-- Performing Test HAVE_STEADY_CLOCK -- compiled but failed to run
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD
-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Failed
-- Check if compiler accepts -pthread
-- Check if compiler accepts -pthread - yes
-- Found Threads: TRUE  
-- Compiling and running to test HAVE_PTHREAD_AFFINITY

-- Performing Test HAVE_PTHREAD_AFFINITY -- failed to compile
-- Configuring done (24.9s)
-- Generating done (2.9s)
-- Build files have been written to: /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm_build_android_i686
-- Generating done (2.9s)
-- Build files have been written to: /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm_build_android_aarch64
-- Generating done (2.8s)
-- Build files have been written to: /var/lib/buildbot/sanitizer-buildbot6/sanitizer-x86_64-linux-android/build/llvm_build_android_arm
Step 10 (build android/aarch64) failure: build android/aarch64 (failure)
...
[655/696] Building CXX object lib/TargetParser/CMakeFiles/LLVMTargetParser.dir/TargetDataLayout.cpp.o
[656/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/ArchitectureSet.cpp.o
[657/696] Building CXX object lib/TargetParser/CMakeFiles/LLVMTargetParser.dir/X86TargetParser.cpp.o
[658/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Architecture.cpp.o
[659/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/PackedVersion.cpp.o
[660/696] Building CXX object lib/TargetParser/CMakeFiles/LLVMTargetParser.dir/Triple.cpp.o
[661/696] Building CXX object lib/TargetParser/CMakeFiles/LLVMTargetParser.dir/RISCVISAInfo.cpp.o
[662/696] Linking CXX static library lib/libLLVMTargetParser.a
[663/696] Linking CXX static library lib/libLLVMBinaryFormat.a
[664/696] Linking CXX static library lib/libLLVMCore.a
[665/696] Linking CXX static library lib/libLLVMBitReader.a
[666/696] Linking CXX static library lib/libLLVMDebugInfoDWARFLowLevel.a
[667/696] Linking CXX static library lib/libLLVMMC.a
[668/696] Linking CXX static library lib/libLLVMMCParser.a
[669/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Platform.cpp.o
[670/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/RecordVisitor.cpp.o
[671/696] Building Opts.inc...
[672/696] Building CXX object lib/AsmParser/CMakeFiles/LLVMAsmParser.dir/Parser.cpp.o
[673/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Symbol.cpp.o
[674/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextAPIError.cpp.o
[675/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Target.cpp.o
[676/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/SymbolSet.cpp.o
[677/696] Building CXX object lib/DebugInfo/Symbolize/CMakeFiles/LLVMSymbolize.dir/Symbolize.cpp.o
[678/696] Building CXX object tools/llvm-symbolizer/CMakeFiles/llvm-symbolizer.dir/llvm-symbolizer-driver.cpp.o
[679/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/InterfaceFile.cpp.o
[680/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/RecordsSlice.cpp.o
[681/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextStubCommon.cpp.o
[682/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/Utils.cpp.o
[683/696] Building CXX object tools/llvm-symbolizer/CMakeFiles/llvm-symbolizer.dir/llvm-symbolizer.cpp.o
[684/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextStubV5.cpp.o
[685/696] Building CXX object lib/TextAPI/CMakeFiles/LLVMTextAPI.dir/TextStub.cpp.o
[686/696] Linking CXX static library lib/libLLVMTextAPI.a
[687/696] Building CXX object lib/AsmParser/CMakeFiles/LLVMAsmParser.dir/LLParser.cpp.o
[688/696] Linking CXX static library lib/libLLVMAsmParser.a
[689/696] Linking CXX static library lib/libLLVMIRReader.a
[690/696] Linking CXX static library lib/libLLVMObject.a
[691/696] Linking CXX static library lib/libLLVMDebugInfoDWARF.a
[692/696] Linking CXX static library lib/libLLVMDebugInfoPDB.a
[693/696] Linking CXX static library lib/libLLVMDebugInfoGSYM.a
[694/696] Linking CXX static library lib/libLLVMSymbolize.a
[695/696] Linking CXX static library lib/libLLVMDebuginfod.a
[696/696] Linking CXX executable bin/llvm-symbolizer
ninja: Entering directory `compiler_rt_build_android_aarch64'
ninja: error: loading 'build.ninja': No such file or directory

How to reproduce locally: https://github.com/google/sanitizers/wiki/SanitizerBotReproduceBuild




Step 19 (run instrumented asan tests [arm/aosp_coral-userdebug/AOSP.MASTER]) failure: run instrumented asan tests [arm/aosp_coral-userdebug/AOSP.MASTER] (failure)
...
[ RUN      ] AddressSanitizer.SignalTest
[       OK ] AddressSanitizer.SignalTest (349 ms)
[ RUN      ] AddressSanitizer.ReallocTest
[       OK ] AddressSanitizer.ReallocTest (28 ms)
[ RUN      ] AddressSanitizer.WrongFreeTest
[       OK ] AddressSanitizer.WrongFreeTest (223 ms)
[ RUN      ] AddressSanitizer.LongJmpTest
[       OK ] AddressSanitizer.LongJmpTest (0 ms)
[ RUN      ] AddressSanitizer.ThreadStackReuseTest
[       OK ] AddressSanitizer.ThreadStackReuseTest (2 ms)
[ DISABLED ] AddressSanitizer.DISABLED_MemIntrinsicUnalignedAccessTest
[ DISABLED ] AddressSanitizer.DISABLED_LargeFunctionSymbolizeTest
[ DISABLED ] AddressSanitizer.DISABLED_MallocFreeUnwindAndSymbolizeTest
[ RUN      ] AddressSanitizer.UseThenFreeThenUseTest
[       OK ] AddressSanitizer.UseThenFreeThenUseTest (307 ms)
[ RUN      ] AddressSanitizer.FileNameInGlobalReportTest
[       OK ] AddressSanitizer.FileNameInGlobalReportTest (296 ms)
[ DISABLED ] AddressSanitizer.DISABLED_StressStackReuseAndExceptionsTest
[ RUN      ] AddressSanitizer.MlockTest
[       OK ] AddressSanitizer.MlockTest (0 ms)
[ DISABLED ] AddressSanitizer.DISABLED_DemoThreadedTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoStackTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoThreadStackTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowIn
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowLeft
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowRight
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFHigh
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOM
[ DISABLED ] AddressSanitizer.DISABLED_DemoDoubleFreeTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoNullDerefTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoFunctionStaticTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoTooMuchMemoryTest
[ RUN      ] AddressSanitizer.LongDoubleNegativeTest
[       OK ] AddressSanitizer.LongDoubleNegativeTest (0 ms)
[----------] 19 tests from AddressSanitizer (67225 ms total)

[----------] Global test environment tear-down
[==========] 22 tests from 2 test suites ran. (67229 ms total)
[  PASSED  ] 22 tests.

  YOU HAVE 1 DISABLED TEST

skipping tests on aarch64

How to reproduce locally: https://github.com/google/sanitizers/wiki/SanitizerBotReproduceBuild




Serial 17031FQCB00176
Step 24 (run instrumented asan tests [arm/bluejay-userdebug/TQ3A.230805.001]) failure: run instrumented asan tests [arm/bluejay-userdebug/TQ3A.230805.001] (failure)
...
[       OK ] AddressSanitizer.SignalTest (231 ms)
[ RUN      ] AddressSanitizer.ReallocTest
[       OK ] AddressSanitizer.ReallocTest (41 ms)
[ RUN      ] AddressSanitizer.WrongFreeTest
[       OK ] AddressSanitizer.WrongFreeTest (137 ms)
[ RUN      ] AddressSanitizer.LongJmpTest
[       OK ] AddressSanitizer.LongJmpTest (0 ms)
[ RUN      ] AddressSanitizer.ThreadStackReuseTest
[       OK ] AddressSanitizer.ThreadStackReuseTest (8 ms)
[ DISABLED ] AddressSanitizer.DISABLED_MemIntrinsicUnalignedAccessTest
[ DISABLED ] AddressSanitizer.DISABLED_LargeFunctionSymbolizeTest
[ DISABLED ] AddressSanitizer.DISABLED_MallocFreeUnwindAndSymbolizeTest
[ RUN      ] AddressSanitizer.UseThenFreeThenUseTest
[       OK ] AddressSanitizer.UseThenFreeThenUseTest (114 ms)
[ RUN      ] AddressSanitizer.FileNameInGlobalReportTest
[       OK ] AddressSanitizer.FileNameInGlobalReportTest (115 ms)
[ DISABLED ] AddressSanitizer.DISABLED_StressStackReuseAndExceptionsTest
[ RUN      ] AddressSanitizer.MlockTest
[       OK ] AddressSanitizer.MlockTest (0 ms)
[ DISABLED ] AddressSanitizer.DISABLED_DemoThreadedTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoStackTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoThreadStackTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowIn
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowLeft
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFLowRight
[ DISABLED ] AddressSanitizer.DISABLED_DemoUAFHigh
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOM
[ DISABLED ] AddressSanitizer.DISABLED_DemoDoubleFreeTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoNullDerefTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoFunctionStaticTest
[ DISABLED ] AddressSanitizer.DISABLED_DemoTooMuchMemoryTest
[ RUN      ] AddressSanitizer.LongDoubleNegativeTest
[       OK ] AddressSanitizer.LongDoubleNegativeTest (0 ms)
[----------] 19 tests from AddressSanitizer (25713 ms total)

[----------] Global test environment tear-down
[==========] 22 tests from 2 test suites ran. (25717 ms total)
[  PASSED  ] 22 tests.

  YOU HAVE 1 DISABLED TEST

skipping tests on aarch64

How to reproduce locally: https://github.com/google/sanitizers/wiki/SanitizerBotReproduceBuild




program finished with exit code 1
elapsedTime=2997.076428

arsenm added a commit that referenced this pull request Dec 1, 2025
…aN and signed zero (#112852)"" (#170067)

Reverts #168838

Justification is confused and this did not receive adequate discussion,
particularly during a holiday week
@llvm-ci
Copy link
Collaborator

llvm-ci commented Dec 1, 2025

LLVM Buildbot has detected a new failure on builder clang-ppc64le-linux-test-suite running on ppc64le-clang-test-suite while building llvm at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/95/builds/19277

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'SanitizerCommon-msan-powerpc64le-Linux :: Linux/getpwnam_r_invalid_user.cpp' FAILED ********************
Exit Code: 134

Command Output (stderr):
--
/home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/build/./bin/clang  --driver-mode=g++ -gline-tables-only -fsanitize=memory  -m64 -fno-function-sections -funwind-tables  -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/build/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp &&  /home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/build/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp # RUN: at line 2
+ /home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/build/./bin/clang --driver-mode=g++ -gline-tables-only -fsanitize=memory -m64 -fno-function-sections -funwind-tables -I/home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/llvm-project/compiler-rt/test -ldl -O0 -g /home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp -o /home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/build/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp
+ /home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/build/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp
Result: 110
getpwnam_r_invalid_user.cpp.tmp: /home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Linux/getpwnam_r_invalid_user.cpp:19: int main(): Assertion `res == 0 || res == ENOENT' failed.
/home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/build/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.script: line 1: 1946179 Aborted                 /home/buildbots/llvm-external-buildbots/workers/ppc64le-clang-test-suite/clang-ppc64le-test-suite/build/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/msan-powerpc64le-Linux/Linux/Output/getpwnam_r_invalid_user.cpp.tmp

--

********************


@phoebewang
Copy link
Contributor Author

This is confused. The new definition better matches IEEE, which is what was actually built into hardware. We need the IEEE operation to avoid un-elidable canonicalizes deeper in the backend. You cannot hand wave away signaling nan behavior on these like other operations

The point is since all targets share the same defination, a change on that needs consensus made through all targets. The hardware details (especially there are differences among different targets) are not the only consideration for the defination. And I explained them in details in the following 2 bullets.

@arsenm
Copy link
Contributor

arsenm commented Dec 1, 2025

The point is since all targets share the same defination, a change on that needs consensus made through all targets. The hardware details (especially there are differences among different targets) are not the only consideration for the defination. And I explained them in details in the following 2 bullets.

This has been discussed at length in the previous threads, and this has never worked correctly across all targets. The operation you want is minimumnum

@valadaptive
Copy link
Contributor

valadaptive commented Dec 1, 2025

This is confused. The new definition better matches IEEE, which is what was actually built into hardware.

x86, at least, does not implement signed-zero ordering the way the "new definition" describes it. The current intrinsics are a complete mess, and IMO specifying qNaN vs. sNaN handling makes no sense, unless we're talking about the "constrained" operations. I'll try and put together an RFC over on Discourse about this or something.

@RalfJung
Copy link
Contributor

RalfJung commented Dec 1, 2025

This got reverted in #170067, re-introducing all the problems with #112852.

@phoebewang
Copy link
Contributor Author

The point is since all targets share the same defination, a change on that needs consensus made through all targets. The hardware details (especially there are differences among different targets) are not the only consideration for the defination. And I explained them in details in the following 2 bullets.

This has been discussed at length in the previous threads, and this has never worked correctly across all targets. The operation you want is minimumnum

We need to clarify what is correct firstly. No matter how IEEE defines it, LLVM allows SNaN to be treated as QNAN in non constrained FP mode. Hence the current behavior is correct to me. To strictly match with IEEE, we can only leverage constrained intrinsics.

@RalfJung
Copy link
Contributor

RalfJung commented Dec 1, 2025

Indeed, there's many operations where IEEE says what happens with SNaN vs QNaN, and LLVM always entirely ignores that (except with strictfp or constrained operations). For instance, IEEE says that x * 1.0 should always return a QNaN, but LLVM doesn't care. Why should minnum/maxnum be any different @arsenm ? There's a full section explaining the NaN behavior, and that section also covers minnum/maxnum.

And ofc there's the signed zero issue where the IEEE 2019 semantics can't be compiled to x86 in a way that is nearly as fast the 2008 semantics. Any plan to switch to IEEE 2019 therefore needs to come with a good proposal for how to avoid perf regressions on x86, and should coordinate with the corresponding backend changes to avoid a world where the LangRef says one thing but LLVM actually does something different.

aahrun pushed a commit to aahrun/llvm-project that referenced this pull request Dec 1, 2025
…igned zero (llvm#112852)" (llvm#168838)

This reverts commit 363b059.

This is a follow up of llvm#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: llvm#138303 and
llvm#169122
aahrun pushed a commit to aahrun/llvm-project that referenced this pull request Dec 1, 2025
…aN and signed zero (llvm#112852)"" (llvm#170067)

Reverts llvm#168838

Justification is confused and this did not receive adequate discussion,
particularly during a holiday week
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Dec 1, 2025
…um about sNaN and signed zero (#112852)"" (#170067)

Reverts llvm/llvm-project#168838

Justification is confused and this did not receive adequate discussion,
particularly during a holiday week
LewisCrawford added a commit to LewisCrawford/llvm-project that referenced this pull request Dec 1, 2025
The behaviour of constant-folding maxnum(sNaN, x) and
minnum(sNaN, x) has become controvertial, and there
are ongoing discussions about which behaviour we want
to specify in the LLVM IR LangRef.

See:
  - llvm#170082
  - llvm#168838
  - llvm#138451
  - llvm#170067
  - https://discourse.llvm.org/t/rfc-a-consistent-set-of-semantics-for-the-floating-point-minimum-and-maximum-operations/89006

This patch removes optimizations and constant-folding
support for maxnum(sNaN, x) but keeps it folded/optimized
for qNaNs. This should allow for some more flexibility
so the implementation can conform to either the old
or new version of the semantics specified without any
changes.

As far as I am aware, optimizations involving constant sNaN
should generally be edge-cases that rarely occur, so here
should hopefully be very little real-world performance impact
from disabling these optimizations.
LewisCrawford added a commit that referenced this pull request Dec 2, 2025
The behaviour of constant-folding `maxnum(sNaN, x)` and `minnum(sNaN,
x)` has become controversial, and there are ongoing discussions about
which behaviour we want to specify in the LLVM IR LangRef.

See:
  - #170082
  - #168838
  - #138451
  - #170067
-
https://discourse.llvm.org/t/rfc-a-consistent-set-of-semantics-for-the-floating-point-minimum-and-maximum-operations/89006

This patch removes optimizations and constant-folding support for
`maxnum(sNaN, x)` but keeps it folded/optimized for `qNaN`. This should
allow for some more flexibility so the implementation can conform to
either the old or new version of the semantics specified without any
changes.

As far as I am aware, optimizations involving constant `sNaN` should
generally be edge-cases that rarely occur, so here should hopefully be
very little real-world performance impact from disabling these
optimizations.
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Dec 2, 2025
The behaviour of constant-folding `maxnum(sNaN, x)` and `minnum(sNaN,
x)` has become controversial, and there are ongoing discussions about
which behaviour we want to specify in the LLVM IR LangRef.

See:
  - llvm/llvm-project#170082
  - llvm/llvm-project#168838
  - llvm/llvm-project#138451
  - llvm/llvm-project#170067
-
https://discourse.llvm.org/t/rfc-a-consistent-set-of-semantics-for-the-floating-point-minimum-and-maximum-operations/89006

This patch removes optimizations and constant-folding support for
`maxnum(sNaN, x)` but keeps it folded/optimized for `qNaN`. This should
allow for some more flexibility so the implementation can conform to
either the old or new version of the semantics specified without any
changes.

As far as I am aware, optimizations involving constant `sNaN` should
generally be edge-cases that rarely occur, so here should hopefully be
very little real-world performance impact from disabling these
optimizations.
augusto2112 pushed a commit to augusto2112/llvm-project that referenced this pull request Dec 3, 2025
…igned zero (llvm#112852)" (llvm#168838)

This reverts commit 363b059.

This is a follow up of llvm#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: llvm#138303 and
llvm#169122
augusto2112 pushed a commit to augusto2112/llvm-project that referenced this pull request Dec 3, 2025
…aN and signed zero (llvm#112852)"" (llvm#170067)

Reverts llvm#168838

Justification is confused and this did not receive adequate discussion,
particularly during a holiday week
kcloudy0717 pushed a commit to kcloudy0717/llvm-project that referenced this pull request Dec 4, 2025
…igned zero (llvm#112852)" (llvm#168838)

This reverts commit 363b059.

This is a follow up of llvm#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: llvm#138303 and
llvm#169122
kcloudy0717 pushed a commit to kcloudy0717/llvm-project that referenced this pull request Dec 4, 2025
…aN and signed zero (llvm#112852)"" (llvm#170067)

Reverts llvm#168838

Justification is confused and this did not receive adequate discussion,
particularly during a holiday week
kcloudy0717 pushed a commit to kcloudy0717/llvm-project that referenced this pull request Dec 4, 2025
The behaviour of constant-folding `maxnum(sNaN, x)` and `minnum(sNaN,
x)` has become controversial, and there are ongoing discussions about
which behaviour we want to specify in the LLVM IR LangRef.

See:
  - llvm#170082
  - llvm#168838
  - llvm#138451
  - llvm#170067
-
https://discourse.llvm.org/t/rfc-a-consistent-set-of-semantics-for-the-floating-point-minimum-and-maximum-operations/89006

This patch removes optimizations and constant-folding support for
`maxnum(sNaN, x)` but keeps it folded/optimized for `qNaN`. This should
allow for some more flexibility so the implementation can conform to
either the old or new version of the semantics specified without any
changes.

As far as I am aware, optimizations involving constant `sNaN` should
generally be edge-cases that rarely occur, so here should hopefully be
very little real-world performance impact from disabling these
optimizations.
honeygoyal pushed a commit to honeygoyal/llvm-project that referenced this pull request Dec 9, 2025
…igned zero (llvm#112852)" (llvm#168838)

This reverts commit 363b059.

This is a follow up of llvm#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: llvm#138303 and
llvm#169122
honeygoyal pushed a commit to honeygoyal/llvm-project that referenced this pull request Dec 9, 2025
…aN and signed zero (llvm#112852)"" (llvm#170067)

Reverts llvm#168838

Justification is confused and this did not receive adequate discussion,
particularly during a holiday week
honeygoyal pushed a commit to honeygoyal/llvm-project that referenced this pull request Dec 9, 2025
The behaviour of constant-folding `maxnum(sNaN, x)` and `minnum(sNaN,
x)` has become controversial, and there are ongoing discussions about
which behaviour we want to specify in the LLVM IR LangRef.

See:
  - llvm#170082
  - llvm#168838
  - llvm#138451
  - llvm#170067
-
https://discourse.llvm.org/t/rfc-a-consistent-set-of-semantics-for-the-floating-point-minimum-and-maximum-operations/89006

This patch removes optimizations and constant-folding support for
`maxnum(sNaN, x)` but keeps it folded/optimized for `qNaN`. This should
allow for some more flexibility so the implementation can conform to
either the old or new version of the semantics specified without any
changes.

As far as I am aware, optimizations involving constant `sNaN` should
generally be edge-cases that rarely occur, so here should hopefully be
very little real-world performance impact from disabling these
optimizations.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:ir llvm:SelectionDAG SelectionDAGISel as well

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[InstCombine] Formation of fmax ignores SNaN

6 participants