[PATCH 2/2] libstdc++: Use new atomic fetch min/max builtins in std::atomic

Jonathan Wakely jwakely@redhat.com
Mon Jan 5 14:19:28 GMT 2026


On Sat, 15 Nov 2025 at 18:11 +0530, soumyaa@nvidia.com wrote:
>From: Soumya AR <soumyaa@nvidia.com>
>
>This patch enables libstdc++ to implement C++26's atomic min/max operations
>through the newly added compiler builtins.
>
>Similar to the __atomic_fetch_addable and __atomic_fetch_subtractable concepts,
>we implement the following concepts for min/max operations:
>
>__atomic_fetch_minable
>__atomic_fetch_maxable
>__atomic_min_fetchable
>__atomic_max_fetchable
>
>Is this naming scheme ok?

I don't love the "minable" and "maxable" cases, because "min" and
"max" are not verbs, but I don't have better suggestions right now.

The existing concepts are intentionally named using verbs, so it's
"__atomic_fetch_subtractable" not "__atomic_fetch_subable" which is
what we'd get if we just naively appended "able" to "fetch_sub".

But as I said, I don't have better suggestions so the names are OK.

Is it likely that any compiler will implement a fetch_min builtin
without a fetch_max builtin? Do we need four concepts, or would two be
enough:

     template<typename _Tp>
       concept __atomic_fetch_minmaxable
	= requires (_Tp __t) {
           __atomic_fetch_min(&__t, __t, 0);
           __atomic_fetch_max(&__t, __t, 0);
         };


Tangentially, are we also going to get new built-ins and concepts for
fetch_fmaximum, fetch_fmaximum_num and the min forms of those, as
added by P3008R6? That will be an explosion of built-ins and
single-use concepts.



>---
>
>These concepts are used to check if the corresponding atomic builtins exist in
>the compiler. If they do, we use the builtin implementation. If they don't, we
>fall back to a CAS loop.
>
>In order to implement these functions for __atomic_base, we need access to the
>__atomic_impl implementation (that makes use of the concepts to do a check for
>the builtins). Currently, this is resolved by doing a forward declaration of the
>min/max functions from __atomic_impl.
>
>---
>
>These functions are currently not guarded by a check for C++26. Would
>appreciate comments on how we want that to be done in libstdc++.
>
>Signed-off-by: Soumya AR <soumyaa@nvidia.com>
>
>libstdc++-v3/ChangeLog:
>
>	* include/bits/atomic_base.h: Add fetch_max and fetch_min
>	member functions. These make a call to the corresponding functions in
>	__atomic_impl.
>	* include/c_compatibility/stdatomic.h: Add using-declaration.
>	* include/std/atomic: Extend for atomic_fetch_max, atomic_fetch_min,
>	atomic_fetch_min_explicit, atomic_fetch_max_explicit
>	* testsuite/29_atomics/atomic_integral/nonmembers.cc: Add tests
>	for non-member min/max functions.
>	* testsuite/29_atomics/atomic_ref/integral.cc: Extend for
>	min/max operations.
>	* testsuite/29_atomics/headers/stdatomic.h/c_compat.cc: Verify
>	C compatibility macros.
>	* testsuite/29_atomics/atomic_integral/fetch_minmax.cc: New test.
>	* testsuite/29_atomics/atomic_integral/fetch_minmax_order.cc: New test.
>---
> libstdc++-v3/include/bits/atomic_base.h       | 165 +++++++++++++++++
> .../include/c_compatibility/stdatomic.h       |   4 +
> libstdc++-v3/include/std/atomic               |  52 ++++++
> .../atomic_integral/fetch_minmax.cc           | 168 ++++++++++++++++++
> .../atomic_integral/fetch_minmax_order.cc     | 116 ++++++++++++
> .../29_atomics/atomic_integral/nonmembers.cc  |  24 +++
> .../29_atomics/atomic_ref/integral.cc         |  51 +++++-
> .../headers/stdatomic.h/c_compat.cc           |   4 +
> 8 files changed, 582 insertions(+), 2 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_integral/fetch_minmax.cc
> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_integral/fetch_minmax_order.cc
>
>diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
>index ccea132bb67..fc4148a0232 100644
>--- a/libstdc++-v3/include/bits/atomic_base.h
>+++ b/libstdc++-v3/include/bits/atomic_base.h
>@@ -334,6 +334,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   // NB: Assuming _ITp is an integral scalar type that is 1, 2, 4, or
>   // 8 bytes, since that is what GCC built-in functions for atomic
>   // memory access expect.
>+
>+  #if __cplusplus > 201703L

