[libcxx-commits] [libcxx] [libc++] Implement P0493R5: Atomic minimum/maximum (PR #180333)
Connector Switch via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jul 10 06:58:09 PDT 2026
c8ef wrote:
```diff
diff --git a/libcxx/include/__atomic/support/c11.h b/libcxx/include/__atomic/support/c11.h
index 899502422570..c42041499d5e 100644
--- a/libcxx/include/__atomic/support/c11.h
+++ b/libcxx/include/__atomic/support/c11.h
@@ -261,9 +261,90 @@ __cxx_atomic_fetch_xor(__cxx_atomic_base_impl<_Tp>* __a, _Tp __pattern, memory_o
std::addressof(__a->__a_value), __pattern, static_cast<__memory_order_underlying_t>(__order));
}
-// Clang's __c11_atomic_fetch_max/min builtins do not accept pointer
-// arguments, so dispatch to a CAS loop for pointer types.
-// TODO: Use the builtin for pointer types once Clang accepts them.
+#if _LIBCPP_STD_VER >= 20
+
+// In C++20 and later, dispatch to the builtin if it supports the type,
+// otherwise fallback to a CAS loop (e.g. for pointer types on older Clang builds).
+template <class _Tp>
+concept __has_c11_atomic_minmax = requires(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val) {
+ __c11_atomic_fetch_max(
+ std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(memory_order_relaxed));
+ __c11_atomic_fetch_min(
+ std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(memory_order_relaxed));
+};
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+ if constexpr (__has_c11_atomic_minmax<_Tp>) {
+ return __c11_atomic_fetch_max(
+ std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+ } else {
+ _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+ _Tp __value;
+ do {
+ __value = __ret > __val ? __ret : __val;
+ } while (
+ !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+ return __ret;
+ }
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+ if constexpr (__has_c11_atomic_minmax<_Tp>) {
+ return __c11_atomic_fetch_max(
+ std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+ } else {
+ _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+ _Tp __value;
+ do {
+ __value = __ret > __val ? __ret : __val;
+ } while (
+ !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+ return __ret;
+ }
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+ if constexpr (__has_c11_atomic_minmax<_Tp>) {
+ return __c11_atomic_fetch_min(
+ std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+ } else {
+ _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+ _Tp __value;
+ do {
+ __value = __ret < __val ? __ret : __val;
+ } while (
+ !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+ return __ret;
+ }
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp
+__cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order __order) _NOEXCEPT {
+ if constexpr (__has_c11_atomic_minmax<_Tp>) {
+ return __c11_atomic_fetch_min(
+ std::addressof(__a->__a_value), __val, static_cast<__memory_order_underlying_t>(__order));
+ } else {
+ _Tp __ret = std::__cxx_atomic_load(__a, memory_order_relaxed);
+ _Tp __value;
+ do {
+ __value = __ret < __val ? __ret : __val;
+ } while (
+ !std::__cxx_atomic_compare_exchange_weak(__a, std::addressof(__ret), __value, __order, memory_order_relaxed));
+ return __ret;
+ }
+}
+
+#else // _LIBCPP_STD_VER >= 20
+
+// Clang's __c11_atomic_fetch_max/min builtins didn't originally accept pointer
+// arguments, so dispatch to a CAS loop for pointer types in older standards.
template <class _Tp, __enable_if_t<!is_pointer<_Tp>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _Tp
__cxx_atomic_fetch_max(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __val, memory_order __order) _NOEXCEPT {
@@ -336,6 +417,8 @@ __cxx_atomic_fetch_min(__cxx_atomic_base_impl<_Tp>* __a, _Tp __val, memory_order
return __ret;
}
+#endif // _LIBCPP_STD_VER >= 20
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP___ATOMIC_SUPPORT_C11_H
```
I think if we go with the concept approach, simply applying the above diff to the current branch will suffice. @huixie90 @ldionne, could you please take a look and let me know if applying this patch is preferable to keeping the current branch as is? Really hoping to resolve this final blocker!
https://github.com/llvm/llvm-project/pull/180333
More information about the libcxx-commits
mailing list