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? --- 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 + namespace __atomic_impl + { + template<typename _Tp> + using _Val = typename remove_volatile<_Tp>::type; + + 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 + _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 }; @@ -1293,6 +1332,92 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __newval; } } + + 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 + { + 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); } + 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); } + 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); } + 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; +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); } + 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); } + 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); } + #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. +// +// 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 <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