Preprocessor directives should not be indented, please remove
whitespace before the # symbol.

These new features should be guarded by the feature test macro for
atomic min/max, not by an explicit check for C++26 (and definitely not
for a check for C++20!)

You need to define the __cpp_lib_atomic_min_max macro in
bits/version.def and regenerate bits/version.h

>+  namespace __atomic_impl
>+  {
>+    template<typename _Tp>
>+      using _Val = typename remove_volatile<_Tp>::type;

This duplicates the __atomic_impl::_Val alias. Please move the
existing definition here, instead of duplicating it.

So something like:

namespace __atomic_impl
{
   template<typename _Tp>
     using _Val = typename remove_volatile<_Tp>::type;

#ifdef __glibcxx_atomic_min_max
   // declarations of __fetch_min and __fetch_max
#endif
}

Then remove the later definition of _Val.

>+
>+    template<typename _Tp>
>+      _Tp
>+      __fetch_min(_Tp* __ptr, _Val<_Tp> __i, memory_order __m) noexcept;
>+
>+    template<typename _Tp>
>+      _Tp
>+      __fetch_max(_Tp* __ptr, _Val<_Tp> __i, memory_order __m) noexcept;
>+  }
>+  #endif
>+
>   template<typename _ITp>
>     struct __atomic_base
>     {
>@@ -674,6 +691,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       fetch_xor(__int_type __i,
> 		memory_order __m = memory_order_seq_cst) volatile noexcept
>       { return __atomic_fetch_xor(&_M_i, __i, int(__m)); }
>+
>+      #if __cplusplus > 201703L

Un-indent the preprocessor directive, and check for the feature test
macro.

>+      _GLIBCXX_ALWAYS_INLINE __int_type
>+      fetch_min(__int_type __i,
>+		memory_order __m = memory_order_seq_cst) noexcept
>+      { return __atomic_impl::__fetch_min(&_M_i, __i, __m); }
>+
>+      _GLIBCXX_ALWAYS_INLINE __int_type
>+      fetch_min(__int_type __i,
>+		memory_order __m = memory_order_seq_cst) volatile noexcept
>+      { return __atomic_impl::__fetch_min(&_M_i, __i, __m); }
>+
>+      _GLIBCXX_ALWAYS_INLINE __int_type
>+      fetch_max(__int_type __i,
>+		memory_order __m = memory_order_seq_cst) noexcept
>+      { return __atomic_impl::__fetch_max(&_M_i, __i, __m); }
>+
>+      _GLIBCXX_ALWAYS_INLINE __int_type
>+      fetch_max(__int_type __i,
>+		memory_order __m = memory_order_seq_cst) volatile noexcept
>+      { return __atomic_impl::__fetch_max(&_M_i, __i, __m); }
>+      #endif

Unindent.

>     };
>
>
>@@ -1293,6 +1332,92 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	    return __newval;
> 	  }
>       }
>+

The concepts and functions below are not needed in C++20 so should be
guarded by the feature test macro.

