-
Notifications
You must be signed in to change notification settings - Fork 14.5k
[libc++] Floating Point Atomic #67799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
1a8bc86
to
317d84b
Compare
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
Show resolved
Hide resolved
✅ With the latest revision this PR passed the C/C++ code formatter. |
libcxx/test/libcxx/atomics/atomics.types.generic/atomics.types.float/locakfree.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h
Outdated
Show resolved
Hide resolved
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
Show resolved
Hide resolved
.../test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp
Outdated
Show resolved
Hide resolved
173b3f4
to
21057f5
Compare
✅ With the latest revision this PR passed the Python code formatter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM once CI is passing and the temporary tests have been cleaned up. Thanks a lot for all the digging you had to do to make this work, it's really non-trivial! It would be great to get some attention on #47978, maybe we should ping the clang or compiler-rt folks there.
...xx/test/std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/test_helper.h
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's ship this.
There are a few things that don't work especially with long double
. However, this patch doesn't make the situation any worse than it already is, since people can already witness the broken behavior with e.g. atomic<long double>::load()
today. I think this patch is in a really good shape and the fixes on top are going to be Clang or compiler-rt fixes.
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/assign.pass.cpp
Show resolved
Hide resolved
- implement P0020R6 Floating Point Atomic Differential Revision: https://reviews.llvm.org/D153981
Hmm, I just looked and the
The So I'm not certain @huixie90 bypassed any checks he should not have bypassed due to ongoing flakes as part of merging this. I'm not claiming this means those tests aren't broken on tsan/msan, I'm just saying the information we have with the checks on this patch doesn't allow claiming that. |
Never mind, libcxx actions failed different tests, I have no idea if they are related. |
Hi, I am a bit confused now. I looked at the UI under this tab and it was all green. (That was how I check if anything failed before). But It does seem that the UI changed a bit |
This adds a few UNSUPPORTED annotations for tests that hang on some msan bots: https://lab.llvm.org/buildbot/#/builders/sanitizer-x86_64-linux-bootstrap-msan https://lab.llvm.org/buildbot/#/builders/sanitizer-aarch64-linux-bootstrap-msan We still haven't figured out the root cause of them hanging on these bots but not on the main libc++ CI infra.
@ldionne our CI used to be under this "show all checks" button but now it is gone |
To be clear, this is a Fuchsia Clang toolchain, but the test is actually failing on Linux so it's not Fuchsia-specific. What's somewhat unusual for our toolchain is that we ship I see neither |
Hello, the test is conditionally adding -latomic if compiling a dummy programme with -latomic return 0. In Libc++ CI, on Linux -latomic would be valid so it would add -latomic in the test. On Mac, -latomic would make the dummy program fail to compile/link , so it would not add the flag. But the test link fine without the flag. I guess in Fushia, the -latomic dummy program test returned false , so it did not add the flag. What is the right way to link under fushia ? |
To emphasize again, we're seeing this issue on Linux and not on Fuchsia. The existing llvm-project/libcxx/utils/libcxx/test/features.py Lines 113 to 122 in 48f5855
|
Actually, is there a reason why we even need to link against any of these libraries manually? Why doesn't Clang link against compiler-rt automatically? It does that for other system libraries by default, why is the atomic support library treated differently? |
Undo a part of #73152. These test do not hang, but unexpectedlty pass on aarch64 https://lab.llvm.org/buildbot/#/builders/74/builds/23708 Tests work on aarch64 and probably any platform without fp80.
I think we got to the bottom of it with Hui just now. He's going to upload a patch soon that should address this issue. Note that these tests are going to be disabled on platforms like Fuchsia where we don't know how to get |
Just for clarity; the issue that @petrhosek has reported wasn’t about Fuchsia, it is the Fuchsia team’s toolchain build, targeting Linux - the issue lies in, AFAIK, what library to link in for atomics. But a fix that disables those tests if unable to link in working atomics probably is right in any case. |
These tests are hanging on my Gentoo amd64 machine:
I've killed them after they were running at 100% CPU utilization for over 20 minutes. Full output (after killing)
|
We really shouldn't be depending on far away configuration options like LLVM_HAVE_LINK_VERSION_SCRIPT here. This patch simplifies the enablement of the linker scripts and as a result gets rid of an undesirable dependency on HandleLLVMOptions.cmake. As a drive-by, the patch also stops taking into account whether Python3 is available. This should have no bearing on whether we generate a linker script or not, which is required for correctness. If someone tries to build libc++ and generate a linker script but Python3 is not available, they should get an error instead of silently getting an incorrect installation of the library.
Can you get a stacktrace in the deadlock condition @mgorny ? |
Note that I think this is "resolved" now. @huixie90 turned off the tests that were broken for |
The corresponding feature was implemented in LLVM 18 (by #67799), but this FTM wasn't added before.
The corresponding feature was implemented in LLVM 18 (by llvm#67799), but this FTM wasn't added before. (cherry picked from commit 2207e3e)
@llvm/pr-subscribers-libcxx Author: Hui (huixie90) Changes
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:
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]
|
Differential Revision: https://reviews.llvm.org/D153981