diff --git a/libcxx/include/__atomic/atomic.h b/libcxx/include/__atomic/atomic.h --- 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 +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -136,6 +140,119 @@ atomic& operator=(const atomic&) volatile = delete; }; +#if _LIBCPP_STD_VER >= 20 +template 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()) + // TODO: do we have a builtin to check this? + _LIBCPP_HIDE_FROM_ABI static constexpr bool __has_rmw_builtin = !std::is_same_v<_Tp, long double>; + + template + _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); + auto __orig = __old; + _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://bugs.llvm.org/show_bug.cgi?id=48634 + // clang bug: __old is not updated on failure + __old = __self.load(memory_order_relaxed); + } + __new = __operation(__old, __operand); + } + return __old; + } + } + + template + _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 + _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 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 --- /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 +#include + +template +void test() { + static_assert(!std::is_copy_assignable_v>); + static_assert(!std::is_copy_constructible_v>); + static_assert(!std::is_move_constructible_v>); + static_assert(!std::is_move_assignable_v>); + static_assert(!std::is_assignable_v&, const std::atomic&>); +} + +template void test(); +template void test(); +template void test(); 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 --- /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 +#include +#include + +#include "test_macros.h" + +template +void test() { + // constexpr atomic() noexcept; + { + constexpr std::atomic a = {}; + assert(a.load() == T(0)); + } + + // constexpr atomic(floating-point-type) noexcept; + { + constexpr std::atomic a = T(5.2); + assert(a.load() == T(5.2)); + } +} + +int main(int, char**) { + test(); + test(); + test(); + 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 --- /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 +#include +#include +#include +#include + +#include "test_macros.h" +#include "make_test_thread.h" + +template +void test() { + // fetch_add + { + std::atomic a(3.1); + std::same_as 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 a(3.1); + std::same_as 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 = 50; + + std::atomic at; + + std::vector 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(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(); + test(); + test(); + + 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 --- /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 +#include +#include + +#include "test_macros.h" + +template +void test() { + // fetch_add + { + std::atomic a(3.1); + std::same_as 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 a(3.1); + std::same_as 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(); + test(); + test(); + + 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 --- /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 +#include +#include + +#include "test_macros.h" + +template +concept isLockFreeNoexcept = requires(T t) { + { t.is_lock_free() } noexcept; +}; + +template +void test() { + static_assert(isLockFreeNoexcept>); + static_assert(isLockFreeNoexcept>); + + // static constexpr bool is_always_lock_free = implementation-defined; + { [[maybe_unused]] constexpr std::same_as decltype(auto) r = std::atomic::is_always_lock_free; } + + // bool is_lock_free() const volatile noexcept; + { + const volatile std::atomic a; + [[maybe_unused]] std::same_as decltype(auto) r = a.is_lock_free(); + } + + // bool is_lock_free() const noexcept; + { + const std::atomic a; + [[maybe_unused]] std::same_as decltype(auto) r = a.is_lock_free(); + } +} + +int main(int, char**) { + test(); + test(); + test(); + + 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 --- /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 +#include + +template +void test() { + static_assert(std::is_same_v::value_type, T>); + static_assert(std::is_same_v::difference_type, T>); +} + +template void test(); +template void test(); +template void test();