>+    template<typename _Tp>
>+      concept __atomic_fetch_minable
>+	= requires (_Tp __t) { __atomic_fetch_min(&__t, __t, 0); };
>+
>+    template<typename _Tp>
>+      _Tp
>+      __fetch_min(_Tp* __ptr, _Val<_Tp> __i, memory_order __m) noexcept
>+      {
>+	if constexpr (__atomic_fetch_minable<_Tp>)
>+	  return __atomic_fetch_min(__ptr, __i, int(__m));
>+	else
>+	  {
>+	    _Val<_Tp> __oldval = load (__ptr, memory_order_relaxed);
>+	    _Val<_Tp> __newval = __oldval < __i ? __oldval : __i;
>+	    while (!compare_exchange_weak (__ptr, __oldval, __newval, __m,
>+					   memory_order_relaxed))
>+	      __newval = __oldval < __i ? __oldval : __i;
>+	    return __oldval;
>+	  }
>+      }
>+
>+    template<typename _Tp>
>+      concept __atomic_fetch_maxable
>+	= requires (_Tp __t) { __atomic_fetch_max(&__t, __t, 0); };
>+
>+    template<typename _Tp>
>+      _Tp
>+      __fetch_max(_Tp* __ptr, _Val<_Tp> __i, memory_order __m) noexcept
>+      {
>+	if constexpr (__atomic_fetch_maxable<_Tp>)
>+	  return __atomic_fetch_max(__ptr, __i, int(__m));
>+	else
>+	  {
>+	    _Val<_Tp> __oldval = load (__ptr, memory_order_relaxed);
>+	    _Val<_Tp> __newval = __oldval > __i ? __oldval : __i;
>+	    while (!compare_exchange_weak (__ptr, __oldval, __newval, __m,
>+					   memory_order_relaxed))
>+	      __newval = __oldval > __i ? __oldval : __i;
>+	    return __oldval;
>+	  }
>+      }
>+
>+    template<typename _Tp>
>+      concept __atomic_min_fetchable
>+	= requires (_Tp __t) { __atomic_min_fetch(&__t, __t, 0); };
>+
>+    template<typename _Tp>
>+      _Tp
>+      __min_fetch(_Tp* __ptr, _Val<_Tp> __i) noexcept

Hang on ... what are the __min_fetch and __max_fetch functions for?
There are no std::atomic::min_fetch member functions, so why are you
adding __atomic_impl::__min_fetch? It doesn't seem to be used
anywhere. Similarly for __max_fetch.

I don't think we need these functions in libstdc++, and does that mean
we don't need the new built-ins either? If the aim is to provide
built-ins that match what's in std::atomic, then we only need the
fetch_min and fetch_max forms, right?

>+      {
>+	if constexpr (__atomic_min_fetchable<_Tp>)
>+	  return __atomic_min_fetch(__ptr, __i, __ATOMIC_SEQ_CST);
>+	else
>+	  {
>+	    _Val<_Tp> __oldval = load (__ptr, memory_order_relaxed);
>+	    _Val<_Tp> __newval = __oldval < __i ? __oldval : __i;
>+	    while (!compare_exchange_weak (__ptr, __oldval, __newval,
>+					   memory_order_seq_cst,
>+					   memory_order_relaxed))
>+	      __newval = __oldval < __i ? __oldval : __i;
>+	    return __newval;
>+	  }
>+      }
>+
>+    template<typename _Tp>
>+      concept __atomic_max_fetchable
>+	= requires (_Tp __t) { __atomic_max_fetch(&__t, __t, 0); };
>+
>+    template<typename _Tp>
>+      _Tp
>+      __max_fetch(_Tp* __ptr, _Val<_Tp> __i) noexcept
>+      {
>+	if constexpr (__atomic_max_fetchable<_Tp>)
>+	  return __atomic_max_fetch(__ptr, __i, __ATOMIC_SEQ_CST);
>+	else
>+	  {
>+	    _Val<_Tp> __oldval = load (__ptr, memory_order_relaxed);
>+	    _Val<_Tp> __newval = __oldval > __i ? __oldval : __i;
>+	    while (!compare_exchange_weak (__ptr, __oldval, __newval,
>+					   memory_order_seq_cst,
>+					   memory_order_relaxed))
>+	      __newval = __oldval > __i ? __oldval : __i;
>+	    return __newval;
>+	  }
>+      }
>   } // namespace __atomic_impl
>
>   // base class for atomic<floating-point-type>
>@@ -1487,6 +1612,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 		memory_order __m = memory_order_seq_cst) volatile noexcept
>       { return __atomic_impl::__fetch_sub_flt(&_M_fp, __i, __m); }
>

These should be guarded by the feature test macro.

