Skip to content

Commit e8b924b

Browse files
committed
Allow any non-const pointer type
1 parent 2a69ca9 commit e8b924b

File tree

3 files changed

+136
-13
lines changed

3 files changed

+136
-13
lines changed

clang/lib/Sema/SemaChecking.cpp

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3926,14 +3926,31 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
39263926
}
39273927
}
39283928

3929-
// Pointer to object of size zero is not allowed.
3930-
if (RequireCompleteType(Ptr->getBeginLoc(), AtomTy,
3931-
diag::err_incomplete_type))
3932-
return ExprError();
3933-
if (Context.getTypeInfoInChars(AtomTy).Width.isZero()) {
3934-
Diag(ExprRange.getBegin(), diag::err_atomic_builtin_must_be_pointer)
3935-
<< Ptr->getType() << 1 << Ptr->getSourceRange();
3936-
return ExprError();
3929+
if (Form != TestAndSet && Form != Clear) {
3930+
// Pointer to object of size zero is not allowed.
3931+
if (RequireCompleteType(Ptr->getBeginLoc(), AtomTy,
3932+
diag::err_incomplete_type))
3933+
return ExprError();
3934+
3935+
if (Context.getTypeInfoInChars(AtomTy).Width.isZero()) {
3936+
Diag(ExprRange.getBegin(), diag::err_atomic_builtin_must_be_pointer)
3937+
<< Ptr->getType() << 1 << Ptr->getSourceRange();
3938+
return ExprError();
3939+
}
3940+
} else {
3941+
// The __atomic_clear and __atomic_test_and_set intrinsics accept any
3942+
// non-const pointer type, including void* and pointers to incomplete
3943+
// structs, but only access the first byte.
3944+
if (AtomTy.isVolatileQualified())
3945+
Ptr = ImpCastExprToType(
3946+
Ptr,
3947+
Context.getPointerType(Context.getVolatileType(Context.CharTy)),
3948+
CK_BitCast)
3949+
.get();
3950+
else
3951+
Ptr = ImpCastExprToType(Ptr, Context.getPointerType(Context.CharTy),
3952+
CK_BitCast)
3953+
.get();
39373954
}
39383955

39393956
// For an arithmetic operation, the implied arithmetic must be well-formed.
@@ -3978,8 +3995,8 @@ ExprResult Sema::BuildAtomicExpr(SourceRange CallRange, SourceRange ExprRange,
39783995
return ExprError();
39793996
}
39803997

3981-
if (!IsC11 && !AtomTy.isTriviallyCopyableType(Context) &&
3982-
!AtomTy->isScalarType()) {
3998+
if (!IsC11 && Form != TestAndSet && Form != Clear &&
3999+
!AtomTy.isTriviallyCopyableType(Context) && !AtomTy->isScalarType()) {
39834000
// For GNU atomics, require a trivially-copyable type. This is not part of
39844001
// the GNU atomics specification but we enforce it for consistency with
39854002
// other atomics which generally all require a trivially-copyable type. This

clang/test/CodeGen/atomic-test-and-set.c

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,16 +235,111 @@ void test_and_set_dynamic(char *ptr, int order) {
235235
// CHECK-SAME: ) #[[ATTR0]] {
236236
// CHECK-NEXT: [[ENTRY:.*:]]
237237
// CHECK-NEXT: [[X:%.*]] = alloca [10 x i32], align 4
238-
// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca i32, align 4
238+
// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca i8, align 1
239239
// CHECK-NEXT: [[ARRAYDECAY:%.*]] = getelementptr inbounds [10 x i32], ptr [[X]], i64 0, i64 0
240240
// CHECK-NEXT: [[TMP0:%.*]] = atomicrmw volatile xchg ptr [[ARRAYDECAY]], i8 1 seq_cst, align 4
241241
// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i8 [[TMP0]], 0
242-
// CHECK-NEXT: store i1 [[TOBOOL]], ptr [[ATOMIC_TEMP]], align 4
243-
// CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[ATOMIC_TEMP]], align 4
242+
// CHECK-NEXT: store i1 [[TOBOOL]], ptr [[ATOMIC_TEMP]], align 1
243+
// CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[ATOMIC_TEMP]], align 1
244244
// CHECK-NEXT: [[LOADEDV:%.*]] = trunc i8 [[TMP1]] to i1
245245
// CHECK-NEXT: ret void
246246
//
247247
void test_and_set_array() {
248248
volatile int x[10];
249249
__atomic_test_and_set(x, memory_order_seq_cst);
250250
}
251+
252+
// These intrinsics accept any pointer type, including void and incomplete
253+
// structs, and always access the first byte regardless of the actual type
254+
// size.
255+
256+
struct incomplete;
257+
258+
// CHECK-LABEL: define dso_local void @clear_int(
259+
// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
260+
// CHECK-NEXT: [[ENTRY:.*:]]
261+
// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8
262+
// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8
263+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8
264+
// CHECK-NEXT: store atomic i8 0, ptr [[TMP0]] monotonic, align 4
265+
// CHECK-NEXT: ret void
266+
//
267+
void clear_int(int *ptr) {
268+
__atomic_clear(ptr, memory_order_relaxed);
269+
}
270+
// CHECK-LABEL: define dso_local void @clear_void(
271+
// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
272+
// CHECK-NEXT: [[ENTRY:.*:]]
273+
// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8
274+
// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8
275+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8
276+
// CHECK-NEXT: store atomic i8 0, ptr [[TMP0]] monotonic, align 1
277+
// CHECK-NEXT: ret void
278+
//
279+
void clear_void(void *ptr) {
280+
__atomic_clear(ptr, memory_order_relaxed);
281+
}
282+
// CHECK-LABEL: define dso_local void @clear_incomplete(
283+
// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
284+
// CHECK-NEXT: [[ENTRY:.*:]]
285+
// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8
286+
// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8
287+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8
288+
// CHECK-NEXT: store atomic i8 0, ptr [[TMP0]] monotonic, align 1
289+
// CHECK-NEXT: ret void
290+
//
291+
void clear_incomplete(struct incomplete *ptr) {
292+
__atomic_clear(ptr, memory_order_relaxed);
293+
}
294+
295+
// CHECK-LABEL: define dso_local void @test_and_set_int(
296+
// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
297+
// CHECK-NEXT: [[ENTRY:.*:]]
298+
// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8
299+
// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca i8, align 1
300+
// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8
301+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8
302+
// CHECK-NEXT: [[TMP1:%.*]] = atomicrmw xchg ptr [[TMP0]], i8 1 monotonic, align 4
303+
// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i8 [[TMP1]], 0
304+
// CHECK-NEXT: store i1 [[TOBOOL]], ptr [[ATOMIC_TEMP]], align 1
305+
// CHECK-NEXT: [[TMP2:%.*]] = load i8, ptr [[ATOMIC_TEMP]], align 1
306+
// CHECK-NEXT: [[LOADEDV:%.*]] = trunc i8 [[TMP2]] to i1
307+
// CHECK-NEXT: ret void
308+
//
309+
void test_and_set_int(int *ptr) {
310+
__atomic_test_and_set(ptr, memory_order_relaxed);
311+
}
312+
// CHECK-LABEL: define dso_local void @test_and_set_void(
313+
// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
314+
// CHECK-NEXT: [[ENTRY:.*:]]
315+
// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8
316+
// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca i8, align 1
317+
// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8
318+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8
319+
// CHECK-NEXT: [[TMP1:%.*]] = atomicrmw xchg ptr [[TMP0]], i8 1 monotonic, align 1
320+
// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i8 [[TMP1]], 0
321+
// CHECK-NEXT: store i1 [[TOBOOL]], ptr [[ATOMIC_TEMP]], align 1
322+
// CHECK-NEXT: [[TMP2:%.*]] = load i8, ptr [[ATOMIC_TEMP]], align 1
323+
// CHECK-NEXT: [[LOADEDV:%.*]] = trunc i8 [[TMP2]] to i1
324+
// CHECK-NEXT: ret void
325+
//
326+
void test_and_set_void(void *ptr) {
327+
__atomic_test_and_set(ptr, memory_order_relaxed);
328+
}
329+
// CHECK-LABEL: define dso_local void @test_and_set_incomplete(
330+
// CHECK-SAME: ptr noundef [[PTR:%.*]]) #[[ATTR0]] {
331+
// CHECK-NEXT: [[ENTRY:.*:]]
332+
// CHECK-NEXT: [[PTR_ADDR:%.*]] = alloca ptr, align 8
333+
// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca i8, align 1
334+
// CHECK-NEXT: store ptr [[PTR]], ptr [[PTR_ADDR]], align 8
335+
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[PTR_ADDR]], align 8
336+
// CHECK-NEXT: [[TMP1:%.*]] = atomicrmw xchg ptr [[TMP0]], i8 1 monotonic, align 1
337+
// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne i8 [[TMP1]], 0
338+
// CHECK-NEXT: store i1 [[TOBOOL]], ptr [[ATOMIC_TEMP]], align 1
339+
// CHECK-NEXT: [[TMP2:%.*]] = load i8, ptr [[ATOMIC_TEMP]], align 1
340+
// CHECK-NEXT: [[LOADEDV:%.*]] = trunc i8 [[TMP2]] to i1
341+
// CHECK-NEXT: ret void
342+
//
343+
void test_and_set_incomplete(struct incomplete *ptr) {
344+
__atomic_test_and_set(ptr, memory_order_relaxed);
345+
}

clang/test/Sema/atomic-ops.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,17 @@ void f(_Atomic(int) *i, const _Atomic(int) *ci,
294294
__atomic_clear(&flag, memory_order_acquire); // expected-warning {{memory order argument to atomic operation is invalid}}
295295
__atomic_clear(&flag, memory_order_acq_rel); // expected-warning {{memory order argument to atomic operation is invalid}}
296296

297+
// These intrinsics accept any non-const pointer type (including
298+
// pointer-to-incomplete), and access the first byte.
299+
__atomic_test_and_set((void*)0x8000, memory_order_seq_cst);
300+
__atomic_test_and_set((char*)0x8000, memory_order_seq_cst);
301+
__atomic_test_and_set((int*)0x8000, memory_order_seq_cst);
302+
__atomic_test_and_set((struct incomplete*)0x8000, memory_order_seq_cst);
303+
__atomic_clear((void*)0x8000, memory_order_seq_cst);
304+
__atomic_clear((char*)0x8000, memory_order_seq_cst);
305+
__atomic_clear((int*)0x8000, memory_order_seq_cst);
306+
__atomic_clear((struct incomplete*)0x8000, memory_order_seq_cst);
307+
297308
__c11_atomic_init(ci, 0); // expected-error {{address argument to atomic operation must be a pointer to non-const _Atomic type ('const _Atomic(int) *' invalid)}}
298309
__c11_atomic_store(ci, 0, memory_order_release); // expected-error {{address argument to atomic operation must be a pointer to non-const _Atomic type ('const _Atomic(int) *' invalid)}}
299310
__c11_atomic_load(ci, memory_order_acquire);

0 commit comments

Comments
 (0)