[libcxx-commits] [libcxx] [libc++] Floating Point Atomic (PR #67799)
via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jul 14 11:14:36 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Hui (huixie90)
<details>
<summary>Changes</summary>
- implement P0020R6 Floating Point Atomic
Differential Revision: https://reviews.llvm.org/D153981
---
Patch is 87.53 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/67799.diff
26 Files Affected:
- (modified) libcxx/docs/ReleaseNotes/18.rst (+1)
- (modified) libcxx/docs/Status/Cxx20Papers.csv (+1-1)
- (modified) libcxx/include/__atomic/atomic.h (+136)
- (modified) libcxx/include/__atomic/cxx_atomic_impl.h (+47)
- (modified) libcxx/include/atomic (+66)
- (added) libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp (+51)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp (+62)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp (+226)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp (+244)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/copy.compile.pass.cpp (+29)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp (+68)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/exchange.pass.cpp (+77)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp (+118)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp (+119)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/load.pass.cpp (+140)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp (+61)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_all.pass.cpp (+98)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/notify_one.pass.cpp (+82)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.float.pass.cpp (+58)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.minus_equals.pass.cpp (+102)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/operator.plus_equals.pass.cpp (+102)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/store.pass.cpp (+114)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h (+122)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/types.compile.pass.cpp (+28)
- (added) libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/wait.pass.cpp (+124)
- (modified) libcxx/utils/libcxx/test/features.py (+10)
``````````diff
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 5f5ff83ca5512..c28a5b73f7ec6 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -49,6 +49,7 @@ Implemented Papers
- P2614R2 - Deprecate ``numeric_limits::has_denorm``
- P0053R7 - C++ Synchronized Buffered Ostream (in the experimental library)
- P2467R1 - Support exclusive mode for fstreams
+- P0020R6 - Floating Point Atomic
Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index 07f53ee995ea1..7aff860c68cf9 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -2,7 +2,7 @@
"`P0463R1 <https://wg21.link/P0463R1>`__","LWG","Endian just Endian","Toronto","|Complete|","7.0"
"`P0674R1 <https://wg21.link/P0674R1>`__","LWG","Extending make_shared to Support Arrays","Toronto","|Complete|","15.0"
"","","","","","",""
-"`P0020R6 <https://wg21.link/P0020R6>`__","LWG","Floating Point Atomic","Albuquerque","",""
+"`P0020R6 <https://wg21.link/P0020R6>`__","LWG","Floating Point Atomic","Albuquerque","|Complete|","18.0"
"`P0053R7 <https://wg21.link/P0053R7>`__","LWG","C++ Synchronized Buffered Ostream","Albuquerque","|Complete|","18.0"
"`P0202R3 <https://wg21.link/P0202R3>`__","LWG","Add constexpr modifiers to functions in <algorithm> and <utility> Headers","Albuquerque","|Complete|","12.0"
"`P0415R1 <https://wg21.link/P0415R1>`__","LWG","Constexpr for ``std::complex``\ ","Albuquerque","|Complete|","16.0"
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 47de6b958a96c..449802a2e3040 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -14,11 +14,17 @@
#include <__atomic/cxx_atomic_impl.h>
#include <__atomic/memory_order.h>
#include <__config>
+#include <__functional/operations.h>
#include <__memory/addressof.h>
+#include <__type_traits/is_floating_point.h>
#include <__type_traits/is_function.h>
#include <__type_traits/is_same.h>
+#include <__type_traits/remove_const.h>
#include <__type_traits/remove_pointer.h>
+#include <__type_traits/remove_volatile.h>
+#include <__utility/forward.h>
#include <cstddef>
+#include <cstring>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -136,6 +142,136 @@ struct atomic<_Tp*>
atomic& operator=(const atomic&) volatile = delete;
};
+#if _LIBCPP_STD_VER >= 20
+template <class _Tp>
+ requires is_floating_point_v<_Tp>
+struct atomic<_Tp> : __atomic_base<_Tp> {
+ private:
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool __is_fp80_long_double() {
+ // Only x87-fp80 long double has 64-bit mantissa
+ return __LDBL_MANT_DIG__ == 64 && std::is_same_v<_Tp, long double>;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool __has_rmw_builtin() {
+# ifndef _LIBCPP_COMPILER_CLANG_BASED
+ return false;
+# else
+ // The builtin __cxx_atomic_fetch_add errors during compilation for
+ // long double on platforms with fp80 format.
+ // For more details, see
+ // lib/Sema/SemaChecking.cpp function IsAllowedValueType
+ // LLVM Parser does not allow atomicrmw with x86_fp80 type.
+ // if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) &&
+ // &Context.getTargetInfo().getLongDoubleFormat() ==
+ // &llvm::APFloat::x87DoubleExtended())
+ // For more info
+ // https://github.com/llvm/llvm-project/issues/68602
+ // https://reviews.llvm.org/D53965
+ return !__is_fp80_long_double();
+# endif
+ }
+
+ template <class _This, class _Operation, class _BuiltinOp>
+ _LIBCPP_HIDE_FROM_ABI static _Tp
+ __rmw_op(_This&& __self, _Tp __operand, memory_order __m, _Operation __operation, _BuiltinOp __builtin_op) {
+ if constexpr (__has_rmw_builtin()) {
+ return __builtin_op(std::addressof(std::forward<_This>(__self).__a_), __operand, __m);
+ } else {
+ _Tp __old = __self.load(memory_order_relaxed);
+ _Tp __new = __operation(__old, __operand);
+ while (!__self.compare_exchange_weak(__old, __new, __m, memory_order_relaxed)) {
+# ifdef _LIBCPP_COMPILER_CLANG_BASED
+ if constexpr (__is_fp80_long_double()) {
+ // https://github.com/llvm/llvm-project/issues/47978
+ // clang bug: __old is not updated on failure for atomic<long double>::compare_exchange_weak
+ // Note __old = __self.load(memory_order_relaxed) will not work
+ std::__cxx_atomic_load_inplace(std::addressof(__self.__a_), &__old, memory_order_relaxed);
+ }
+# endif
+ __new = __operation(__old, __operand);
+ }
+ return __old;
+ }
+ }
+
+ template <class _This>
+ _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_add(_This&& __self, _Tp __operand, memory_order __m) {
+ auto __builtin_op = [](auto __a, auto __builtin_operand, auto __order) {
+ return std::__cxx_atomic_fetch_add(__a, __builtin_operand, __order);
+ };
+ return __rmw_op(std::forward<_This>(__self), __operand, __m, std::plus<>{}, __builtin_op);
+ }
+
+ template <class _This>
+ _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_sub(_This&& __self, _Tp __operand, memory_order __m) {
+ auto __builtin_op = [](auto __a, auto __builtin_operand, auto __order) {
+ return std::__cxx_atomic_fetch_sub(__a, __builtin_operand, __order);
+ };
+ return __rmw_op(std::forward<_This>(__self), __operand, __m, std::minus<>{}, __builtin_op);
+ }
+
+ public:
+ using __base = __atomic_base<_Tp>;
+ using value_type = _Tp;
+ using difference_type = value_type;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr atomic() noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr atomic(_Tp __d) noexcept : __base(__d) {}
+
+ atomic(const atomic&) = delete;
+ atomic& operator=(const atomic&) = delete;
+ atomic& operator=(const atomic&) volatile = delete;
+
+ _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __d) volatile noexcept
+ requires __base::is_always_lock_free
+ {
+ __base::store(__d);
+ return __d;
+ }
+ _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __d) noexcept {
+ __base::store(__d);
+ return __d;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+ requires __base::is_always_lock_free
+ {
+ return __fetch_add(*this, __op, __m);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+ return __fetch_add(*this, __op, __m);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) volatile noexcept
+ requires __base::is_always_lock_free
+ {
+ return __fetch_sub(*this, __op, __m);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __op, memory_order __m = memory_order_seq_cst) noexcept {
+ return __fetch_sub(*this, __op, __m);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) volatile noexcept
+ requires __base::is_always_lock_free
+ {
+ return fetch_add(__op) + __op;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __op) noexcept { return fetch_add(__op) + __op; }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) volatile noexcept
+ requires __base::is_always_lock_free
+ {
+ return fetch_sub(__op) - __op;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __op) noexcept { return fetch_sub(__op) - __op; }
+};
+
+#endif // _LIBCPP_STD_VER >= 20
+
// atomic_is_lock_free
template <class _Tp>
diff --git a/libcxx/include/__atomic/cxx_atomic_impl.h b/libcxx/include/__atomic/cxx_atomic_impl.h
index d670fddc3934c..5d724669fee87 100644
--- a/libcxx/include/__atomic/cxx_atomic_impl.h
+++ b/libcxx/include/__atomic/cxx_atomic_impl.h
@@ -128,6 +128,18 @@ _Tp __cxx_atomic_load(const volatile __cxx_atomic_base_impl<_Tp>* __a,
return __ret;
}
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI void
+__cxx_atomic_load_inplace(const volatile __cxx_atomic_base_impl<_Tp>* __a, _Tp* __dst, memory_order __order) {
+ __atomic_load(std::addressof(__a->__a_value), __dst, __to_gcc_order(__order));
+}
+
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI void
+__cxx_atomic_load_inplace(const __cxx_atomic_base_impl<_Tp>* __a, _Tp* __dst, memory_order __order) {
+ __atomic_load(std::addressof(__a->__a_value), __dst, __to_gcc_order(__order));
+}
+
template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI
_Tp __cxx_atomic_load(const __cxx_atomic_base_impl<_Tp>* __a, memory_order __order) {
@@ -362,6 +374,21 @@ _Tp __cxx_atomic_load(__cxx_atomic_base_impl<_Tp> const* __a, memory_order __ord
const_cast<__ptr_type>(std::addressof(__a->__a_value)), static_cast<__memory_order_underlying_t>(__order));
}
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI void
+__cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const volatile* __a, _Tp* __dst, memory_order __order) _NOEXCEPT {
+ using __ptr_type = __remove_const_t<decltype(__a->__a_value)>*;
+ *__dst = __c11_atomic_load(
+ const_cast<__ptr_type>(std::addressof(__a->__a_value)), static_cast<__memory_order_underlying_t>(__order));
+}
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI void
+__cxx_atomic_load_inplace(__cxx_atomic_base_impl<_Tp> const* __a, _Tp* __dst, memory_order __order) _NOEXCEPT {
+ using __ptr_type = __remove_const_t<decltype(__a->__a_value)>*;
+ *__dst = __c11_atomic_load(
+ const_cast<__ptr_type>(std::addressof(__a->__a_value)), static_cast<__memory_order_underlying_t>(__order));
+}
+
template<class _Tp>
_LIBCPP_HIDE_FROM_ABI
_Tp __cxx_atomic_exchange(__cxx_atomic_base_impl<_Tp> volatile* __a, _Tp __value, memory_order __order) _NOEXCEPT {
@@ -558,6 +585,16 @@ struct __cxx_atomic_lock_impl {
__unlock();
return __old;
}
+ _LIBCPP_HIDE_FROM_ABI void __read_inplace(_Tp* __dst) const volatile {
+ __lock();
+ __cxx_atomic_assign_volatile(*__dst, __a_value);
+ __unlock();
+ }
+ _LIBCPP_HIDE_FROM_ABI void __read_inplace(_Tp* __dst) const {
+ __lock();
+ *__dst = __a_value;
+ __unlock();
+ }
};
template <typename _Tp>
@@ -597,6 +634,16 @@ _Tp __cxx_atomic_load(const __cxx_atomic_lock_impl<_Tp>* __a, memory_order) {
return __a->__read();
}
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI void
+__cxx_atomic_load(const volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp* __dst, memory_order) {
+ __a->__read_inplace(__dst);
+}
+template <typename _Tp>
+_LIBCPP_HIDE_FROM_ABI void __cxx_atomic_load(const __cxx_atomic_lock_impl<_Tp>* __a, _Tp* __dst, memory_order) {
+ __a->__read_inplace(__dst);
+}
+
template <typename _Tp>
_LIBCPP_HIDE_FROM_ABI
_Tp __cxx_atomic_exchange(volatile __cxx_atomic_lock_impl<_Tp>* __a, _Tp __value, memory_order) {
diff --git a/libcxx/include/atomic b/libcxx/include/atomic
index 2f122a707bdc3..7bed8fd8bacfc 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -262,6 +262,72 @@ struct atomic<T*>
void notify_all() noexcept;
};
+template<>
+struct atomic<floating-point-type> { // since C++20
+ using value_type = floating-point-type;
+ using difference_type = value_type;
+
+ static constexpr bool is_always_lock_free = implementation-defined;
+ bool is_lock_free() const volatile noexcept;
+ bool is_lock_free() const noexcept;
+
+ constexpr atomic() noexcept;
+ constexpr atomic(floating-point-type) noexcept;
+ atomic(const atomic&) = delete;
+ atomic& operator=(const atomic&) = delete;
+ atomic& operator=(const atomic&) volatile = delete;
+
+ void store(floating-point-type, memory_order = memory_order::seq_cst) volatile noexcept;
+ void store(floating-point-type, memory_order = memory_order::seq_cst) noexcept;
+ floating-point-type operator=(floating-point-type) volatile noexcept;
+ floating-point-type operator=(floating-point-type) noexcept;
+ floating-point-type load(memory_order = memory_order::seq_cst) volatile noexcept;
+ floating-point-type load(memory_order = memory_order::seq_cst) noexcept;
+ operator floating-point-type() volatile noexcept;
+ operator floating-point-type() noexcept;
+
+ floating-point-type exchange(floating-point-type,
+ memory_order = memory_order::seq_cst) volatile noexcept;
+ floating-point-type exchange(floating-point-type,
+ memory_order = memory_order::seq_cst) noexcept;
+ bool compare_exchange_weak(floating-point-type&, floating-point-type,
+ memory_order, memory_order) volatile noexcept;
+ bool compare_exchange_weak(floating-point-type&, floating-point-type,
+ memory_order, memory_order) noexcept;
+ bool compare_exchange_strong(floating-point-type&, floating-point-type,
+ memory_order, memory_order) volatile noexcept;
+ bool compare_exchange_strong(floating-point-type&, floating-point-type,
+ memory_order, memory_order) noexcept;
+ bool compare_exchange_weak(floating-point-type&, floating-point-type,
+ memory_order = memory_order::seq_cst) volatile noexcept;
+ bool compare_exchange_weak(floating-point-type&, floating-point-type,
+ memory_order = memory_order::seq_cst) noexcept;
+ bool compare_exchange_strong(floating-point-type&, floating-point-type,
+ memory_order = memory_order::seq_cst) volatile noexcept;
+ bool compare_exchange_strong(floating-point-type&, floating-point-type,
+ memory_order = memory_order::seq_cst) noexcept;
+
+ floating-point-type fetch_add(floating-point-type,
+ memory_order = memory_order::seq_cst) volatile noexcept;
+ floating-point-type fetch_add(floating-point-type,
+ memory_order = memory_order::seq_cst) noexcept;
+ floating-point-type fetch_sub(floating-point-type,
+ memory_order = memory_order::seq_cst) volatile noexcept;
+ floating-point-type fetch_sub(floating-point-type,
+ memory_order = memory_order::seq_cst) noexcept;
+
+ floating-point-type operator+=(floating-point-type) volatile noexcept;
+ floating-point-type operator+=(floating-point-type) noexcept;
+ floating-point-type operator-=(floating-point-type) volatile noexcept;
+ floating-point-type operator-=(floating-point-type) noexcept;
+
+ void wait(floating-point-type, memory_order = memory_order::seq_cst) const volatile noexcept;
+ void wait(floating-point-type, memory_order = memory_order::seq_cst) const noexcept;
+ void notify_one() volatile noexcept;
+ void notify_one() noexcept;
+ void notify_all() volatile noexcept;
+ void notify_all() noexcept;
+};
// [atomics.nonmembers], non-member functions
template<class T>
diff --git a/libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp b/libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
new file mode 100644
index 0000000000000..46511530c7c75
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: target={{.+}}-windows-gnu
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
+
+// static constexpr bool is_always_lock_free = implementation-defined;
+// bool is_lock_free() const volatile noexcept;
+// bool is_lock_free() const noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_macros.h"
+
+template <class T>
+void test() {
+ // static constexpr bool is_always_lock_free = implementation-defined;
+ {
+ bool r = std::atomic<T>::is_always_lock_free;
+ assert(r == __atomic_always_lock_free(sizeof(std::__cxx_atomic_impl<T>), 0));
+ }
+
+ // bool is_lock_free() const volatile noexcept;
+ {
+ const volatile std::atomic<T> a;
+ bool r = a.is_lock_free();
+ assert(r == __cxx_atomic_is_lock_free(sizeof(std::__cxx_atomic_impl<T>)));
+ }
+
+ // bool is_lock_free() const noexcept;
+ {
+ const std::atomic<T> a;
+ bool r = a.is_lock_free();
+ assert(r == __cxx_atomic_is_lock_free(sizeof(std::__cxx_atomic_impl<T>)));
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
new file mode 100644
index 0000000000000..8efb556cb5d99
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: target={{.+}}-windows-gnu
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
+
+// floating-point-type operator=(floating-point-type) volatile noexcept;
+// floating-point-type operator=(floating-point-type) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <type_traits>
+
+#include "test_helper.h"
+#include "test_macros.h"
+
+template <class T>
+concept HasVolatileAssign = requires(volatile std::atomic<T>& a, T t) { a = t; };
+
+template <class T, template <class> class MaybeVolatile = std::type_identity_t>
+void test_impl() {
+ static_assert(HasVolatileAssign<T> == std::atomic<T>::is_always_lock_free);
+
+ static_assert(noexcept(std::declval<MaybeVolatile<std::atomic<T>>&>() = (T(0))));
+
+ // assignment
+ {
+ MaybeVolatile<std::atomic<T>> a(3.1);
+ std::same_as<T> decltype(auto) r = (a = T(1.2));
+ assert(a.load() == T(1.2));
+ assert(r == T(1.2));
+ }
+
+ // memory_order::seq_cst
+ {
+ auto assign = [](MaybeVolatile<std::atomic<T>>& x, T, T new_val) { x = new_val; };
+ auto load = [](MaybeVolatile<std::atomic<T>>& x) { return x.load(); };
+ test_seq_cst<T, MaybeVolatile>(assign, load);
+ }
+}
+
+template <class T>
+void test() {
+ test_impl<T>();
+ if constexpr (std::atomic<T>::is_always_lock_free) {
+ test_impl<T, std::add_volatile_t>();
+ }
+}
+
+int main(int, char**) {
+ test<float>();
+ test<double>();
+ test<long double>();
+
+ return 0;
+}
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp
new file mode 100644
index 0000000000000..839d79d3a4110
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp
@@ -0,0 +1,226 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// ADDITIONAL_COMPILE_FLAGS(has-latomic): -latomic
+
+// bool compare_exchange_strong(T& expected, T desired,
+// memory_order success, memory_order failure) volatile noexcept;
+// bool compare_exchange_strong(T& expected, T desired,
+// memory_order success, memory_order failure) noexcept;
+// bool compare_exchange_strong(T& expected, T desired,
+// memory...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/67799
More information about the libcxx-commits
mailing list