>+      value_type
>+      fetch_min(value_type __i,
>+		memory_order __m = memory_order_seq_cst) noexcept
>+      { return __atomic_impl::__fetch_min(&_M_fp, __i, __m); }
>+
>+      value_type
>+      fetch_min(value_type __i,
>+		memory_order __m = memory_order_seq_cst) volatile noexcept
>+      { return __atomic_impl::__fetch_min(&_M_fp, __i, __m); }
>+
>+      value_type
>+      fetch_max(value_type __i,
>+		memory_order __m = memory_order_seq_cst) noexcept
>+      { return __atomic_impl::__fetch_max(&_M_fp, __i, __m); }
>+
>+      value_type
>+      fetch_max(value_type __i,
>+		memory_order __m = memory_order_seq_cst) volatile noexcept
>+      { return __atomic_impl::__fetch_max(&_M_fp, __i, __m); }
>+
>       value_type
>       operator+=(value_type __i) noexcept
>       { return __atomic_impl::__add_fetch_flt(&_M_fp, __i); }
>@@ -1744,6 +1889,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 		memory_order __m = memory_order_seq_cst) const noexcept
>       { return __atomic_impl::fetch_xor(this->_M_ptr, __i, __m); }

Ditto

>+      value_type
>+      fetch_min(value_type __i,
>+		memory_order __m = memory_order_seq_cst) const noexcept
>+      { return __atomic_impl::__fetch_min(this->_M_ptr, __i, __m); }
>+
>+      value_type
>+      fetch_max(value_type __i,
>+		memory_order __m = memory_order_seq_cst) const noexcept
>+      { return __atomic_impl::__fetch_max(this->_M_ptr, __i, __m); }
>+
>       _GLIBCXX_ALWAYS_INLINE value_type
>       operator++(int) const noexcept
>       { return fetch_add(1); }
>@@ -1810,6 +1965,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 		memory_order __m = memory_order_seq_cst) const noexcept
>       { return __atomic_impl::__fetch_sub_flt(this->_M_ptr, __i, __m); }

Ditto

>+      value_type
>+      fetch_min(value_type __i,
>+		memory_order __m = memory_order_seq_cst) const noexcept
>+      { return __atomic_impl::__fetch_min(this->_M_ptr, __i, __m); }
>+
>+      value_type
>+      fetch_max(value_type __i,
>+		memory_order __m = memory_order_seq_cst) const noexcept
>+      { return __atomic_impl::__fetch_max(this->_M_ptr, __i, __m); }
>+
>       value_type
>       operator+=(value_type __i) const noexcept
>       { return __atomic_impl::__add_fetch_flt(this->_M_ptr, __i); }
>diff --git a/libstdc++-v3/include/c_compatibility/stdatomic.h b/libstdc++-v3/include/c_compatibility/stdatomic.h
>index 72b9446eb17..177dd2f240e 100644
>--- a/libstdc++-v3/include/c_compatibility/stdatomic.h
>+++ b/libstdc++-v3/include/c_compatibility/stdatomic.h
>@@ -118,6 +118,10 @@ using std::atomic_fetch_xor;
> using std::atomic_fetch_xor_explicit;
> using std::atomic_fetch_and;
> using std::atomic_fetch_and_explicit;

Ditto

>+using std::atomic_fetch_min;
>+using std::atomic_fetch_min_explicit;
>+using std::atomic_fetch_max;
>+using std::atomic_fetch_max_explicit;
> using std::atomic_flag_test_and_set;
> using std::atomic_flag_test_and_set_explicit;
> using std::atomic_flag_clear;
>diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
>index ccb77fa6327..fc71d3a0c20 100644
>--- a/libstdc++-v3/include/std/atomic
>+++ b/libstdc++-v3/include/std/atomic
>@@ -1569,6 +1569,34 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 			      memory_order __m) noexcept
>     { return __a->fetch_xor(__i, __m); }

Ditto

