On Fri, 16 Jan 2026 at 08:28, Soumya AR <[email protected]> wrote:
>
>
> Hi Jonathan,
>
> Thanks for taking a look.
>
> I've updated the libstdc++ patch with the changes you suggested.
> This patch is built upon the support for atomic min/max builtins in GCC, which
> are lowered to CAS loops at gimple level.
>
> Jakub had mentioned earlier that we cannot commit the libstdc++ changes 
> without
> implementing the builtins.

Hmm, I think we could add the libstdc++ parts without the builtins,
because the concepts mean we gracefully fallback for libstdc++ but
would use the builtins if libstdc++ headers are compiled by a version
of Clang that already has them.


> Hence why both these patches were sent out together
> earlier as well:
>
> https://gcc.gnu.org/pipermail/gcc-patches/2025-November/700786.html
>
> Addressed the other major questions below:
>
> > On 5 Jan 2026, at 7:49 PM, Jonathan Wakely <[email protected]> wrote:
> >
> > External email: Use caution opening links or attachments
> >
> >
> > On Sat, 15 Nov 2025 at 18:11 +0530, [email protected] wrote:
> >> From: Soumya AR <[email protected]>
> >>
> >> 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);
> >        };
> >
>
> I agree, combining these makes more sense. Without the op_fetch variants,
> we can reduce it to just one concept.

Nice, thanks.

>
> >
> > 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.
> >
>
> I assume for those cases, we will still follow the approach of only
> adding a generic builtin that libstdc++ can call.
>
> The single-use concepts will still exist, but I suppose we can similarly
> combine the min/max checks for both fmaximum and fmaximum_num? For a total
> of 3 checks.

Sounds good. Much better than 12  :-)

>
> >
> >> ---
> >>
> >> 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 <[email protected]>
> >>
> >> 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!)
> >
> My bad! I was using this as a placeholder for the correct check.
> > You need to define the __cpp_lib_atomic_min_max macro in
> > bits/version.def and regenerate bits/version.h
> >
> Added.
>
> >> +  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?
>
> Right, as I understand, the op_fetch variants of the other operations
> in libstdc++ only exist for operator overloading? If that's the case
> then they aren't needed for min/max.

Right, we need add_fetch for operator-- and operator+= but there is no
"infix max" operator for expressions like `a max b` which would
require a max_fetch operation.

>
> That being said, I'm not entirely sure if we should remove them from the
> builtins as well? Since GCC has historically implemented both variants of the
> other atomic instructions.. If this was only done to support std::atomic
> then there's no point in keeping the builtins, but I don't know if we have
> some other reason for it.

I am 99% sure they are there only to support the C++ std::atomic
operator members.
C does not have atomic_add_fetch etc. and C++ doesn't expose them as
std::atomic_add_fetch functions either, so I'm pretty sure they're
only for supporting the std::atomic::operator@ members.

However, the legacy __sync builtins did provide the __sync_OP_and_fetch forms:
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html

Since you're not adding __sync forms of these new builtins, I don't
think that's very relevant.

>
> >
> >> +      {
> >> +      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;

Actually I've just noticed that the new functions shouldn't be in
<stdatomic.h> at all. Sorry for not noticing that sooner.

The functions are not in C, so it makes sense that we don't add them
to the stdatomic.h compatibility header.

Code which wants to be compatible with both C and C++ has to avoid
these functions, not expect them to be in stdatomic.h (because they
won't be there for C).

Apart from this change to stdatomic.h I think this looks good now.


> >> 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 "" } ?
>
> Yes, you're right, ideally should have both directives.
>
> However, on re-working this patch, I don't think these tests are needed in
> libstdc++ at all. I already have correctness tests for the builtins in the
> GCC testsuite.
>
> To test the API, I've created a C++26 version of nonmembers.cc for 
> fetch_min/max.
> And another fetch_min/max version for the correctness tests for atomic_ref.
>
> Or for both of these, should we just update the existing tests to C++26? What
> about c_compat.cc?
>
> Thanks,
> Soumya
>
> >> +#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

Reply via email to