Skip to content

Commit 7979fb9

Browse files
[libc] fix and explicit atomic difference from c++
- align alignment requirement with atomic_ref in libc++, which is more modern - use `addressof` to avoid problems if future users override their address operator - check `has_unique_object_representations` and rejects padded objects in libc's implementations
1 parent 3c464d2 commit 7979fb9

File tree

3 files changed

+77
-30
lines changed

3 files changed

+77
-30
lines changed

libc/src/__support/CPP/atomic.h

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H
1010
#define LLVM_LIBC_SRC___SUPPORT_CPP_ATOMIC_H
1111

12+
#include "src/__support/CPP/type_traits/has_unique_object_representations.h"
1213
#include "src/__support/macros/attributes.h"
1314
#include "src/__support/macros/config.h"
1415
#include "src/__support/macros/properties/architectures.h"
@@ -47,12 +48,11 @@ template <typename T> struct Atomic {
4748
"constructible, move constructible, copy assignable, "
4849
"and move assignable.");
4950

51+
static_assert(cpp::has_unique_object_representations_v<T>,
52+
"atomic<T> in libc only support types whose values has unique "
53+
"object representations.");
54+
5055
private:
51-
// The value stored should be appropriately aligned so that
52-
// hardware instructions used to perform atomic operations work
53-
// correctly.
54-
static constexpr int ALIGNMENT = sizeof(T) > alignof(T) ? sizeof(T)
55-
: alignof(T);
5656
// type conversion helper to avoid long c++ style casts
5757
LIBC_INLINE static int order(MemoryOrder mem_ord) {
5858
return static_cast<int>(mem_ord);
@@ -62,6 +62,18 @@ template <typename T> struct Atomic {
6262
return static_cast<int>(mem_scope);
6363
}
6464

65+
LIBC_INLINE static T *addressof(T &ref) { return __builtin_addressof(ref); }
66+
67+
// require types that are 1, 2, 4, 8, or 16 bytes in length to be aligned to
68+
// at least their size to be potentially
69+
// used lock-free
70+
LIBC_INLINE_VAR static constexpr size_t MIN_ALIGNMENT =
71+
(sizeof(T) & (sizeof(T) - 1)) || (sizeof(T) > 16) ? 0 : sizeof(T);
72+
73+
LIBC_INLINE_VAR static constexpr size_t ALIGNMENT = alignof(T) > MIN_ALIGNMENT
74+
? alignof(T)
75+
: MIN_ALIGNMENT;
76+
6577
public:
6678
using value_type = T;
6779

@@ -87,9 +99,10 @@ template <typename T> struct Atomic {
8799
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
88100
T res;
89101
#if __has_builtin(__scoped_atomic_load)
90-
__scoped_atomic_load(&val, &res, order(mem_ord), scope(mem_scope));
102+
__scoped_atomic_load(addressof(val), addressof(res), order(mem_ord),
103+
scope(mem_scope));
91104
#else
92-
__atomic_load(&val, &res, order(mem_ord));
105+
__atomic_load(addressof(val), addressof(res), order(mem_ord));
93106
#endif
94107
return res;
95108
}
@@ -104,36 +117,39 @@ template <typename T> struct Atomic {
104117
store(T rhs, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
105118
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
106119
#if __has_builtin(__scoped_atomic_store)
107-
__scoped_atomic_store(&val, &rhs, order(mem_ord), scope(mem_scope));
120+
__scoped_atomic_store(addressof(val), addressof(rhs), order(mem_ord),
121+
scope(mem_scope));
108122
#else
109-
__atomic_store(&val, &rhs, order(mem_ord));
123+
__atomic_store(addressof(val), addressof(rhs), order(mem_ord));
110124
#endif
111125
}
112126

113127
// Atomic compare exchange
114128
LIBC_INLINE bool compare_exchange_strong(
115129
T &expected, T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
116130
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
117-
return __atomic_compare_exchange(&val, &expected, &desired, false,
118-
order(mem_ord), order(mem_ord));
131+
return __atomic_compare_exchange(addressof(val), addressof(expected),
132+
addressof(desired), false, order(mem_ord),
133+
order(mem_ord));
119134
}
120135

121136
// Atomic compare exchange (separate success and failure memory orders)
122137
LIBC_INLINE bool compare_exchange_strong(
123138
T &expected, T desired, MemoryOrder success_order,
124139
MemoryOrder failure_order,
125140
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
126-
return __atomic_compare_exchange(&val, &expected, &desired, false,
127-
order(success_order),
128-
order(failure_order));
141+
return __atomic_compare_exchange(
142+
addressof(val), addressof(expected), addressof(desired), false,
143+
order(success_order), order(failure_order));
129144
}
130145

131146
// Atomic compare exchange (weak version)
132147
LIBC_INLINE bool compare_exchange_weak(
133148
T &expected, T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
134149
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
135-
return __atomic_compare_exchange(&val, &expected, &desired, true,
136-
order(mem_ord), order(mem_ord));
150+
return __atomic_compare_exchange(addressof(val), addressof(expected),
151+
addressof(desired), true, order(mem_ord),
152+
order(mem_ord));
137153
}
138154

139155
// Atomic compare exchange (weak version with separate success and failure
@@ -142,20 +158,21 @@ template <typename T> struct Atomic {
142158
T &expected, T desired, MemoryOrder success_order,
143159
MemoryOrder failure_order,
144160
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
145-
return __atomic_compare_exchange(&val, &expected, &desired, true,
146-
order(success_order),
147-
order(failure_order));
161+
return __atomic_compare_exchange(
162+
addressof(val), addressof(expected), addressof(desired), true,
163+
order(success_order), order(failure_order));
148164
}
149165

150166
LIBC_INLINE T
151167
exchange(T desired, MemoryOrder mem_ord = MemoryOrder::SEQ_CST,
152168
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
153169
T ret;
154170
#if __has_builtin(__scoped_atomic_exchange)
155-
__scoped_atomic_exchange(&val, &desired, &ret, order(mem_ord),
156-
scope(mem_scope));
171+
__scoped_atomic_exchange(addressof(val), addressof(desired), addressof(ret),
172+
order(mem_ord), scope(mem_scope));
157173
#else
158-
__atomic_exchange(&val, &desired, &ret, order(mem_ord));
174+
__atomic_exchange(addressof(val), addressof(desired), addressof(ret),
175+
order(mem_ord));
159176
#endif
160177
return ret;
161178
}
@@ -165,10 +182,10 @@ template <typename T> struct Atomic {
165182
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
166183
static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
167184
#if __has_builtin(__scoped_atomic_fetch_add)
168-
return __scoped_atomic_fetch_add(&val, increment, order(mem_ord),
185+
return __scoped_atomic_fetch_add(addressof(val), increment, order(mem_ord),
169186
scope(mem_scope));
170187
#else
171-
return __atomic_fetch_add(&val, increment, order(mem_ord));
188+
return __atomic_fetch_add(addressof(val), increment, order(mem_ord));
172189
#endif
173190
}
174191

@@ -177,10 +194,10 @@ template <typename T> struct Atomic {
177194
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
178195
static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
179196
#if __has_builtin(__scoped_atomic_fetch_or)
180-
return __scoped_atomic_fetch_or(&val, mask, order(mem_ord),
197+
return __scoped_atomic_fetch_or(addressof(val), mask, order(mem_ord),
181198
scope(mem_scope));
182199
#else
183-
return __atomic_fetch_or(&val, mask, order(mem_ord));
200+
return __atomic_fetch_or(addressof(val), mask, order(mem_ord));
184201
#endif
185202
}
186203

@@ -189,10 +206,10 @@ template <typename T> struct Atomic {
189206
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
190207
static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
191208
#if __has_builtin(__scoped_atomic_fetch_and)
192-
return __scoped_atomic_fetch_and(&val, mask, order(mem_ord),
209+
return __scoped_atomic_fetch_and(addressof(val), mask, order(mem_ord),
193210
scope(mem_scope));
194211
#else
195-
return __atomic_fetch_and(&val, mask, order(mem_ord));
212+
return __atomic_fetch_and(addressof(val), mask, order(mem_ord));
196213
#endif
197214
}
198215

@@ -201,10 +218,10 @@ template <typename T> struct Atomic {
201218
[[maybe_unused]] MemoryScope mem_scope = MemoryScope::DEVICE) {
202219
static_assert(cpp::is_integral_v<T>, "T must be an integral type.");
203220
#if __has_builtin(__scoped_atomic_fetch_sub)
204-
return __scoped_atomic_fetch_sub(&val, decrement, order(mem_ord),
221+
return __scoped_atomic_fetch_sub(addressof(val), decrement, order(mem_ord),
205222
scope(mem_scope));
206223
#else
207-
return __atomic_fetch_sub(&val, decrement, order(mem_ord));
224+
return __atomic_fetch_sub(addressof(val), decrement, order(mem_ord));
208225
#endif
209226
}
210227

libc/src/__support/CPP/type_traits.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "src/__support/CPP/type_traits/decay.h"
1919
#include "src/__support/CPP/type_traits/enable_if.h"
2020
#include "src/__support/CPP/type_traits/false_type.h"
21+
#include "src/__support/CPP/type_traits/has_unique_object_representations.h"
2122
#include "src/__support/CPP/type_traits/integral_constant.h"
2223
#include "src/__support/CPP/type_traits/invoke.h"
2324
#include "src/__support/CPP/type_traits/invoke_result.h"
@@ -65,4 +66,5 @@
6566
#include "src/__support/CPP/type_traits/type_identity.h"
6667
#include "src/__support/CPP/type_traits/void_t.h"
6768

69+
6870
#endif // LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_H
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===-- has_unique_object_representations type_traits ------------*- C++-*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_HAS_UNIQUE_OBJECT_REPRESENTATIONS_H
9+
#define LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_HAS_UNIQUE_OBJECT_REPRESENTATIONS_H
10+
11+
#include "src/__support/CPP/type_traits/integral_constant.h"
12+
#include "src/__support/macros/config.h"
13+
14+
namespace LIBC_NAMESPACE_DECL {
15+
namespace cpp {
16+
17+
template <class T>
18+
struct has_unique_object_representations
19+
: public integral_constant<bool, __has_unique_object_representations(T)> {};
20+
21+
template <class T>
22+
LIBC_INLINE_VAR constexpr bool has_unique_object_representations_v =
23+
has_unique_object_representations<T>::value;
24+
25+
} // namespace cpp
26+
} // namespace LIBC_NAMESPACE_DECL
27+
28+
#endif // LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_HAS_UNIQUE_OBJECT_REPRESENTATIONS_H

0 commit comments

Comments
 (0)