>+  template<typename _ITp>
>+    inline _ITp
>+    atomic_fetch_min_explicit(__atomic_base<_ITp>* __a,
>+			      __atomic_val_t<_ITp> __i,
>+			      memory_order __m) noexcept
>+    { return __a->fetch_min(__i, __m); }
>+
>+  template<typename _ITp>
>+    inline _ITp
>+    atomic_fetch_min_explicit(volatile __atomic_base<_ITp>* __a,
>+			      __atomic_val_t<_ITp> __i,
>+			      memory_order __m) noexcept
>+    { return __a->fetch_min(__i, __m); }
>+
>+  template<typename _ITp>
>+    inline _ITp
>+    atomic_fetch_max_explicit(__atomic_base<_ITp>* __a,
>+			      __atomic_val_t<_ITp> __i,
>+			      memory_order __m) noexcept
>+    { return __a->fetch_max(__i, __m); }
>+
>+  template<typename _ITp>
>+    inline _ITp
>+    atomic_fetch_max_explicit(volatile __atomic_base<_ITp>* __a,
>+			      __atomic_val_t<_ITp> __i,
>+			      memory_order __m) noexcept
>+    { return __a->fetch_max(__i, __m); }
>+
>   template<typename _ITp>
>     inline _ITp
>     atomic_fetch_add(atomic<_ITp>* __a,
>@@ -1629,6 +1657,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 		     __atomic_val_t<_ITp> __i) noexcept
>     { return atomic_fetch_xor_explicit(__a, __i, memory_order_seq_cst); }

Ditto

>+  template<typename _ITp>
>+    inline _ITp
>+    atomic_fetch_min(__atomic_base<_ITp>* __a,
>+		     __atomic_val_t<_ITp> __i) noexcept
>+    { return atomic_fetch_min_explicit(__a, __i, memory_order_seq_cst); }
>+
>+  template<typename _ITp>
>+    inline _ITp
>+    atomic_fetch_min(volatile __atomic_base<_ITp>* __a,
>+		     __atomic_val_t<_ITp> __i) noexcept
>+    { return atomic_fetch_min_explicit(__a, __i, memory_order_seq_cst); }
>+
>+  template<typename _ITp>
>+    inline _ITp
>+    atomic_fetch_max(__atomic_base<_ITp>* __a,
>+		     __atomic_val_t<_ITp> __i) noexcept
>+    { return atomic_fetch_max_explicit(__a, __i, memory_order_seq_cst); }
>+
>+  template<typename _ITp>
>+    inline _ITp
>+    atomic_fetch_max(volatile __atomic_base<_ITp>* __a,
>+		     __atomic_val_t<_ITp> __i) noexcept
>+    { return atomic_fetch_max_explicit(__a, __i, memory_order_seq_cst); }
>+

All these new functions need to be exported from the std module, so
must be added to src/c++23/std.cc.in

> #ifdef __cpp_lib_atomic_float
>   template<>
>     struct atomic<float> : __atomic_float<float>
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/fetch_minmax.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/fetch_minmax.cc
>new file mode 100644
>index 00000000000..5293a349aaf
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/fetch_minmax.cc
>@@ -0,0 +1,168 @@
>+// Copyright The GNU Toolchain Authors.

No copyright and license headers in tests please, these tests are not
novel or interesting enough to need the headers. Please see other
recent tests.

>+// This file is part of the GNU ISO C++ Library.  This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+// GNU General Public License for more details.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-do run { target c++26 } }

Do these new tests need { dg-add-options libatomic } ? I think they
probably do.

What about { dg-require-atomic-cmpxchg-word "" } ?

