-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[libc] fix atomic and apply an explicit check on unique object representations #119715
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -9,6 +9,7 @@ | |||
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H | ||||
#define LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H | ||||
|
||||
#include "src/__support/CPP/type_traits/has_unique_object_representations.h" | ||||
#include "src/__support/macros/attributes.h" | ||||
#include "src/__support/macros/config.h" | ||||
#include "src/__support/macros/properties/architectures.h" | ||||
|
@@ -47,12 +48,11 @@ template <typename T> struct Atomic { | |||
"constructible, move constructible, copy assignable, " | ||||
"and move assignable."); | ||||
|
||||
static_assert(cpp::has_unique_object_representations_v<T>, | ||||
"atomic<T> in libc only support types whose values has unique " | ||||
"object representations."); | ||||
|
||||
private: | ||||
// The value stored should be appropriately aligned so that | ||||
// hardware instructions used to perform atomic operations work | ||||
// correctly. | ||||
static constexpr int ALIGNMENT = sizeof(T) > alignof(T) ? sizeof(T) | ||||
: alignof(T); | ||||
// type conversion helper to avoid long c++ style casts | ||||
LIBC_INLINE static int order(MemoryOrder mem_ord) { | ||||
return static_cast<int>(mem_ord); | ||||
|
@@ -62,6 +62,17 @@ template <typename T> struct Atomic { | |||
return static_cast<int>(mem_scope); | ||||
} | ||||
|
||||
LIBC_INLINE static T *addressof(T &ref) { return __builtin_addressof(ref); } | ||||
|
||||
// Require types that are 1, 2, 4, 8, or 16 bytes in length to be aligned to | ||||
// at least their size to be potentially used lock-free. | ||||
LIBC_INLINE_VAR static constexpr size_t MIN_ALIGNMENT = | ||||
(sizeof(T) & (sizeof(T) - 1)) || (sizeof(T) > 16) ? 0 : sizeof(T); | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this expression re-used from libc++? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is copied from
Also, notice |
||||
|
||||
LIBC_INLINE_VAR static constexpr size_t ALIGNMENT = alignof(T) > MIN_ALIGNMENT | ||||
? alignof(T) | ||||
: MIN_ALIGNMENT; | ||||
|
||||
public: | ||||
using value_type = T; | ||||
|
||||
|
@@ -87,9 +98,10 @@ template <typename T> struct Atomic { | |||
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) { | ||||
T res; | ||||
#if __has_builtin(__scoped_atomic_load) | ||||
__scoped_atomic_load(&val, &res, order(mem_ord), scope(mem_scope)); | ||||
__scoped_atomic_load(addressof(val), addressof(res), order(mem_ord), | ||||
scope(mem_scope)); | ||||
#else | ||||
__atomic_load(&val, &res, order(mem_ord)); | ||||
__atomic_load(addressof(val), addressof(res), order(mem_ord)); | ||||
#endif | ||||
return res; | ||||
} | ||||
|
@@ -104,36 +116,39 @@ template <typename T> struct Atomic { | |||
store(T rhs, MemoryOrder mem_ord = MemoryOrder::SEQ_CST, | ||||
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) { | ||||
#if __has_builtin(__scoped_atomic_store) | ||||
__scoped_atomic_store(&val, &rhs, order(mem_ord), scope(mem_scope)); | ||||
__scoped_atomic_store(addressof(val), addressof(rhs), order(mem_ord), | ||||
scope(mem_scope)); | ||||
#else | ||||
__atomic_store(&val, &rhs, order(mem_ord)); | ||||
__atomic_store(addressof(val), addressof(rhs), order(mem_ord)); | ||||
#endif | ||||
} | ||||
|
||||
// Atomic compare exchange | ||||
LIBC_INLINE 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(&val, &expected, &desired, false, | ||||
order(mem_ord), order(mem_ord)); | ||||
return __atomic_compare_exchange(addressof(val), addressof(expected), | ||||
addressof(desired), false, order(mem_ord), | ||||
order(mem_ord)); | ||||
} | ||||
|
||||
// Atomic compare exchange (separate success and failure memory orders) | ||||
LIBC_INLINE 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(&val, &expected, &desired, false, | ||||
order(success_order), | ||||
order(failure_order)); | ||||
return __atomic_compare_exchange( | ||||
addressof(val), addressof(expected), addressof(desired), false, | ||||
order(success_order), order(failure_order)); | ||||
} | ||||
|
||||
// Atomic compare exchange (weak version) | ||||
LIBC_INLINE 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(&val, &expected, &desired, true, | ||||
order(mem_ord), order(mem_ord)); | ||||
return __atomic_compare_exchange(addressof(val), addressof(expected), | ||||
addressof(desired), true, order(mem_ord), | ||||
order(mem_ord)); | ||||
} | ||||
|
||||
// Atomic compare exchange (weak version with separate success and failure | ||||
|
@@ -142,20 +157,21 @@ template <typename T> struct Atomic { | |||
T &expected, T desired, MemoryOrder success_order, | ||||
MemoryOrder failure_order, | ||||
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) { | ||||
return __atomic_compare_exchange(&val, &expected, &desired, true, | ||||
order(success_order), | ||||
order(failure_order)); | ||||
return __atomic_compare_exchange( | ||||
addressof(val), addressof(expected), addressof(desired), true, | ||||
order(success_order), order(failure_order)); | ||||
} | ||||
|
||||
LIBC_INLINE T | ||||
exchange(T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST, | ||||
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) { | ||||
T ret; | ||||
#if __has_builtin(__scoped_atomic_exchange) | ||||
__scoped_atomic_exchange(&val, &desired, &ret, order(mem_ord), | ||||
scope(mem_scope)); | ||||
__scoped_atomic_exchange(addressof(val), addressof(desired), addressof(ret), | ||||
order(mem_ord), scope(mem_scope)); | ||||
#else | ||||
__atomic_exchange(&val, &desired, &ret, order(mem_ord)); | ||||
__atomic_exchange(addressof(val), addressof(desired), addressof(ret), | ||||
order(mem_ord)); | ||||
#endif | ||||
return ret; | ||||
} | ||||
|
@@ -165,10 +181,10 @@ template <typename T> struct Atomic { | |||
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) { | ||||
static_assert(cpp::is_integral_v<T>, "T must be an integral type."); | ||||
#if __has_builtin(__scoped_atomic_fetch_add) | ||||
return __scoped_atomic_fetch_add(&val, increment, order(mem_ord), | ||||
return __scoped_atomic_fetch_add(addressof(val), increment, order(mem_ord), | ||||
scope(mem_scope)); | ||||
#else | ||||
return __atomic_fetch_add(&val, increment, order(mem_ord)); | ||||
return __atomic_fetch_add(addressof(val), increment, order(mem_ord)); | ||||
#endif | ||||
} | ||||
|
||||
|
@@ -177,10 +193,10 @@ template <typename T> struct Atomic { | |||
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) { | ||||
static_assert(cpp::is_integral_v<T>, "T must be an integral type."); | ||||
#if __has_builtin(__scoped_atomic_fetch_or) | ||||
return __scoped_atomic_fetch_or(&val, mask, order(mem_ord), | ||||
return __scoped_atomic_fetch_or(addressof(val), mask, order(mem_ord), | ||||
scope(mem_scope)); | ||||
#else | ||||
return __atomic_fetch_or(&val, mask, order(mem_ord)); | ||||
return __atomic_fetch_or(addressof(val), mask, order(mem_ord)); | ||||
#endif | ||||
} | ||||
|
||||
|
@@ -189,10 +205,10 @@ template <typename T> struct Atomic { | |||
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) { | ||||
static_assert(cpp::is_integral_v<T>, "T must be an integral type."); | ||||
#if __has_builtin(__scoped_atomic_fetch_and) | ||||
return __scoped_atomic_fetch_and(&val, mask, order(mem_ord), | ||||
return __scoped_atomic_fetch_and(addressof(val), mask, order(mem_ord), | ||||
scope(mem_scope)); | ||||
#else | ||||
return __atomic_fetch_and(&val, mask, order(mem_ord)); | ||||
return __atomic_fetch_and(addressof(val), mask, order(mem_ord)); | ||||
#endif | ||||
} | ||||
|
||||
|
@@ -201,10 +217,10 @@ template <typename T> struct Atomic { | |||
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) { | ||||
static_assert(cpp::is_integral_v<T>, "T must be an integral type."); | ||||
#if __has_builtin(__scoped_atomic_fetch_sub) | ||||
return __scoped_atomic_fetch_sub(&val, decrement, order(mem_ord), | ||||
return __scoped_atomic_fetch_sub(addressof(val), decrement, order(mem_ord), | ||||
scope(mem_scope)); | ||||
#else | ||||
return __atomic_fetch_sub(&val, decrement, order(mem_ord)); | ||||
return __atomic_fetch_sub(addressof(val), decrement, order(mem_ord)); | ||||
#endif | ||||
} | ||||
|
||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
//===-- has_unique_object_representations type_traits ------------*- C++-*-===// | ||
// | ||
// 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 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_HAS_UNIQUE_OBJECT_REPRESENTATIONS_H | ||
#define LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_HAS_UNIQUE_OBJECT_REPRESENTATIONS_H | ||
|
||
#include "src/__support/CPP/type_traits/integral_constant.h" | ||
#include "src/__support/CPP/type_traits/remove_all_extents.h" | ||
#include "src/__support/macros/config.h" | ||
|
||
namespace LIBC_NAMESPACE_DECL { | ||
namespace cpp { | ||
|
||
template <class T> | ||
struct has_unique_object_representations | ||
: public integral_constant<bool, __has_unique_object_representations( | ||
remove_all_extents_t<T>)> {}; | ||
|
||
template <class T> | ||
LIBC_INLINE_VAR constexpr bool has_unique_object_representations_v = | ||
has_unique_object_representations<T>::value; | ||
|
||
} // namespace cpp | ||
} // namespace LIBC_NAMESPACE_DECL | ||
|
||
#endif // LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_HAS_UNIQUE_OBJECT_REPRESENTATIONS_H | ||
SchrodingerZhu marked this conversation as resolved.
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.
Do we need
static
, here and below?Uh oh!
There was an error while loading. Please reload this page.
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.
constexpr
can only be static, otherwise the compiler would complain.The function here can be static or not. As it does not depend on the member field so
static
seems more suitable.