[libcxx-commits] [libcxx] [libc++][In progress] Floating Point Atomic (PR #67799)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Sep 29 05:55:00 PDT 2023
https://github.com/huixie90 created https://github.com/llvm/llvm-project/pull/67799
- implement P0020R6 Floating Point Atomic
Differential Revision: https://reviews.llvm.org/D153981
>From 1a8bc86c1e2fb28d8fe96ab02f9130cdce2e70ef Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Wed, 28 Jun 2023 15:47:39 +0100
Subject: [PATCH] [libc++][In progress] Floating Point Atomic
- implement P0020R6 Floating Point Atomic
Differential Revision: https://reviews.llvm.org/D153981
---
libcxx/include/__atomic/atomic.h | 118 ++++++++++++++++++
.../atomics.types.float/copy.compile.pass.cpp | 29 +++++
.../atomics.types.float/ctor.pass.cpp | 39 ++++++
.../atomics.types.float/fetch_add.pass.cpp | 81 ++++++++++++
.../atomics.types.float/fetch_sub.pass.cpp | 50 ++++++++
.../atomics.types.float/lockfree.pass.cpp | 52 ++++++++
.../typedef.compile.pass.cpp | 25 ++++
7 files changed, 394 insertions(+)
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/copy.compile.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
create mode 100644 libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/typedef.compile.pass.cpp
diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h
index 68df7f12c1e585f..bb701d73df3d5ad 100644
--- a/libcxx/include/__atomic/atomic.h
+++ b/libcxx/include/__atomic/atomic.h
@@ -14,11 +14,15 @@
#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_pointer.h>
+#include <__utility/forward.h>
#include <cstddef>
+#include <cstdio>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -136,6 +140,120 @@ 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>
+ : public __atomic_base<_Tp>
+{
+ private:
+ // see lib/Sema/SemaChecking.cpp approx. line 6748, function IsAllowedValueType
+ // // LLVM Parser does not allow atomicrmw with x86_fp80 type.
+ //if (ValType->isSpecificBuiltinType(BuiltinType::LongDouble) &&
+ // &Context.getTargetInfo().getLongDoubleFormat() ==
+ // &llvm::APFloat::x87DoubleExtended())
+
+ // this is platform dependent. How can we get the correct answer?
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool __has_rmw_builtin = !std::is_same_v<_Tp, long double>;
+
+ template <class _This, class _BuiltinOp, class _Operation>
+ _LIBCPP_HIDE_FROM_ABI static _Tp __rmw_op(_This&& __self, _Tp __operand, memory_order __m, _BuiltinOp __builtin_op, _Operation __operation) {
+ 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)) {
+ if constexpr (std::is_same_v<_Tp, long double>){
+ // https://reviews.llvm.org/D53965
+ // https://bugs.llvm.org/show_bug.cgi?id=48634
+ // clang bug: __old is not updated on failure for atomic<long double>
+ __old = __self.load(memory_order_relaxed);
+ }
+ __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 __operand, auto __order){
+ return std::__cxx_atomic_fetch_add(__a, __operand, __order);
+ };
+ return __rmw_op(std::forward<_This>(__self), __operand, __m, __builtin_op, std::plus<>{});
+ }
+
+ template <class _This>
+ _LIBCPP_HIDE_FROM_ABI static _Tp __fetch_sub(_This&& __self, _Tp __operand, memory_order __m) {
+ auto __builtin_op = [](auto __a, auto __operand, auto __order){
+ return std::__cxx_atomic_fetch_sub(__a, __operand, __order);
+ };
+ return __rmw_op(std::forward<_This>(__self), __operand, __m, __builtin_op, std::minus<>{});
+ }
+
+ 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
+ {__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/test/std/atomics/atomics.types.generic/atomics.types.float/copy.compile.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/copy.compile.pass.cpp
new file mode 100644
index 000000000000000..3984d621ad3ab04
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/copy.compile.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// atomic(const atomic&) = delete;
+// atomic& operator=(const atomic&) = delete;
+// atomic& operator=(const atomic&) volatile = delete;
+
+#include <atomic>
+#include <type_traits>
+
+template <class T>
+void test() {
+ static_assert(!std::is_copy_assignable_v<std::atomic<T>>);
+ static_assert(!std::is_copy_constructible_v<std::atomic<T>>);
+ static_assert(!std::is_move_constructible_v<std::atomic<T>>);
+ static_assert(!std::is_move_assignable_v<std::atomic<T>>);
+ static_assert(!std::is_assignable_v<volatile std::atomic<T>&, const std::atomic<T>&>);
+}
+
+template void test<float>();
+template void test<double>();
+template void test<long double>();
diff --git a/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
new file mode 100644
index 000000000000000..8fc6ac0f34cf1ed
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/ctor.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// constexpr atomic() noexcept;
+// constexpr atomic(floating-point-type) noexcept;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_macros.h"
+
+template <class T>
+void test() {
+ // constexpr atomic() noexcept;
+ {
+ constexpr std::atomic<T> a = {};
+ assert(a.load() == T(0));
+ }
+
+ // constexpr atomic(floating-point-type) noexcept;
+ {
+ constexpr std::atomic<T> a = T(5.2);
+ assert(a.load() == T(5.2));
+ }
+}
+
+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/fetch_add.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
new file mode 100644
index 000000000000000..32f5f4097165ae4
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_add.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// 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;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+#include <thread>
+#include <vector>
+
+#include "test_macros.h"
+#include "make_test_thread.h"
+
+template <class T>
+void test() {
+ // fetch_add
+ {
+ std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a.fetch_add(1.2);
+ assert(r == T(3.1));
+ assert(a.load() == T(3.1) + T(1.2));
+ }
+
+ // fetch_add volatile
+ {
+ volatile std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a.fetch_add(1.2);
+ assert(r == T(3.1));
+ assert(a.load() == T(3.1) + T(1.2));
+ }
+
+ // fetch_add concurrent
+ {
+ constexpr auto numberOfThreads = 4;
+ constexpr auto loop = 1000;
+
+ std::atomic<T> at;
+
+ std::vector<std::thread> threads;
+ threads.reserve(numberOfThreads);
+ for (auto i = 0; i < numberOfThreads; ++i) {
+ threads.emplace_back([&at]() {
+ for (auto j = 0; j < loop; ++j) {
+ at.fetch_add(T(1.234));
+ }
+ });
+ }
+
+ for (auto& thread : threads) {
+ thread.join();
+ }
+
+ const auto times = [](T t, int n) {
+ T res(0);
+ for (auto i = 0; i < n; ++i) {
+ res += t;
+ }
+ return res;
+ };
+
+ assert(at.load() == times(1.234, numberOfThreads * loop));
+ }
+}
+
+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/fetch_sub.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
new file mode 100644
index 000000000000000..ce3aa57e506acfd
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/fetch_sub.pass.cpp
@@ -0,0 +1,50 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// 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;
+
+#include <atomic>
+#include <cassert>
+#include <concepts>
+
+#include "test_macros.h"
+
+template <class T>
+void test() {
+ // fetch_add
+ {
+ std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a.fetch_add(1.2);
+ assert(r == T(3.1));
+ assert(a.load() == T(3.1) + T(1.2));
+ }
+
+ // fetch_sub
+ {
+ std::atomic<T> a(3.1);
+ std::same_as<T> decltype(auto) r = a.fetch_sub(1.2);
+ assert(r == T(3.1));
+ assert(a.load() == T(3.1) - T(1.2));
+ }
+}
+
+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/lockfree.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
new file mode 100644
index 000000000000000..b632df79c330486
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/lockfree.pass.cpp
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// 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>
+concept isLockFreeNoexcept = requires(T t) {
+ { t.is_lock_free() } noexcept;
+};
+
+template <class T>
+void test() {
+ static_assert(isLockFreeNoexcept<const std::atomic<T>>);
+ static_assert(isLockFreeNoexcept<const volatile std::atomic<T>>);
+
+ // static constexpr bool is_always_lock_free = implementation-defined;
+ { [[maybe_unused]] constexpr std::same_as<const bool> decltype(auto) r = std::atomic<T>::is_always_lock_free; }
+
+ // bool is_lock_free() const volatile noexcept;
+ {
+ const volatile std::atomic<T> a;
+ [[maybe_unused]] std::same_as<bool> decltype(auto) r = a.is_lock_free();
+ }
+
+ // bool is_lock_free() const noexcept;
+ {
+ const std::atomic<T> a;
+ [[maybe_unused]] std::same_as<bool> decltype(auto) r = a.is_lock_free();
+ }
+}
+
+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/typedef.compile.pass.cpp b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/typedef.compile.pass.cpp
new file mode 100644
index 000000000000000..1046665f1c8fa26
--- /dev/null
+++ b/libcxx/test/std/atomics/atomics.types.generic/atomics.types.float/typedef.compile.pass.cpp
@@ -0,0 +1,25 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// using value_type = floating-point-type;
+// using difference_type = value_type;
+
+#include <atomic>
+#include <type_traits>
+
+template <class T>
+void test() {
+ static_assert(std::is_same_v<typename std::atomic<T>::value_type, T>);
+ static_assert(std::is_same_v<typename std::atomic<T>::difference_type, T>);
+}
+
+template void test<float>();
+template void test<double>();
+template void test<long double>();
More information about the libcxx-commits
mailing list