#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H
#define LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/macros/properties/architectures.h"
#include "type_traits.h"
namespace LIBC_NAMESPACE_DECL {
namespace cpp {
enum class MemoryOrder : int {
RELAXED = __ATOMIC_RELAXED,
CONSUME = __ATOMIC_CONSUME,
ACQUIRE = __ATOMIC_ACQUIRE,
RELEASE = __ATOMIC_RELEASE,
ACQ_REL = __ATOMIC_ACQ_REL,
SEQ_CST = __ATOMIC_SEQ_CST
};
enum class MemoryScope : int {
#if defined(__MEMORY_SCOPE_SYSTEM) && defined(__MEMORY_SCOPE_DEVICE)
SYSTEM = __MEMORY_SCOPE_SYSTEM,
DEVICE = __MEMORY_SCOPE_DEVICE,
#else
SYSTEM = 0,
DEVICE = 0,
#endif
};
template <typename T> struct Atomic {
static_assert(is_arithmetic_v<T>, "Only arithmetic types can be atomic.");
private:
static constexpr int ALIGNMENT = sizeof(T) > alignof(T) ? sizeof(T)
: alignof(T);
public:
using value_type = T;
alignas(ALIGNMENT) value_type val;
constexpr Atomic() = default;
constexpr Atomic(value_type v) : val(v) {}
Atomic(const Atomic &) = delete;
Atomic &operator=(const Atomic &) = delete;
operator T() { return __atomic_load_n(&val, int(MemoryOrder::SEQ_CST)); }
T load(MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
#if __has_builtin(__scoped_atomic_load_n)
return __scoped_atomic_load_n(&val, int(mem_ord), (int)(mem_scope));
#else
return __atomic_load_n(&val, int(mem_ord));
#endif
}
T operator=(T rhs) {
__atomic_store_n(&val, rhs, int(MemoryOrder::SEQ_CST));
return rhs;
}
void store(T rhs, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
#if __has_builtin(__scoped_atomic_store_n)
__scoped_atomic_store_n(&val, rhs, int(mem_ord), (int)(mem_scope));
#else
__atomic_store_n(&val, rhs, int(mem_ord));
#endif
}
bool compare_exchange_strong(
T &expected, T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
return __atomic_compare_exchange_n(&val, &expected, desired, false,
int(mem_ord), int(mem_ord));
}
bool compare_exchange_strong(
T &expected, T desired, MemoryOrder success_order,
MemoryOrder failure_order,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
return __atomic_compare_exchange_n(&val, &expected, desired, false,
static_cast<int>(success_order),
static_cast<int>(failure_order));
}
bool compare_exchange_weak(
T &expected, T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
return __atomic_compare_exchange_n(&val, &expected, desired, true,
static_cast<int>(mem_ord),
static_cast<int>(mem_ord));
}
bool compare_exchange_weak(
T &expected, T desired, MemoryOrder success_order,
MemoryOrder failure_order,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
return __atomic_compare_exchange_n(&val, &expected, desired, true,
static_cast<int>(success_order),
static_cast<int>(failure_order));
}
T exchange(T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
#if __has_builtin(__scoped_atomic_exchange_n)
return __scoped_atomic_exchange_n(&val, desired, int(mem_ord),
(int)(mem_scope));
#else
return __atomic_exchange_n(&val, desired, int(mem_ord));
#endif
}
T fetch_add(T increment, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
#if __has_builtin(__scoped_atomic_fetch_add)
return __scoped_atomic_fetch_add(&val, increment, int(mem_ord),
(int)(mem_scope));
#else
return __atomic_fetch_add(&val, increment, int(mem_ord));
#endif
}
T fetch_or(T mask, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
#if __has_builtin(__scoped_atomic_fetch_or)
return __scoped_atomic_fetch_or(&val, mask, int(mem_ord), (int)(mem_scope));
#else
return __atomic_fetch_or(&val, mask, int(mem_ord));
#endif
}
T fetch_and(T mask, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
#if __has_builtin(__scoped_atomic_fetch_and)
return __scoped_atomic_fetch_and(&val, mask, int(mem_ord),
(int)(mem_scope));
#else
return __atomic_fetch_and(&val, mask, int(mem_ord));
#endif
}
T fetch_sub(T decrement, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
#if __has_builtin(__scoped_atomic_fetch_sub)
return __scoped_atomic_fetch_sub(&val, decrement, int(mem_ord),
(int)(mem_scope));
#else
return __atomic_fetch_sub(&val, decrement, int(mem_ord));
#endif
}
void set(T rhs) { val = rhs; }
};
LIBC_INLINE void atomic_thread_fence([[maybe_unused]] MemoryOrder mem_ord) {
#ifdef LIBC_TARGET_ARCH_IS_NVPTX
__nvvm_membar_sys();
#else
__atomic_thread_fence(static_cast<int>(mem_ord));
#endif
}
LIBC_INLINE void atomic_signal_fence([[maybe_unused]] MemoryOrder mem_ord) {
#if __has_builtin(__atomic_signal_fence)
__atomic_signal_fence(static_cast<int>(mem_ord));
#else
asm volatile("" ::: "memory");
#endif
}
}
}
#endif