>+#include <atomic>
>+#include <limits>
>+#include <testsuite_hooks.h>
>+
>+template<typename T>
>+void test_signed()
>+{
>+  std::atomic<T> a;
>+  const std::memory_order mo = std::memory_order_relaxed;
>+  T expected = 0;
>+  T value = 0;
>+
>+  a.store(100);
>+  value = a.fetch_min(50);
>+  VERIFY( value == 100 );
>+  VERIFY( a.load() == 50 );
>+
>+  value = a.fetch_min(75, mo);
>+  VERIFY( value == 50 );
>+  VERIFY( a.load() == 50 );
>+
>+  value = a.fetch_min(25);
>+  VERIFY( value == 50 );
>+  VERIFY( a.load() == 25 );
>+
>+  a.store(-10);
>+  value = a.fetch_min(-20);
>+  VERIFY( value == -10 );
>+  VERIFY( a.load() == -20 );
>+
>+  value = a.fetch_min(-5, mo);
>+  VERIFY( value == -20 );
>+  VERIFY( a.load() == -20 );
>+
>+  a.store(10);
>+  value = a.fetch_min(-5);
>+  VERIFY( value == 10 );
>+  VERIFY( a.load() == -5 );
>+
>+  a.store(20);
>+  value = a.fetch_max(50);
>+  VERIFY( value == 20 );
>+  VERIFY( a.load() == 50 );
>+
>+  value = a.fetch_max(30, mo);
>+  VERIFY( value == 50 );
>+  VERIFY( a.load() == 50 );
>+
>+  value = a.fetch_max(100);
>+  VERIFY( value == 50 );
>+  VERIFY( a.load() == 100 );
>+
>+  a.store(-50);
>+  value = a.fetch_max(-20);
>+  VERIFY( value == -50 );
>+  VERIFY( a.load() == -20 );
>+
>+  value = a.fetch_max(-10, mo);
>+  VERIFY( value == -20 );
>+  VERIFY( a.load() == -10 );
>+
>+  a.store(-10);
>+  value = a.fetch_max(5);
>+  VERIFY( value == -10 );
>+  VERIFY( a.load() == 5 );
>+
>+  T min_val = std::numeric_limits<T>::min();
>+  T max_val = std::numeric_limits<T>::max();
>+
>+  a.store(0);
>+  value = a.fetch_min(min_val);
>+  VERIFY( value == 0 );
>+  VERIFY( a.load() == min_val );
>+
>+  a.store(0);
>+  value = a.fetch_max(max_val);
>+  VERIFY( value == 0 );
>+  VERIFY( a.load() == max_val );
>+}
>+
>+template<typename T>
>+void test_unsigned()
>+{
>+  std::atomic<T> a;
>+  const std::memory_order mo = std::memory_order_relaxed;
>+  T expected = 0;
>+  T value = 0;
>+
>+  a.store(100);
>+  value = a.fetch_min(50);
>+  VERIFY( value == 100 );
>+  VERIFY( a.load() == 50 );
>+
>+  value = a.fetch_min(75, mo);
>+  VERIFY( value == 50 );
>+  VERIFY( a.load() == 50 );
>+
>+  value = a.fetch_min(25);
>+  VERIFY( value == 50 );
>+  VERIFY( a.load() == 25 );
>+
>+  a.store(20);
>+  value = a.fetch_max(50);
>+  VERIFY( value == 20 );
>+  VERIFY( a.load() == 50 );
>+
>+  value = a.fetch_max(30, mo);
>+  VERIFY( value == 50 );
>+  VERIFY( a.load() == 50 );
>+
>+  value = a.fetch_max(100);
>+  VERIFY( value == 50 );
>+  VERIFY( a.load() == 100 );
>+
>+  T min_val = std::numeric_limits<T>::min();
>+  T max_val = std::numeric_limits<T>::max();
>+
>+  a.store(10);
>+  value = a.fetch_min(min_val);
>+  VERIFY( value == 10 );
>+  VERIFY( a.load() == min_val );
>+
>+  a.store(10);
>+  value = a.fetch_max(max_val);
>+  VERIFY( value == 10 );
>+  VERIFY( a.load() == max_val );
>+}
>+
>+int main()
>+{
>+  test_signed<signed char>();
>+  test_signed<short>();
>+  test_signed<int>();
>+  test_signed<long>();
>+  test_signed<long long>();
>+
>+  test_unsigned<unsigned char>();
>+  test_unsigned<unsigned short>();
>+  test_unsigned<unsigned int>();
>+  test_unsigned<unsigned long>();
>+  test_unsigned<unsigned long long>();
>+
>+  return 0;
>+}
>\ No newline at end of file
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/fetch_minmax_order.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/fetch_minmax_order.cc
>new file mode 100644
>index 00000000000..3ed6f2e2e6f
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/fetch_minmax_order.cc
>@@ -0,0 +1,116 @@
>+// Copyright The GNU Toolchain Authors.
>+//
>+// This file is part of the GNU ISO C++ Library.  This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+// GNU General Public License for more details.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-do run { target c++26 } }
>+
>+#include <atomic>
>+#include <testsuite_hooks.h>
>+
>+void test_memory_orderings()
>+{
>+  std::atomic<int> a;
>+  int v;
>+
>+  a.store(100);
>+  v = a.fetch_min(50, std::memory_order_relaxed);
>+  VERIFY( v == 100 );
>+  VERIFY( a.load(std::memory_order_relaxed) == 50 );
>+
>+  a.store(100);
>+  v = a.fetch_min(50, std::memory_order_consume);
>+  VERIFY( v == 100 );
>+  VERIFY( a.load() == 50 );
>+
>+  a.store(100);
>+  v = a.fetch_min(50, std::memory_order_acquire);
>+  VERIFY( v == 100 );
>+  VERIFY( a.load() == 50 );
>+
>+  a.store(100);
>+  v = a.fetch_min(50, std::memory_order_release);
>+  VERIFY( v == 100 );
>+  VERIFY( a.load() == 50 );
>+
>+  a.store(100);
>+  v = a.fetch_min(50, std::memory_order_acq_rel);
>+  VERIFY( v == 100 );
>+  VERIFY( a.load() == 50 );
>+
>+  a.store(100);
>+  v = a.fetch_min(50, std::memory_order_seq_cst);
>+  VERIFY( v == 100 );
>+  VERIFY( a.load() == 50 );
>+
>+  a.store(20);
>+  v = a.fetch_max(50, std::memory_order_relaxed);
>+  VERIFY( v == 20 );
>+  VERIFY( a.load(std::memory_order_relaxed) == 50 );
>+
>+  a.store(20);
>+  v = a.fetch_max(50, std::memory_order_consume);
>+  VERIFY( v == 20 );
>+  VERIFY( a.load() == 50 );
>+
>+  a.store(20);
>+  v = a.fetch_max(50, std::memory_order_acquire);
>+  VERIFY( v == 20 );
>+  VERIFY( a.load() == 50 );
>+
>+  a.store(20);
>+  v = a.fetch_max(50, std::memory_order_release);
>+  VERIFY( v == 20 );
>+  VERIFY( a.load() == 50 );
>+
>+  a.store(20);
>+  v = a.fetch_max(50, std::memory_order_acq_rel);
>+  VERIFY( v == 20 );
>+  VERIFY( a.load() == 50 );
>+
>+  a.store(20);
>+  v = a.fetch_max(50, std::memory_order_seq_cst);
>+  VERIFY( v == 20 );
>+  VERIFY( a.load() == 50 );
>+}
>+
>+// Test volatile atomic operations
>+void test_volatile()
>+{
>+  volatile std::atomic<int> va;
>+  int v;
>+
>+  va.store(100);
>+  v = va.fetch_min(50);
>+  VERIFY( v == 100 );
>+  VERIFY( va.load() == 50 );
>+
>+  va.store(20);
>+  v = va.fetch_max(50, std::memory_order_relaxed);
>+  VERIFY( v == 20 );
>+  VERIFY( va.load() == 50 );
>+}
>+
>+int main()
>+{
>+  test_memory_orderings();
>+  test_volatile();
>+  return 0;
>+}
>\ No newline at end of file
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/nonmembers.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/nonmembers.cc
>index a07d9700ff4..3fb86db65b1 100644
>--- a/libstdc++-v3/testsuite/29_atomics/atomic_integral/nonmembers.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/nonmembers.cc
>@@ -111,6 +111,22 @@ test01()
>   static_assert( std::is_same<decltype(r37), int>::value, "" );
>   auto r38 = atomic_fetch_xor_explicit(&a, l, mo);
>   static_assert( std::is_same<decltype(r38), long>::value, "" );
>+  auto r39 = atomic_fetch_min(&v, i);
>+  static_assert( std::is_same<decltype(r39), int>::value, "" );
>+  auto r40 = atomic_fetch_min(&a, l);
>+  static_assert( std::is_same<decltype(r40), long>::value, "" );
>+  auto r41 = atomic_fetch_min_explicit(&v, i, mo);
>+  static_assert( std::is_same<decltype(r41), int>::value, "" );
>+  auto r42 = atomic_fetch_min_explicit(&a, l, mo);
>+  static_assert( std::is_same<decltype(r42), long>::value, "" );
>+  auto r43 = atomic_fetch_max(&v, i);
>+  static_assert( std::is_same<decltype(r43), int>::value, "" );
>+  auto r44 = atomic_fetch_max(&a, l);
>+  static_assert( std::is_same<decltype(r44), long>::value, "" );
>+  auto r45 = atomic_fetch_max_explicit(&v, i, mo);
>+  static_assert( std::is_same<decltype(r45), int>::value, "" );
>+  auto r46 = atomic_fetch_max_explicit(&a, l, mo);
>+  static_assert( std::is_same<decltype(r46), long>::value, "" );
> }
>
> void
>@@ -160,4 +176,12 @@ test02()
>   atomic_fetch_xor(&a, i);
>   atomic_fetch_xor_explicit(&v, i, mo);
>   atomic_fetch_xor_explicit(&a, i, mo);
>+  atomic_fetch_min(&v, i);
>+  atomic_fetch_min(&a, i);
>+  atomic_fetch_min_explicit(&v, i, mo);
>+  atomic_fetch_min_explicit(&a, i, mo);
>+  atomic_fetch_max(&v, i);
>+  atomic_fetch_max(&a, i);
>+  atomic_fetch_max_explicit(&v, i, mo);
>+  atomic_fetch_max_explicit(&a, i, mo);
> }
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc
>index 310434cefb5..5a282320bd7 100644
>--- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc
>@@ -155,9 +155,38 @@ test01()
>     v = a ^= 0x121;
>     VERIFY( v == 0x022 );
>     VERIFY( a == 0x022 );
>+
>+
>+    a = 100;
>+    v = a.fetch_min(50);
>+    VERIFY( v == 100 );
>+    VERIFY( a == 50 );
>+
>+    v = a.fetch_min(75, mo);
>+    VERIFY( v == 50 );
>+    VERIFY( a == 50 );
>+
>+    a = -10;
>+    v = a.fetch_min(-20);
>+    VERIFY( v == -10 );
>+    VERIFY( a == -20 );
>+
>+    a = 20;
>+    v = a.fetch_max(50);
>+    VERIFY( v == 20 );
>+    VERIFY( a == 50 );
>+
>+    v = a.fetch_max(30, mo);
>+    VERIFY( v == 50 );
>+    VERIFY( a == 50 );
>+
>+    a = -50;
>+    v = a.fetch_max(-20);
>+    VERIFY( v == -50 );
>+    VERIFY( a == -20 );
>   }
>
>-  VERIFY( value == 0x022 );
>+  VERIFY( value == -20 );
> }
>
> void
>@@ -292,9 +321,27 @@ test02()
>     v = a ^= 0x12;
>     VERIFY( v == 0x24 );
>     VERIFY( a == 0x24 );
>+
>+    a = 100;
>+    v = a.fetch_min(50);
>+    VERIFY( v == 100 );
>+    VERIFY( a == 50 );
>+
>+    v = a.fetch_min(75, mo);
>+    VERIFY( v == 50 );
>+    VERIFY( a == 50 );
>+
>+    a = 20;
>+    v = a.fetch_max(50);
>+    VERIFY( v == 20 );
>+    VERIFY( a == 50 );
>+
>+    v = a.fetch_max(30, mo);
>+    VERIFY( v == 50 );
>+    VERIFY( a == 50 );
>   }
>
>-  VERIFY( value == 0x24 );
>+  VERIFY( value == 50 );
> }
> void
> test03()
>diff --git a/libstdc++-v3/testsuite/29_atomics/headers/stdatomic.h/c_compat.cc b/libstdc++-v3/testsuite/29_atomics/headers/stdatomic.h/c_compat.cc
>index bcdb969b0c0..2e4c73c456c 100644
>--- a/libstdc++-v3/testsuite/29_atomics/headers/stdatomic.h/c_compat.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/headers/stdatomic.h/c_compat.cc
>@@ -131,6 +131,10 @@ static_assert( requires (::atomic_int* i, int* e) {
>   ::atomic_fetch_or_explicit(i, 1, memory_order_relaxed);
>   ::atomic_fetch_xor(i, 1);
>   ::atomic_fetch_xor_explicit(i, 1, memory_order_relaxed);
>+  ::atomic_fetch_min(i, 1);
>+  ::atomic_fetch_min_explicit(i, 1, memory_order_relaxed);
>+  ::atomic_fetch_max(i, 1);
>+  ::atomic_fetch_max_explicit(i, 1, memory_order_relaxed);
> } );
>
> static_assert( requires (::atomic_flag* f) {
>-- 
>2.44.0
>
>



More information about the Libstdc++ mailing list