blob: 4030447ecdb3afa3676b4398dcd30310bc574b8d [file] [log] [blame]
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-apple-darwin10 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOCOMPAT
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-apple-darwin10 -fclang-abi-compat=6.0 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-V6COMPAT
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-scei-ps4 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-V6COMPAT
// RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-sie-ps5 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-V6COMPAT
extern int int_source();
extern void int_sink(int x);
namespace test0 {
struct A {
int aField;
int bField;
};
struct B {
int onebit : 2;
int twobit : 6;
int intField;
};
struct __attribute__((packed, aligned(2))) C : A, B {
};
// These accesses should have alignment 4 because they're at offset 0
// in a reference with an assumed alignment of 4.
// CHECK-LABEL: @_ZN5test01aERNS_1BE
void a(B &b) {
// CHECK: [[CALL:%.*]] = call noundef i32 @_Z10int_sourcev()
// CHECK: [[B_P:%.*]] = load ptr, ptr
// CHECK: [[TRUNC:%.*]] = trunc i32 [[CALL]] to i8
// CHECK: [[OLD_VALUE:%.*]] = load i8, ptr [[B_P]], align 4
// CHECK: [[T0:%.*]] = and i8 [[TRUNC]], 3
// CHECK: [[T1:%.*]] = and i8 [[OLD_VALUE]], -4
// CHECK: [[T2:%.*]] = or i8 [[T1]], [[T0]]
// CHECK: store i8 [[T2]], ptr [[B_P]], align 4
b.onebit = int_source();
// CHECK: [[B_P:%.*]] = load ptr, ptr
// CHECK: [[VALUE:%.*]] = load i8, ptr [[B_P]], align 4
// CHECK: [[T0:%.*]] = shl i8 [[VALUE]], 6
// CHECK: [[T1:%.*]] = ashr i8 [[T0]], 6
// CHECK: [[T2:%.*]] = sext i8 [[T1]] to i32
// CHECK: call void @_Z8int_sinki(i32 noundef [[T2]])
int_sink(b.onebit);
}
// These accesses should have alignment 2 because they're at offset 8
// in a reference/pointer with an assumed alignment of 2.
// CHECK-LABEL: @_ZN5test01bERNS_1CE
void b(C &c) {
// CHECK: [[CALL:%.*]] = call noundef i32 @_Z10int_sourcev()
// CHECK: [[C_P:%.*]] = load ptr, ptr
// CHECK: [[FIELD_P:%.*]] = getelementptr inbounds i8, ptr [[C_P]], i64 8
// CHECK: [[TRUNC:%.*]] = trunc i32 [[CALL]] to i8
// CHECK-V6COMPAT: [[OLD_VALUE:%.*]] = load i8, ptr [[FIELD_P]], align 2
// CHECK-NOCOMPAT: [[OLD_VALUE:%.*]] = load i8, ptr [[FIELD_P]], align 4
// CHECK: [[T0:%.*]] = and i8 [[TRUNC]], 3
// CHECK: [[T1:%.*]] = and i8 [[OLD_VALUE]], -4
// CHECK: [[T2:%.*]] = or i8 [[T1]], [[T0]]
// CHECK-V6COMPAT: store i8 [[T2]], ptr [[FIELD_P]], align 2
// CHECK-NOCOMPAT: store i8 [[T2]], ptr [[FIELD_P]], align 4
c.onebit = int_source();
// CHECK: [[C_P:%.*]] = load ptr, ptr
// CHECK: [[FIELD_P:%.*]] = getelementptr inbounds i8, ptr [[C_P]], i64 8
// CHECK-V6COMPAT: [[VALUE:%.*]] = load i8, ptr [[FIELD_P]], align 2
// CHECK-NOCOMPAT: [[VALUE:%.*]] = load i8, ptr [[FIELD_P]], align 4
// CHECK: [[T0:%.*]] = shl i8 [[VALUE]], 6
// CHECK: [[T1:%.*]] = ashr i8 [[T0]], 6
// CHECK: [[T2:%.*]] = sext i8 [[T1]] to i32
// CHECK: call void @_Z8int_sinki(i32 noundef [[T2]])
int_sink(c.onebit);
}
// CHECK-LABEL: @_ZN5test01cEPNS_1CE
void c(C *c) {
// CHECK: [[CALL:%.*]] = call noundef i32 @_Z10int_sourcev()
// CHECK: [[C_P:%.*]] = load ptr, ptr
// CHECK: [[FIELD_P:%.*]] = getelementptr inbounds i8, ptr [[C_P]], i64 8
// CHECK: [[TRUNC:%.*]] = trunc i32 [[CALL]] to i8
// CHECK-V6COMPAT: [[OLD_VALUE:%.*]] = load i8, ptr [[FIELD_P]], align 2
// CHECK-NOCOMPAT: [[OLD_VALUE:%.*]] = load i8, ptr [[FIELD_P]], align 4
// CHECK: [[T0:%.*]] = and i8 [[TRUNC]], 3
// CHECK: [[T1:%.*]] = and i8 [[OLD_VALUE]], -4
// CHECK: [[T2:%.*]] = or i8 [[T1]], [[T0]]
// CHECK-V6COMPAT: store i8 [[T2]], ptr [[FIELD_P]], align 2
// CHECK-NOCOMPAT: store i8 [[T2]], ptr [[FIELD_P]], align 4
c->onebit = int_source();
// CHECK: [[C_P:%.*]] = load ptr, ptr
// CHECK: [[P:%.*]] = getelementptr inbounds i8, ptr [[C_P]], i64 8
// CHECK-V6COMPAT: [[VALUE:%.*]] = load i8, ptr [[P]], align 2
// CHECK-NOCOMPAT: [[VALUE:%.*]] = load i8, ptr [[P]], align 4
// CHECK: [[T0:%.*]] = shl i8 [[VALUE]], 6
// CHECK: [[T1:%.*]] = ashr i8 [[T0]], 6
// CHECK: [[T2:%.*]] = sext i8 [[T1]] to i32
// CHECK: call void @_Z8int_sinki(i32 noundef [[T2]])
int_sink(c->onebit);
}
// These accesses should have alignment 2 because they're at offset 8
// in an alignment-2 variable.
// CHECK-LABEL: @_ZN5test01dEv
void d() {
// CHECK-V6COMPAT: [[C_P:%.*]] = alloca [[C:%.*]], align 2
// CHECK-NOCOMPAT: [[C_P:%.*]] = alloca [[C:%.*]], align 4
C c;
// CHECK: [[CALL:%.*]] = call noundef i32 @_Z10int_sourcev()
// CHECK: [[FIELD_P:%.*]] = getelementptr inbounds i8, ptr [[C_P]], i64 8
// CHECK: [[TRUNC:%.*]] = trunc i32 [[CALL]] to i8
// CHECK-V6COMPAT: [[OLD_VALUE:%.*]] = load i8, ptr [[FIELD_P]], align 2
// CHECK-NOCOMPAT: [[OLD_VALUE:%.*]] = load i8, ptr [[FIELD_P]], align 4
// CHECK: [[T0:%.*]] = and i8 [[TRUNC]], 3
// CHECK: [[T1:%.*]] = and i8 [[OLD_VALUE]], -4
// CHECK: [[T2:%.*]] = or i8 [[T1]], [[T0]]
// CHECK-V6COMPAT: store i8 [[T2]], ptr [[FIELD_P]], align 2
// CHECK-NOCOMPAT: store i8 [[T2]], ptr [[FIELD_P]], align 4
c.onebit = int_source();
// CHECK: [[T1:%.*]] = getelementptr inbounds i8, ptr [[C_P]], i64 8
// CHECK-V6COMPAT: [[VALUE:%.*]] = load i8, ptr [[T1]], align 2
// CHECK-NOCOMPAT: [[VALUE:%.*]] = load i8, ptr [[T1]], align 4
// CHECK: [[T0:%.*]] = shl i8 [[VALUE]], 6
// CHECK: [[T1:%.*]] = ashr i8 [[T0]], 6
// CHECK: [[T2:%.*]] = sext i8 [[T1]] to i32
// CHECK: call void @_Z8int_sinki(i32 noundef [[T2]])
int_sink(c.onebit);
}
// These accesses should have alignment 8 because they're at offset 8
// in an alignment-16 variable.
// CHECK-LABEL: @_ZN5test01eEv
void e() {
// CHECK: [[C_P:%.*]] = alloca [[C:%.*]], align 16
__attribute__((aligned(16))) C c;
// CHECK: [[CALL:%.*]] = call noundef i32 @_Z10int_sourcev()
// CHECK: [[FIELD_P:%.*]] = getelementptr inbounds i8, ptr [[C_P]], i64 8
// CHECK: [[TRUNC:%.*]] = trunc i32 [[CALL]] to i8
// CHECK: [[OLD_VALUE:%.*]] = load i8, ptr [[FIELD_P]], align 8
// CHECK: [[T0:%.*]] = and i8 [[TRUNC]], 3
// CHECK: [[T1:%.*]] = and i8 [[OLD_VALUE]], -4
// CHECK: [[T2:%.*]] = or i8 [[T1]], [[T0]]
// CHECK: store i8 [[T2]], ptr [[FIELD_P]], align 8
c.onebit = int_source();
// CHECK: [[T1:%.*]] = getelementptr inbounds i8, ptr [[C_P]], i64 8
// CHECK: [[VALUE:%.*]] = load i8, ptr [[T1]], align 8
// CHECK: [[T0:%.*]] = shl i8 [[VALUE]], 6
// CHECK: [[T1:%.*]] = ashr i8 [[T0]], 6
// CHECK: [[T2:%.*]] = sext i8 [[T1]] to i32
// CHECK: call void @_Z8int_sinki(i32 noundef [[T2]])
int_sink(c.onebit);
}
}
namespace test1 {
struct Array {
int elts[4];
};
struct A {
__attribute__((aligned(16))) Array aArray;
};
struct B : virtual A {
void *bPointer; // puts bArray at offset 16
Array bArray;
};
struct C : virtual A { // must be viable as primary base
// Non-empty, nv-size not a multiple of 16.
void *cPointer1;
void *cPointer2;
};
// Proof of concept that the non-virtual components of B do not have
// to be 16-byte-aligned.
struct D : C, B {};
// For the following tests, we want to assign into a variable whose
// alignment is high enough that it will absolutely not be the
// constraint on the memcpy alignment.
typedef __attribute__((aligned(64))) Array AlignedArray;
// CHECK-LABEL: @_ZN5test11aERNS_1AE
void a(A &a) {
// CHECK: [[RESULT:%.*]] = alloca [[ARRAY:%.*]], align 64
// CHECK: [[A_P:%.*]] = load ptr, ptr
// CHECK: [[ARRAY_P:%.*]] = getelementptr inbounds [[A:%.*]], ptr [[A_P]], i32 0, i32 0
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 64 [[RESULT]], ptr align 16 [[ARRAY_P]], i64 16, i1 false)
AlignedArray result = a.aArray;
}
// CHECK-LABEL: @_ZN5test11bERNS_1BE
void b(B &b) {
// CHECK: [[RESULT:%.*]] = alloca [[ARRAY]], align 64
// CHECK: [[B_P:%.*]] = load ptr, ptr
// CHECK: [[VPTR:%.*]] = load ptr, ptr [[B_P]], align 8
// CHECK: [[T0:%.*]] = getelementptr i8, ptr [[VPTR]], i64 -24
// CHECK: [[OFFSET:%.*]] = load i64, ptr [[T0]], align 8
// CHECK: [[T1:%.*]] = getelementptr inbounds i8, ptr [[B_P]], i64 [[OFFSET]]
// CHECK: [[ARRAY_P:%.*]] = getelementptr inbounds [[A]], ptr [[T1]], i32 0, i32 0
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 64 [[RESULT]], ptr align 16 [[ARRAY_P]], i64 16, i1 false)
AlignedArray result = b.aArray;
}
// CHECK-LABEL: @_ZN5test11cERNS_1BE
void c(B &b) {
// CHECK: [[RESULT:%.*]] = alloca [[ARRAY]], align 64
// CHECK: [[B_P:%.*]] = load ptr, ptr
// CHECK: [[ARRAY_P:%.*]] = getelementptr inbounds [[B:%.*]], ptr [[B_P]], i32 0, i32 2
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 64 [[RESULT]], ptr align 8 [[ARRAY_P]], i64 16, i1 false)
AlignedArray result = b.bArray;
}
// CHECK-LABEL: @_ZN5test11dEPNS_1BE
void d(B *b) {
// CHECK: [[RESULT:%.*]] = alloca [[ARRAY]], align 64
// CHECK: [[B_P:%.*]] = load ptr, ptr
// CHECK: [[ARRAY_P:%.*]] = getelementptr inbounds [[B:%.*]], ptr [[B_P]], i32 0, i32 2
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 64 [[RESULT]], ptr align 8 [[ARRAY_P]], i64 16, i1 false)
AlignedArray result = b->bArray;
}
// CHECK-LABEL: @_ZN5test11eEv
void e() {
// CHECK: [[B_P:%.*]] = alloca [[B:%.*]], align 16
// CHECK: [[RESULT:%.*]] = alloca [[ARRAY]], align 64
// CHECK: [[ARRAY_P:%.*]] = getelementptr inbounds [[B:%.*]], ptr [[B_P]], i32 0, i32 2
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 64 [[RESULT]], ptr align 16 [[ARRAY_P]], i64 16, i1 false)
B b;
AlignedArray result = b.bArray;
}
// CHECK-LABEL: @_ZN5test11fEv
void f() {
// TODO: we should devirtualize this derived-to-base conversion.
// CHECK: [[D_P:%.*]] = alloca [[D:%.*]], align 16
// CHECK: [[RESULT:%.*]] = alloca [[ARRAY]], align 64
// CHECK: [[VPTR:%.*]] = load ptr, ptr [[D_P]], align 16
// CHECK: [[T0:%.*]] = getelementptr i8, ptr [[VPTR]], i64 -24
// CHECK: [[OFFSET:%.*]] = load i64, ptr [[T0]], align 8
// CHECK: [[T1:%.*]] = getelementptr inbounds i8, ptr [[D_P]], i64 [[OFFSET]]
// CHECK: [[ARRAY_P:%.*]] = getelementptr inbounds [[A]], ptr [[T1]], i32 0, i32 0
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 64 [[RESULT]], ptr align 16 [[ARRAY_P]], i64 16, i1 false)
D d;
AlignedArray result = d.aArray;
}
// CHECK-LABEL: @_ZN5test11gEv
void g() {
// CHECK: [[D_P:%.*]] = alloca [[D]], align 16
// CHECK: [[RESULT:%.*]] = alloca [[ARRAY]], align 64
// CHECK: [[T1:%.*]] = getelementptr inbounds i8, ptr [[D_P]], i64 24
// CHECK: [[ARRAY_P:%.*]] = getelementptr inbounds [[B:%.*]], ptr [[T1]], i32 0, i32 2
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 64 [[RESULT]], ptr align 8 [[ARRAY_P]], i64 16, i1 false)
D d;
AlignedArray result = d.bArray;
}
// CHECK-LABEL: @_ZN5test11hEPA_NS_1BE
void h(B (*b)[]) {
// CHECK: [[RESULT:%.*]] = alloca [[ARRAY]], align 64
// CHECK: [[B_P:%.*]] = load ptr, ptr
// CHECK: [[ELEMENT_P:%.*]] = getelementptr inbounds [0 x [[B]]], ptr [[B_P]], i64 0
// CHECK: [[ARRAY_P:%.*]] = getelementptr inbounds [[B]], ptr [[ELEMENT_P]], i32 0, i32 2
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 64 [[RESULT]], ptr align 16 [[ARRAY_P]], i64 16, i1 false)
AlignedArray result = (*b)->bArray;
}
}
// CHECK-LABEL: @_Z22incomplete_array_derefPA_i
// CHECK: load i32, ptr {{%.*}}, align 4
int incomplete_array_deref(int (*p)[]) { return (*p)[2]; }