1*bb722a7dSDimitry Andric //===-- A simple equivalent of std::atomic ----------------------*- C++ -*-===//
2*bb722a7dSDimitry Andric //
3*bb722a7dSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*bb722a7dSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*bb722a7dSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*bb722a7dSDimitry Andric //
7*bb722a7dSDimitry Andric //===----------------------------------------------------------------------===//
8*bb722a7dSDimitry Andric
9*bb722a7dSDimitry Andric #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H
10*bb722a7dSDimitry Andric #define LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H
11*bb722a7dSDimitry Andric
12*bb722a7dSDimitry Andric #include "src/__support/CPP/type_traits/has_unique_object_representations.h"
13*bb722a7dSDimitry Andric #include "src/__support/macros/attributes.h"
14*bb722a7dSDimitry Andric #include "src/__support/macros/config.h"
15*bb722a7dSDimitry Andric #include "src/__support/macros/properties/architectures.h"
16*bb722a7dSDimitry Andric
17*bb722a7dSDimitry Andric #include "type_traits.h"
18*bb722a7dSDimitry Andric
19*bb722a7dSDimitry Andric namespace LIBC_NAMESPACE_DECL {
20*bb722a7dSDimitry Andric namespace cpp {
21*bb722a7dSDimitry Andric
22*bb722a7dSDimitry Andric enum class MemoryOrder : int {
23*bb722a7dSDimitry Andric RELAXED = __ATOMIC_RELAXED,
24*bb722a7dSDimitry Andric CONSUME = __ATOMIC_CONSUME,
25*bb722a7dSDimitry Andric ACQUIRE = __ATOMIC_ACQUIRE,
26*bb722a7dSDimitry Andric RELEASE = __ATOMIC_RELEASE,
27*bb722a7dSDimitry Andric ACQ_REL = __ATOMIC_ACQ_REL,
28*bb722a7dSDimitry Andric SEQ_CST = __ATOMIC_SEQ_CST
29*bb722a7dSDimitry Andric };
30*bb722a7dSDimitry Andric
31*bb722a7dSDimitry Andric // These are a clang extension, see the clang documentation for more
32*bb722a7dSDimitry Andric // information:
33*bb722a7dSDimitry Andric // https://clang.llvm.org/docs/LanguageExtensions.html#scoped-atomic-builtins.
34*bb722a7dSDimitry Andric enum class MemoryScope : int {
35*bb722a7dSDimitry Andric #if defined(__MEMORY_SCOPE_SYSTEM) && defined(__MEMORY_SCOPE_DEVICE)
36*bb722a7dSDimitry Andric SYSTEM = __MEMORY_SCOPE_SYSTEM,
37*bb722a7dSDimitry Andric DEVICE = __MEMORY_SCOPE_DEVICE,
38*bb722a7dSDimitry Andric #else
39*bb722a7dSDimitry Andric SYSTEM = 0,
40*bb722a7dSDimitry Andric DEVICE = 0,
41*bb722a7dSDimitry Andric #endif
42*bb722a7dSDimitry Andric };
43*bb722a7dSDimitry Andric
44*bb722a7dSDimitry Andric namespace impl {
order(MemoryOrder mem_ord)45*bb722a7dSDimitry Andric LIBC_INLINE constexpr int order(MemoryOrder mem_ord) {
46*bb722a7dSDimitry Andric return static_cast<int>(mem_ord);
47*bb722a7dSDimitry Andric }
48*bb722a7dSDimitry Andric
scope(MemoryScope mem_scope)49*bb722a7dSDimitry Andric LIBC_INLINE constexpr int scope(MemoryScope mem_scope) {
50*bb722a7dSDimitry Andric return static_cast<int>(mem_scope);
51*bb722a7dSDimitry Andric }
52*bb722a7dSDimitry Andric
addressof(T & ref)53*bb722a7dSDimitry Andric template <class T> LIBC_INLINE T *addressof(T &ref) {
54*bb722a7dSDimitry Andric return __builtin_addressof(ref);
55*bb722a7dSDimitry Andric }
56*bb722a7dSDimitry Andric
infer_failure_order(MemoryOrder mem_ord)57*bb722a7dSDimitry Andric LIBC_INLINE constexpr int infer_failure_order(MemoryOrder mem_ord) {
58*bb722a7dSDimitry Andric if (mem_ord == MemoryOrder::RELEASE)
59*bb722a7dSDimitry Andric return order(MemoryOrder::RELAXED);
60*bb722a7dSDimitry Andric if (mem_ord == MemoryOrder::ACQ_REL)
61*bb722a7dSDimitry Andric return order(MemoryOrder::ACQUIRE);
62*bb722a7dSDimitry Andric return order(mem_ord);
63*bb722a7dSDimitry Andric }
64*bb722a7dSDimitry Andric } // namespace impl
65*bb722a7dSDimitry Andric
66*bb722a7dSDimitry Andric template <typename T> struct Atomic {
67*bb722a7dSDimitry Andric static_assert(is_trivially_copyable_v<T> && is_copy_constructible_v<T> &&
68*bb722a7dSDimitry Andric is_move_constructible_v<T> && is_copy_assignable_v<T> &&
69*bb722a7dSDimitry Andric is_move_assignable_v<T>,
70*bb722a7dSDimitry Andric "atomic<T> requires T to be trivially copyable, copy "
71*bb722a7dSDimitry Andric "constructible, move constructible, copy assignable, "
72*bb722a7dSDimitry Andric "and move assignable.");
73*bb722a7dSDimitry Andric
74*bb722a7dSDimitry Andric static_assert(cpp::has_unique_object_representations_v<T>,
75*bb722a7dSDimitry Andric "atomic<T> in libc only support types whose values has unique "
76*bb722a7dSDimitry Andric "object representations.");
77*bb722a7dSDimitry Andric
78*bb722a7dSDimitry Andric private:
79*bb722a7dSDimitry Andric // type conversion helper to avoid long c++ style casts
80*bb722a7dSDimitry Andric
81*bb722a7dSDimitry Andric // Require types that are 1, 2, 4, 8, or 16 bytes in length to be aligned to
82*bb722a7dSDimitry Andric // at least their size to be potentially used lock-free.
83*bb722a7dSDimitry Andric LIBC_INLINE_VAR static constexpr size_t MIN_ALIGNMENT =
84*bb722a7dSDimitry Andric (sizeof(T) & (sizeof(T) - 1)) || (sizeof(T) > 16) ? 0 : sizeof(T);
85*bb722a7dSDimitry Andric
86*bb722a7dSDimitry Andric LIBC_INLINE_VAR static constexpr size_t ALIGNMENT = alignof(T) > MIN_ALIGNMENT
87*bb722a7dSDimitry Andric ? alignof(T)
88*bb722a7dSDimitry Andric : MIN_ALIGNMENT;
89*bb722a7dSDimitry Andric
90*bb722a7dSDimitry Andric public:
91*bb722a7dSDimitry Andric using value_type = T;
92*bb722a7dSDimitry Andric
93*bb722a7dSDimitry Andric // We keep the internal value public so that it can be addressable.
94*bb722a7dSDimitry Andric // This is useful in places like the Linux futex operations where
95*bb722a7dSDimitry Andric // we need pointers to the memory of the atomic values. Load and store
96*bb722a7dSDimitry Andric // operations should be performed using the atomic methods however.
97*bb722a7dSDimitry Andric alignas(ALIGNMENT) value_type val;
98*bb722a7dSDimitry Andric
99*bb722a7dSDimitry Andric LIBC_INLINE constexpr Atomic() = default;
100*bb722a7dSDimitry Andric
101*bb722a7dSDimitry Andric // Initializes the value without using atomic operations.
AtomicAtomic102*bb722a7dSDimitry Andric LIBC_INLINE constexpr Atomic(value_type v) : val(v) {}
103*bb722a7dSDimitry Andric
104*bb722a7dSDimitry Andric LIBC_INLINE Atomic(const Atomic &) = delete;
105*bb722a7dSDimitry Andric LIBC_INLINE Atomic &operator=(const Atomic &) = delete;
106*bb722a7dSDimitry Andric
107*bb722a7dSDimitry Andric // Atomic load.
TAtomic108*bb722a7dSDimitry Andric LIBC_INLINE operator T() { return load(); }
109*bb722a7dSDimitry Andric
110*bb722a7dSDimitry Andric LIBC_INLINE T
111*bb722a7dSDimitry Andric load(MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
112*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
113*bb722a7dSDimitry Andric T res;
114*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_load)
115*bb722a7dSDimitry Andric __scoped_atomic_load(impl::addressof(val), impl::addressof(res),
116*bb722a7dSDimitry Andric impl::order(mem_ord), impl::scope(mem_scope));
117*bb722a7dSDimitry Andric #else
118*bb722a7dSDimitry Andric __atomic_load(impl::addressof(val), impl::addressof(res),
119*bb722a7dSDimitry Andric impl::order(mem_ord));
120*bb722a7dSDimitry Andric #endif
121*bb722a7dSDimitry Andric return res;
122*bb722a7dSDimitry Andric }
123*bb722a7dSDimitry Andric
124*bb722a7dSDimitry Andric // Atomic store.
125*bb722a7dSDimitry Andric LIBC_INLINE T operator=(T rhs) {
126*bb722a7dSDimitry Andric store(rhs);
127*bb722a7dSDimitry Andric return rhs;
128*bb722a7dSDimitry Andric }
129*bb722a7dSDimitry Andric
130*bb722a7dSDimitry Andric LIBC_INLINE void
131*bb722a7dSDimitry Andric store(T rhs, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
132*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
133*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_store)
134*bb722a7dSDimitry Andric __scoped_atomic_store(impl::addressof(val), impl::addressof(rhs),
135*bb722a7dSDimitry Andric impl::order(mem_ord), impl::scope(mem_scope));
136*bb722a7dSDimitry Andric #else
137*bb722a7dSDimitry Andric __atomic_store(impl::addressof(val), impl::addressof(rhs),
138*bb722a7dSDimitry Andric impl::order(mem_ord));
139*bb722a7dSDimitry Andric #endif
140*bb722a7dSDimitry Andric }
141*bb722a7dSDimitry Andric
142*bb722a7dSDimitry Andric // Atomic compare exchange
143*bb722a7dSDimitry Andric LIBC_INLINE bool compare_exchange_strong(
144*bb722a7dSDimitry Andric T &expected, T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
145*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
146*bb722a7dSDimitry Andric return __atomic_compare_exchange(
147*bb722a7dSDimitry Andric impl::addressof(val), impl::addressof(expected),
148*bb722a7dSDimitry Andric impl::addressof(desired), false, impl::order(mem_ord),
149*bb722a7dSDimitry Andric impl::infer_failure_order(mem_ord));
150*bb722a7dSDimitry Andric }
151*bb722a7dSDimitry Andric
152*bb722a7dSDimitry Andric // Atomic compare exchange (separate success and failure memory orders)
153*bb722a7dSDimitry Andric LIBC_INLINE bool compare_exchange_strong(
154*bb722a7dSDimitry Andric T &expected, T desired, MemoryOrder success_order,
155*bb722a7dSDimitry Andric MemoryOrder failure_order,
156*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
157*bb722a7dSDimitry Andric return __atomic_compare_exchange(
158*bb722a7dSDimitry Andric impl::addressof(val), impl::addressof(expected),
159*bb722a7dSDimitry Andric impl::addressof(desired), false, impl::order(success_order),
160*bb722a7dSDimitry Andric impl::order(failure_order));
161*bb722a7dSDimitry Andric }
162*bb722a7dSDimitry Andric
163*bb722a7dSDimitry Andric // Atomic compare exchange (weak version)
164*bb722a7dSDimitry Andric LIBC_INLINE bool compare_exchange_weak(
165*bb722a7dSDimitry Andric T &expected, T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
166*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
167*bb722a7dSDimitry Andric return __atomic_compare_exchange(
168*bb722a7dSDimitry Andric impl::addressof(val), impl::addressof(expected),
169*bb722a7dSDimitry Andric impl::addressof(desired), true, impl::order(mem_ord),
170*bb722a7dSDimitry Andric impl::infer_failure_order(mem_ord));
171*bb722a7dSDimitry Andric }
172*bb722a7dSDimitry Andric
173*bb722a7dSDimitry Andric // Atomic compare exchange (weak version with separate success and failure
174*bb722a7dSDimitry Andric // memory orders)
175*bb722a7dSDimitry Andric LIBC_INLINE bool compare_exchange_weak(
176*bb722a7dSDimitry Andric T &expected, T desired, MemoryOrder success_order,
177*bb722a7dSDimitry Andric MemoryOrder failure_order,
178*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
179*bb722a7dSDimitry Andric return __atomic_compare_exchange(
180*bb722a7dSDimitry Andric impl::addressof(val), impl::addressof(expected),
181*bb722a7dSDimitry Andric impl::addressof(desired), true, impl::order(success_order),
182*bb722a7dSDimitry Andric impl::order(failure_order));
183*bb722a7dSDimitry Andric }
184*bb722a7dSDimitry Andric
185*bb722a7dSDimitry Andric LIBC_INLINE T
186*bb722a7dSDimitry Andric exchange(T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
187*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
188*bb722a7dSDimitry Andric T ret;
189*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_exchange)
190*bb722a7dSDimitry Andric __scoped_atomic_exchange(impl::addressof(val), impl::addressof(desired),
191*bb722a7dSDimitry Andric impl::addressof(ret), impl::order(mem_ord),
192*bb722a7dSDimitry Andric impl::scope(mem_scope));
193*bb722a7dSDimitry Andric #else
194*bb722a7dSDimitry Andric __atomic_exchange(impl::addressof(val), impl::addressof(desired),
195*bb722a7dSDimitry Andric impl::addressof(ret), impl::order(mem_ord));
196*bb722a7dSDimitry Andric #endif
197*bb722a7dSDimitry Andric return ret;
198*bb722a7dSDimitry Andric }
199*bb722a7dSDimitry Andric
200*bb722a7dSDimitry Andric LIBC_INLINE T
201*bb722a7dSDimitry Andric fetch_add(T increment, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
202*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
203*bb722a7dSDimitry Andric static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
204*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_fetch_add)
205*bb722a7dSDimitry Andric return __scoped_atomic_fetch_add(impl::addressof(val), increment,
206*bb722a7dSDimitry Andric impl::order(mem_ord),
207*bb722a7dSDimitry Andric impl::scope(mem_scope));
208*bb722a7dSDimitry Andric #else
209*bb722a7dSDimitry Andric return __atomic_fetch_add(impl::addressof(val), increment,
210*bb722a7dSDimitry Andric impl::order(mem_ord));
211*bb722a7dSDimitry Andric #endif
212*bb722a7dSDimitry Andric }
213*bb722a7dSDimitry Andric
214*bb722a7dSDimitry Andric LIBC_INLINE T
215*bb722a7dSDimitry Andric fetch_or(T mask, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
216*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
217*bb722a7dSDimitry Andric static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
218*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_fetch_or)
219*bb722a7dSDimitry Andric return __scoped_atomic_fetch_or(impl::addressof(val), mask,
220*bb722a7dSDimitry Andric impl::order(mem_ord),
221*bb722a7dSDimitry Andric impl::scope(mem_scope));
222*bb722a7dSDimitry Andric #else
223*bb722a7dSDimitry Andric return __atomic_fetch_or(impl::addressof(val), mask, impl::order(mem_ord));
224*bb722a7dSDimitry Andric #endif
225*bb722a7dSDimitry Andric }
226*bb722a7dSDimitry Andric
227*bb722a7dSDimitry Andric LIBC_INLINE T
228*bb722a7dSDimitry Andric fetch_and(T mask, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
229*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
230*bb722a7dSDimitry Andric static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
231*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_fetch_and)
232*bb722a7dSDimitry Andric return __scoped_atomic_fetch_and(impl::addressof(val), mask,
233*bb722a7dSDimitry Andric impl::order(mem_ord),
234*bb722a7dSDimitry Andric impl::scope(mem_scope));
235*bb722a7dSDimitry Andric #else
236*bb722a7dSDimitry Andric return __atomic_fetch_and(impl::addressof(val), mask, impl::order(mem_ord));
237*bb722a7dSDimitry Andric #endif
238*bb722a7dSDimitry Andric }
239*bb722a7dSDimitry Andric
240*bb722a7dSDimitry Andric LIBC_INLINE T
241*bb722a7dSDimitry Andric fetch_sub(T decrement, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
242*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
243*bb722a7dSDimitry Andric static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
244*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_fetch_sub)
245*bb722a7dSDimitry Andric return __scoped_atomic_fetch_sub(impl::addressof(val), decrement,
246*bb722a7dSDimitry Andric impl::order(mem_ord),
247*bb722a7dSDimitry Andric impl::scope(mem_scope));
248*bb722a7dSDimitry Andric #else
249*bb722a7dSDimitry Andric return __atomic_fetch_sub(impl::addressof(val), decrement,
250*bb722a7dSDimitry Andric impl::order(mem_ord));
251*bb722a7dSDimitry Andric #endif
252*bb722a7dSDimitry Andric }
253*bb722a7dSDimitry Andric
254*bb722a7dSDimitry Andric // Set the value without using an atomic operation. This is useful
255*bb722a7dSDimitry Andric // in initializing atomic values without a constructor.
setAtomic256*bb722a7dSDimitry Andric LIBC_INLINE void set(T rhs) { val = rhs; }
257*bb722a7dSDimitry Andric };
258*bb722a7dSDimitry Andric
259*bb722a7dSDimitry Andric template <typename T> struct AtomicRef {
260*bb722a7dSDimitry Andric static_assert(is_trivially_copyable_v<T> && is_copy_constructible_v<T> &&
261*bb722a7dSDimitry Andric is_move_constructible_v<T> && is_copy_assignable_v<T> &&
262*bb722a7dSDimitry Andric is_move_assignable_v<T>,
263*bb722a7dSDimitry Andric "AtomicRef<T> requires T to be trivially copyable, copy "
264*bb722a7dSDimitry Andric "constructible, move constructible, copy assignable, "
265*bb722a7dSDimitry Andric "and move assignable.");
266*bb722a7dSDimitry Andric
267*bb722a7dSDimitry Andric static_assert(cpp::has_unique_object_representations_v<T>,
268*bb722a7dSDimitry Andric "AtomicRef<T> only supports types with unique object "
269*bb722a7dSDimitry Andric "representations.");
270*bb722a7dSDimitry Andric
271*bb722a7dSDimitry Andric private:
272*bb722a7dSDimitry Andric T *ptr;
273*bb722a7dSDimitry Andric
274*bb722a7dSDimitry Andric public:
275*bb722a7dSDimitry Andric // Constructor from T reference
AtomicRefAtomicRef276*bb722a7dSDimitry Andric LIBC_INLINE explicit constexpr AtomicRef(T &obj) : ptr(&obj) {}
277*bb722a7dSDimitry Andric
278*bb722a7dSDimitry Andric // Non-standard Implicit conversion from T*
AtomicRefAtomicRef279*bb722a7dSDimitry Andric LIBC_INLINE constexpr AtomicRef(T *obj) : ptr(obj) {}
280*bb722a7dSDimitry Andric
281*bb722a7dSDimitry Andric LIBC_INLINE AtomicRef(const AtomicRef &) = default;
282*bb722a7dSDimitry Andric LIBC_INLINE AtomicRef &operator=(const AtomicRef &) = default;
283*bb722a7dSDimitry Andric
284*bb722a7dSDimitry Andric // Atomic load
TAtomicRef285*bb722a7dSDimitry Andric LIBC_INLINE operator T() const { return load(); }
286*bb722a7dSDimitry Andric
287*bb722a7dSDimitry Andric LIBC_INLINE T
288*bb722a7dSDimitry Andric load(MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
289*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {
290*bb722a7dSDimitry Andric T res;
291*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_load)
292*bb722a7dSDimitry Andric __scoped_atomic_load(ptr, &res, impl::order(mem_ord),
293*bb722a7dSDimitry Andric impl::scope(mem_scope));
294*bb722a7dSDimitry Andric #else
295*bb722a7dSDimitry Andric __atomic_load(ptr, &res, impl::order(mem_ord));
296*bb722a7dSDimitry Andric #endif
297*bb722a7dSDimitry Andric return res;
298*bb722a7dSDimitry Andric }
299*bb722a7dSDimitry Andric
300*bb722a7dSDimitry Andric // Atomic store
301*bb722a7dSDimitry Andric LIBC_INLINE T operator=(T rhs) const {
302*bb722a7dSDimitry Andric store(rhs);
303*bb722a7dSDimitry Andric return rhs;
304*bb722a7dSDimitry Andric }
305*bb722a7dSDimitry Andric
306*bb722a7dSDimitry Andric LIBC_INLINE void
307*bb722a7dSDimitry Andric store(T rhs, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
308*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {
309*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_store)
310*bb722a7dSDimitry Andric __scoped_atomic_store(ptr, &rhs, impl::order(mem_ord),
311*bb722a7dSDimitry Andric impl::scope(mem_scope));
312*bb722a7dSDimitry Andric #else
313*bb722a7dSDimitry Andric __atomic_store(ptr, &rhs, impl::order(mem_ord));
314*bb722a7dSDimitry Andric #endif
315*bb722a7dSDimitry Andric }
316*bb722a7dSDimitry Andric
317*bb722a7dSDimitry Andric // Atomic compare exchange (strong)
318*bb722a7dSDimitry Andric LIBC_INLINE bool compare_exchange_strong(
319*bb722a7dSDimitry Andric T &expected, T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
320*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {
321*bb722a7dSDimitry Andric return __atomic_compare_exchange(ptr, &expected, &desired, false,
322*bb722a7dSDimitry Andric impl::order(mem_ord),
323*bb722a7dSDimitry Andric impl::infer_failure_order(mem_ord));
324*bb722a7dSDimitry Andric }
325*bb722a7dSDimitry Andric
326*bb722a7dSDimitry Andric // Atomic compare exchange (strong, separate success/failure memory orders)
327*bb722a7dSDimitry Andric LIBC_INLINE bool compare_exchange_strong(
328*bb722a7dSDimitry Andric T &expected, T desired, MemoryOrder success_order,
329*bb722a7dSDimitry Andric MemoryOrder failure_order,
330*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {
331*bb722a7dSDimitry Andric return __atomic_compare_exchange(ptr, &expected, &desired, false,
332*bb722a7dSDimitry Andric impl::order(success_order),
333*bb722a7dSDimitry Andric impl::order(failure_order));
334*bb722a7dSDimitry Andric }
335*bb722a7dSDimitry Andric
336*bb722a7dSDimitry Andric // Atomic exchange
337*bb722a7dSDimitry Andric LIBC_INLINE T
338*bb722a7dSDimitry Andric exchange(T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
339*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {
340*bb722a7dSDimitry Andric T ret;
341*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_exchange)
342*bb722a7dSDimitry Andric __scoped_atomic_exchange(ptr, &desired, &ret, impl::order(mem_ord),
343*bb722a7dSDimitry Andric impl::scope(mem_scope));
344*bb722a7dSDimitry Andric #else
345*bb722a7dSDimitry Andric __atomic_exchange(ptr, &desired, &ret, impl::order(mem_ord));
346*bb722a7dSDimitry Andric #endif
347*bb722a7dSDimitry Andric return ret;
348*bb722a7dSDimitry Andric }
349*bb722a7dSDimitry Andric
350*bb722a7dSDimitry Andric LIBC_INLINE T fetch_add(
351*bb722a7dSDimitry Andric T increment, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
352*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {
353*bb722a7dSDimitry Andric static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
354*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_fetch_add)
355*bb722a7dSDimitry Andric return __scoped_atomic_fetch_add(ptr, increment, impl::order(mem_ord),
356*bb722a7dSDimitry Andric impl::scope(mem_scope));
357*bb722a7dSDimitry Andric #else
358*bb722a7dSDimitry Andric return __atomic_fetch_add(ptr, increment, impl::order(mem_ord));
359*bb722a7dSDimitry Andric #endif
360*bb722a7dSDimitry Andric }
361*bb722a7dSDimitry Andric
362*bb722a7dSDimitry Andric LIBC_INLINE T
363*bb722a7dSDimitry Andric fetch_or(T mask, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
364*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {
365*bb722a7dSDimitry Andric static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
366*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_fetch_or)
367*bb722a7dSDimitry Andric return __scoped_atomic_fetch_or(ptr, mask, impl::order(mem_ord),
368*bb722a7dSDimitry Andric impl::scope(mem_scope));
369*bb722a7dSDimitry Andric #else
370*bb722a7dSDimitry Andric return __atomic_fetch_or(ptr, mask, impl::order(mem_ord));
371*bb722a7dSDimitry Andric #endif
372*bb722a7dSDimitry Andric }
373*bb722a7dSDimitry Andric
374*bb722a7dSDimitry Andric LIBC_INLINE T fetch_and(
375*bb722a7dSDimitry Andric T mask, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
376*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {
377*bb722a7dSDimitry Andric static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
378*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_fetch_and)
379*bb722a7dSDimitry Andric return __scoped_atomic_fetch_and(ptr, mask, impl::order(mem_ord),
380*bb722a7dSDimitry Andric impl::scope(mem_scope));
381*bb722a7dSDimitry Andric #else
382*bb722a7dSDimitry Andric return __atomic_fetch_and(ptr, mask, impl::order(mem_ord));
383*bb722a7dSDimitry Andric #endif
384*bb722a7dSDimitry Andric }
385*bb722a7dSDimitry Andric
386*bb722a7dSDimitry Andric LIBC_INLINE T fetch_sub(
387*bb722a7dSDimitry Andric T decrement, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
388*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) const {
389*bb722a7dSDimitry Andric static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
390*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_fetch_sub)
391*bb722a7dSDimitry Andric return __scoped_atomic_fetch_sub(ptr, decrement, impl::order(mem_ord),
392*bb722a7dSDimitry Andric impl::scope(mem_scope));
393*bb722a7dSDimitry Andric #else
394*bb722a7dSDimitry Andric return __atomic_fetch_sub(ptr, decrement, impl::order(mem_ord));
395*bb722a7dSDimitry Andric #endif
396*bb722a7dSDimitry Andric }
397*bb722a7dSDimitry Andric };
398*bb722a7dSDimitry Andric
399*bb722a7dSDimitry Andric // Permit CTAD when generating an atomic reference.
400*bb722a7dSDimitry Andric template <typename T> AtomicRef(T &) -> AtomicRef<T>;
401*bb722a7dSDimitry Andric
402*bb722a7dSDimitry Andric // Issue a thread fence with the given memory ordering.
403*bb722a7dSDimitry Andric LIBC_INLINE void atomic_thread_fence(
404*bb722a7dSDimitry Andric MemoryOrder mem_ord,
405*bb722a7dSDimitry Andric [[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
406*bb722a7dSDimitry Andric #if __has_builtin(__scoped_atomic_thread_fence)
407*bb722a7dSDimitry Andric __scoped_atomic_thread_fence(static_cast<int>(mem_ord),
408*bb722a7dSDimitry Andric static_cast<int>(mem_scope));
409*bb722a7dSDimitry Andric #else
410*bb722a7dSDimitry Andric __atomic_thread_fence(static_cast<int>(mem_ord));
411*bb722a7dSDimitry Andric #endif
412*bb722a7dSDimitry Andric }
413*bb722a7dSDimitry Andric
414*bb722a7dSDimitry Andric // Establishes memory synchronization ordering of non-atomic and relaxed atomic
415*bb722a7dSDimitry Andric // accesses, as instructed by order, between a thread and a signal handler
416*bb722a7dSDimitry Andric // executed on the same thread. This is equivalent to atomic_thread_fence,
417*bb722a7dSDimitry Andric // except no instructions for memory ordering are issued. Only reordering of
418*bb722a7dSDimitry Andric // the instructions by the compiler is suppressed as order instructs.
atomic_signal_fence(MemoryOrder mem_ord)419*bb722a7dSDimitry Andric LIBC_INLINE void atomic_signal_fence([[maybe_unused]] MemoryOrder mem_ord) {
420*bb722a7dSDimitry Andric #if __has_builtin(__atomic_signal_fence)
421*bb722a7dSDimitry Andric __atomic_signal_fence(static_cast<int>(mem_ord));
422*bb722a7dSDimitry Andric #else
423*bb722a7dSDimitry Andric // if the builtin is not ready, use asm as a full compiler barrier.
424*bb722a7dSDimitry Andric asm volatile("" ::: "memory");
425*bb722a7dSDimitry Andric #endif
426*bb722a7dSDimitry Andric }
427*bb722a7dSDimitry Andric } // namespace cpp
428*bb722a7dSDimitry Andric } // namespace LIBC_NAMESPACE_DECL
429*bb722a7dSDimitry Andric
430*bb722a7dSDimitry Andric #endif // LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H
431