fixes #30023 The issue is that for compare exchange builtin, if the type's size is not power of 2, it creates a temporary of size power of 2, then emit the compare exchange operation. And later, the results of the compare exchange operation has two components: 1. a boolean whether or not the exchange happens. 2. the old value we are supposed to write the old value into user's "expected" value. However, in case the type is not power of 2, what we actually wrote to is the temporary that was created. The fix is to pass the "expected" address all the way down so it can wrote to the correct address
131 lines
8.5 KiB
C++
131 lines
8.5 KiB
C++
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5
|
|
// RUN: %clang_cc1 -std=c++20 -triple=x86_64-linux-gnu -emit-llvm -o - %s | FileCheck %s
|
|
|
|
|
|
template <unsigned Size>
|
|
struct S {
|
|
char data[Size];
|
|
};
|
|
|
|
// CHECK-LABEL: define dso_local noundef zeroext i1 @_Z21test_compare_exchangePU7_Atomic1SILj3EEPS0_S0_(
|
|
// CHECK-SAME: ptr noundef [[A:%.*]], ptr noundef [[EXPECTED:%.*]], i24 [[DESIRED_COERCE:%.*]]) #[[ATTR0:[0-9]+]] {
|
|
// CHECK-NEXT: [[ENTRY:.*:]]
|
|
// CHECK-NEXT: [[DESIRED:%.*]] = alloca [[STRUCT_S:%.*]], align 1
|
|
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: [[EXPECTED_ADDR:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: [[DOTATOMICTMP:%.*]] = alloca [[STRUCT_S]], align 1
|
|
// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca { [[STRUCT_S]], [1 x i8] }, align 4
|
|
// CHECK-NEXT: [[ATOMIC_TEMP1:%.*]] = alloca { [[STRUCT_S]], [1 x i8] }, align 4
|
|
// CHECK-NEXT: [[CMPXCHG_BOOL:%.*]] = alloca i8, align 1
|
|
// CHECK-NEXT: [[OLD_TMP:%.*]] = alloca i32, align 4
|
|
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_S]], ptr [[DESIRED]], i32 0, i32 0
|
|
// CHECK-NEXT: store i24 [[DESIRED_COERCE]], ptr [[COERCE_DIVE]], align 1
|
|
// CHECK-NEXT: store ptr [[A]], ptr [[A_ADDR]], align 8
|
|
// CHECK-NEXT: store ptr [[EXPECTED]], ptr [[EXPECTED_ADDR]], align 8
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[EXPECTED_ADDR]], align 8
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DOTATOMICTMP]], ptr align 1 [[DESIRED]], i64 3, i1 false)
|
|
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ATOMIC_TEMP]], i8 0, i64 4, i1 false)
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ATOMIC_TEMP]], ptr align 1 [[TMP1]], i64 3, i1 false)
|
|
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 4 [[ATOMIC_TEMP1]], i8 0, i64 4, i1 false)
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[ATOMIC_TEMP1]], ptr align 1 [[DOTATOMICTMP]], i64 3, i1 false)
|
|
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ATOMIC_TEMP]], align 4
|
|
// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ATOMIC_TEMP1]], align 4
|
|
// CHECK-NEXT: [[TMP4:%.*]] = cmpxchg ptr [[TMP0]], i32 [[TMP2]], i32 [[TMP3]] monotonic monotonic, align 4
|
|
// CHECK-NEXT: [[TMP5:%.*]] = extractvalue { i32, i1 } [[TMP4]], 0
|
|
// CHECK-NEXT: [[TMP6:%.*]] = extractvalue { i32, i1 } [[TMP4]], 1
|
|
// CHECK-NEXT: br i1 [[TMP6]], label %[[CMPXCHG_CONTINUE:.*]], label %[[CMPXCHG_STORE_EXPECTED:.*]]
|
|
// CHECK: [[CMPXCHG_STORE_EXPECTED]]:
|
|
// CHECK-NEXT: store i32 [[TMP5]], ptr [[OLD_TMP]], align 4
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[TMP1]], ptr align 4 [[OLD_TMP]], i64 3, i1 false)
|
|
// CHECK-NEXT: br label %[[CMPXCHG_CONTINUE]]
|
|
// CHECK: [[CMPXCHG_CONTINUE]]:
|
|
// CHECK-NEXT: [[STOREDV:%.*]] = zext i1 [[TMP6]] to i8
|
|
// CHECK-NEXT: store i8 [[STOREDV]], ptr [[CMPXCHG_BOOL]], align 1
|
|
// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[CMPXCHG_BOOL]], align 1
|
|
// CHECK-NEXT: [[LOADEDV:%.*]] = trunc i8 [[TMP7]] to i1
|
|
// CHECK-NEXT: ret i1 [[LOADEDV]]
|
|
//
|
|
bool test_compare_exchange(_Atomic(S<3>)* a, S<3>* expected, S<3> desired) {
|
|
return __c11_atomic_compare_exchange_strong(a, expected, desired, 0, 0);
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: define dso_local noundef zeroext i1 @_Z21test_compare_exchangePU7_Atomic1SILj4EEPS0_S0_(
|
|
// CHECK-SAME: ptr noundef [[A:%.*]], ptr noundef [[EXPECTED:%.*]], i32 [[DESIRED_COERCE:%.*]]) #[[ATTR0]] {
|
|
// CHECK-NEXT: [[ENTRY:.*:]]
|
|
// CHECK-NEXT: [[DESIRED:%.*]] = alloca [[STRUCT_S_0:%.*]], align 1
|
|
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: [[EXPECTED_ADDR:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: [[DOTATOMICTMP:%.*]] = alloca [[STRUCT_S_0]], align 1
|
|
// CHECK-NEXT: [[CMPXCHG_BOOL:%.*]] = alloca i8, align 1
|
|
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_S_0]], ptr [[DESIRED]], i32 0, i32 0
|
|
// CHECK-NEXT: store i32 [[DESIRED_COERCE]], ptr [[COERCE_DIVE]], align 1
|
|
// CHECK-NEXT: store ptr [[A]], ptr [[A_ADDR]], align 8
|
|
// CHECK-NEXT: store ptr [[EXPECTED]], ptr [[EXPECTED_ADDR]], align 8
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[EXPECTED_ADDR]], align 8
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DOTATOMICTMP]], ptr align 1 [[DESIRED]], i64 4, i1 false)
|
|
// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[TMP1]], align 1
|
|
// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[DOTATOMICTMP]], align 1
|
|
// CHECK-NEXT: [[TMP4:%.*]] = cmpxchg ptr [[TMP0]], i32 [[TMP2]], i32 [[TMP3]] monotonic monotonic, align 4
|
|
// CHECK-NEXT: [[TMP5:%.*]] = extractvalue { i32, i1 } [[TMP4]], 0
|
|
// CHECK-NEXT: [[TMP6:%.*]] = extractvalue { i32, i1 } [[TMP4]], 1
|
|
// CHECK-NEXT: br i1 [[TMP6]], label %[[CMPXCHG_CONTINUE:.*]], label %[[CMPXCHG_STORE_EXPECTED:.*]]
|
|
// CHECK: [[CMPXCHG_STORE_EXPECTED]]:
|
|
// CHECK-NEXT: store i32 [[TMP5]], ptr [[TMP1]], align 1
|
|
// CHECK-NEXT: br label %[[CMPXCHG_CONTINUE]]
|
|
// CHECK: [[CMPXCHG_CONTINUE]]:
|
|
// CHECK-NEXT: [[STOREDV:%.*]] = zext i1 [[TMP6]] to i8
|
|
// CHECK-NEXT: store i8 [[STOREDV]], ptr [[CMPXCHG_BOOL]], align 1
|
|
// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[CMPXCHG_BOOL]], align 1
|
|
// CHECK-NEXT: [[LOADEDV:%.*]] = trunc i8 [[TMP7]] to i1
|
|
// CHECK-NEXT: ret i1 [[LOADEDV]]
|
|
//
|
|
bool test_compare_exchange(_Atomic(S<4>)* a, S<4>* expected, S<4> desired) {
|
|
return __c11_atomic_compare_exchange_strong(a, expected, desired, 0, 0);
|
|
}
|
|
|
|
// CHECK-LABEL: define dso_local noundef zeroext i1 @_Z21test_compare_exchangePU7_Atomic1SILj6EEPS0_S0_(
|
|
// CHECK-SAME: ptr noundef [[A:%.*]], ptr noundef [[EXPECTED:%.*]], i48 [[DESIRED_COERCE:%.*]]) #[[ATTR0]] {
|
|
// CHECK-NEXT: [[ENTRY:.*:]]
|
|
// CHECK-NEXT: [[DESIRED:%.*]] = alloca [[STRUCT_S_1:%.*]], align 1
|
|
// CHECK-NEXT: [[A_ADDR:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: [[EXPECTED_ADDR:%.*]] = alloca ptr, align 8
|
|
// CHECK-NEXT: [[DOTATOMICTMP:%.*]] = alloca [[STRUCT_S_1]], align 1
|
|
// CHECK-NEXT: [[ATOMIC_TEMP:%.*]] = alloca { [[STRUCT_S_1]], [2 x i8] }, align 8
|
|
// CHECK-NEXT: [[ATOMIC_TEMP1:%.*]] = alloca { [[STRUCT_S_1]], [2 x i8] }, align 8
|
|
// CHECK-NEXT: [[CMPXCHG_BOOL:%.*]] = alloca i8, align 1
|
|
// CHECK-NEXT: [[OLD_TMP:%.*]] = alloca i64, align 8
|
|
// CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_S_1]], ptr [[DESIRED]], i32 0, i32 0
|
|
// CHECK-NEXT: store i48 [[DESIRED_COERCE]], ptr [[COERCE_DIVE]], align 1
|
|
// CHECK-NEXT: store ptr [[A]], ptr [[A_ADDR]], align 8
|
|
// CHECK-NEXT: store ptr [[EXPECTED]], ptr [[EXPECTED_ADDR]], align 8
|
|
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[A_ADDR]], align 8
|
|
// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[EXPECTED_ADDR]], align 8
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[DOTATOMICTMP]], ptr align 1 [[DESIRED]], i64 6, i1 false)
|
|
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[ATOMIC_TEMP]], i8 0, i64 8, i1 false)
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[ATOMIC_TEMP]], ptr align 1 [[TMP1]], i64 6, i1 false)
|
|
// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 8 [[ATOMIC_TEMP1]], i8 0, i64 8, i1 false)
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[ATOMIC_TEMP1]], ptr align 1 [[DOTATOMICTMP]], i64 6, i1 false)
|
|
// CHECK-NEXT: [[TMP2:%.*]] = load i64, ptr [[ATOMIC_TEMP]], align 8
|
|
// CHECK-NEXT: [[TMP3:%.*]] = load i64, ptr [[ATOMIC_TEMP1]], align 8
|
|
// CHECK-NEXT: [[TMP4:%.*]] = cmpxchg ptr [[TMP0]], i64 [[TMP2]], i64 [[TMP3]] monotonic monotonic, align 8
|
|
// CHECK-NEXT: [[TMP5:%.*]] = extractvalue { i64, i1 } [[TMP4]], 0
|
|
// CHECK-NEXT: [[TMP6:%.*]] = extractvalue { i64, i1 } [[TMP4]], 1
|
|
// CHECK-NEXT: br i1 [[TMP6]], label %[[CMPXCHG_CONTINUE:.*]], label %[[CMPXCHG_STORE_EXPECTED:.*]]
|
|
// CHECK: [[CMPXCHG_STORE_EXPECTED]]:
|
|
// CHECK-NEXT: store i64 [[TMP5]], ptr [[OLD_TMP]], align 8
|
|
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 1 [[TMP1]], ptr align 8 [[OLD_TMP]], i64 6, i1 false)
|
|
// CHECK-NEXT: br label %[[CMPXCHG_CONTINUE]]
|
|
// CHECK: [[CMPXCHG_CONTINUE]]:
|
|
// CHECK-NEXT: [[STOREDV:%.*]] = zext i1 [[TMP6]] to i8
|
|
// CHECK-NEXT: store i8 [[STOREDV]], ptr [[CMPXCHG_BOOL]], align 1
|
|
// CHECK-NEXT: [[TMP7:%.*]] = load i8, ptr [[CMPXCHG_BOOL]], align 1
|
|
// CHECK-NEXT: [[LOADEDV:%.*]] = trunc i8 [[TMP7]] to i1
|
|
// CHECK-NEXT: ret i1 [[LOADEDV]]
|
|
//
|
|
bool test_compare_exchange(_Atomic(S<6>)* a, S<6>* expected, S<6> desired) {
|
|
return __c11_atomic_compare_exchange_strong(a, expected, desired, 0, 0);
|
|
}
|