// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fstrict-vtable-pointers -o - %s \
// RUN: | FileCheck --check-prefixes=CHECK,CHECK-STRICT %s
// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s \
// RUN: | FileCheck --check-prefixes=CHECK,CHECK-NONSTRICT %s
//===----------------------------------------------------------------------===//
// Positive Cases
//===----------------------------------------------------------------------===//
struct TestVirtualFn {
virtual void foo() {}
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_fn
extern "C" void test_builtin_launder_virtual_fn(TestVirtualFn *p) {
// CHECK: store ptr %p, ptr %p.addr
// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr %p.addr
// CHECK-NONSTRICT-NEXT: store ptr [[TMP0]], ptr %d
// CHECK-STRICT-NEXT: [[TMP2:%.*]] = call ptr @llvm.launder.invariant.group.p0(ptr [[TMP0]])
// CHECK-STRICT-NEXT: store ptr [[TMP2]], ptr %d
// CHECK-NEXT: ret void
TestVirtualFn *d = __builtin_launder(p);
}
struct TestPolyBase : TestVirtualFn {
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_poly_base
extern "C" void test_builtin_launder_poly_base(TestPolyBase *p) {
// CHECK-STRICT-NOT: ret void
// CHECK-STRICT: @llvm.launder.invariant.group
// CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
// CHECK: ret void
TestPolyBase *d = __builtin_launder(p);
}
struct TestBase {};
struct TestVirtualBase : virtual TestBase {};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_base
extern "C" void test_builtin_launder_virtual_base(TestVirtualBase *p) {
// CHECK-STRICT-NOT: ret void
// CHECK-STRICT: @llvm.launder.invariant.group
// CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
// CHECK: ret void
TestVirtualBase *d = __builtin_launder(p);
}
//===----------------------------------------------------------------------===//
// Negative Cases
//===----------------------------------------------------------------------===//
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_ommitted_one
extern "C" void test_builtin_launder_ommitted_one(int *p) {
// CHECK: entry
// CHECK-NEXT: %p.addr = alloca ptr
// CHECK-NEXT: %d = alloca ptr
// CHECK-NEXT: store ptr %p, ptr %p.addr, align 8
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr %p.addr
// CHECK-NEXT: store ptr [[TMP]], ptr %d
// CHECK-NEXT: ret void
int *d = __builtin_launder(p);
}
struct TestNoInvariant {
int x;
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_ommitted_two
extern "C" void test_builtin_launder_ommitted_two(TestNoInvariant *p) {
// CHECK: entry
// CHECK-NOT: llvm.launder.invariant.group
// CHECK-NEXT: %p.addr = alloca ptr, align 8
// CHECK-NEXT: %d = alloca ptr
// CHECK-NEXT: store ptr %p, ptr %p.addr
// CHECK-NEXT: [[TMP:%.*]] = load ptr, ptr %p.addr
// CHECK-NEXT: store ptr [[TMP]], ptr %d
// CHECK-NEXT: ret void
TestNoInvariant *d = __builtin_launder(p);
}
struct TestVirtualMember {
TestVirtualFn member;
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_member
extern "C" void test_builtin_launder_virtual_member(TestVirtualMember *p) {
// CHECK: entry
// CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
// CHECK-STRICT: @llvm.launder.invariant.group
// CHECK: ret void
TestVirtualMember *d = __builtin_launder(p);
}
struct TestVirtualMemberDepth2 {
TestVirtualMember member;
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_member_depth_2
extern "C" void test_builtin_launder_virtual_member_depth_2(TestVirtualMemberDepth2 *p) {
// CHECK: entry
// CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
// CHECK-STRICT: @llvm.launder.invariant.group
// CHECK: ret void
TestVirtualMemberDepth2 *d = __builtin_launder(p);
}
struct TestVirtualReferenceMember {
TestVirtualFn &member;
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_reference_member
extern "C" void test_builtin_launder_virtual_reference_member(TestVirtualReferenceMember *p) {
// CHECK: entry
// CHECK-NOT: @llvm.launder.invariant.group
// CHECK: ret void
TestVirtualReferenceMember *d = __builtin_launder(p);
}
struct TestRecursiveMember {
TestRecursiveMember() : member(*this) {}
TestRecursiveMember &member;
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_recursive_member
extern "C" void test_builtin_launder_recursive_member(TestRecursiveMember *p) {
// CHECK: entry
// CHECK-NOT: @llvm.launder.invariant.group
// CHECK: ret void
TestRecursiveMember *d = __builtin_launder(p);
}
struct TestVirtualRecursiveMember {
TestVirtualRecursiveMember() : member(*this) {}
TestVirtualRecursiveMember &member;
virtual void foo();
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_recursive_member
extern "C" void test_builtin_launder_virtual_recursive_member(TestVirtualRecursiveMember *p) {
// CHECK: entry
// CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
// CHECK-STRICT: @llvm.launder.invariant.group
// CHECK: ret void
TestVirtualRecursiveMember *d = __builtin_launder(p);
}
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_array(
extern "C" void test_builtin_launder_array(TestVirtualFn (&Arr)[5]) {
// CHECK: entry
// CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
// CHECK-STRICT: @llvm.launder.invariant.group
// CHECK: ret void
TestVirtualFn *d = __builtin_launder(Arr);
}
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_array_nested(
extern "C" void test_builtin_launder_array_nested(TestVirtualFn (&Arr)[5][2]) {
// CHECK: entry
// CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
// CHECK-STRICT: @llvm.launder.invariant.group
// CHECK: ret void
using RetTy = TestVirtualFn(*)[2];
RetTy d = __builtin_launder(Arr);
}
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_array_no_invariant(
extern "C" void test_builtin_launder_array_no_invariant(TestNoInvariant (&Arr)[5]) {
// CHECK: entry
// CHECK-NOT: @llvm.launder.invariant.group
// CHECK: ret void
TestNoInvariant *d = __builtin_launder(Arr);
}
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_array_nested_no_invariant(
extern "C" void test_builtin_launder_array_nested_no_invariant(TestNoInvariant (&Arr)[5][2]) {
// CHECK: entry
// CHECK-NOT: @llvm.launder.invariant.group
// CHECK: ret void
using RetTy = TestNoInvariant(*)[2];
RetTy d = __builtin_launder(Arr);
}
template <class Member>
struct WithMember {
Member mem;
};
template struct WithMember<TestVirtualFn[5]>;
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array(
extern "C" void test_builtin_launder_member_array(WithMember<TestVirtualFn[5]> *p) {
// CHECK: entry
// CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
// CHECK-STRICT: @llvm.launder.invariant.group
// CHECK: ret void
auto *d = __builtin_launder(p);
}
template struct WithMember<TestVirtualFn[5][2]>;
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array_nested(
extern "C" void test_builtin_launder_member_array_nested(WithMember<TestVirtualFn[5][2]> *p) {
// CHECK: entry
// CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
// CHECK-STRICT: @llvm.launder.invariant.group
// CHECK: ret void
auto *d = __builtin_launder(p);
}
template struct WithMember<TestNoInvariant[5]>;
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array_no_invariant(
extern "C" void test_builtin_launder_member_array_no_invariant(WithMember<TestNoInvariant[5]> *p) {
// CHECK: entry
// CHECK-NOT: @llvm.launder.invariant.group
// CHECK: ret void
auto *d = __builtin_launder(p);
}
template struct WithMember<TestNoInvariant[5][2]>;
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array_nested_no_invariant(
extern "C" void test_builtin_launder_member_array_nested_no_invariant(WithMember<TestNoInvariant[5][2]> *p) {
// CHECK: entry
// CHECK-NOT: @llvm.launder.invariant.group
// CHECK: ret void
auto *d = __builtin_launder(p);
}
template <class T>
struct WithBase : T {};
template struct WithBase<TestNoInvariant>;
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_base_no_invariant(
extern "C" void test_builtin_launder_base_no_invariant(WithBase<TestNoInvariant> *p) {
// CHECK: entry
// CHECK-NOT: @llvm.launder.invariant.group
// CHECK: ret void
auto *d = __builtin_launder(p);
}
template struct WithBase<TestVirtualFn>;
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_base(
extern "C" void test_builtin_launder_base(WithBase<TestVirtualFn> *p) {
// CHECK: entry
// CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
// CHECK-STRICT: @llvm.launder.invariant.group
// CHECK: ret void
auto *d = __builtin_launder(p);
}
/// The test cases in this namespace technically need to be laundered according
/// to the language in the standard (ie they have const or reference subobjects)
/// but LLVM doesn't currently optimize on these cases -- so Clang emits
/// __builtin_launder as a nop.
///
/// NOTE: Adding optimizations for these cases later is an LTO ABI break. That's
/// probably OK for now -- but is something to keep in mind.
namespace pessimizing_cases {
struct TestConstMember {
const int x;
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_const_member
extern "C" void test_builtin_launder_const_member(TestConstMember *p) {
// CHECK: entry
// CHECK-NOT: @llvm.launder.invariant.group
// CHECK: ret void
TestConstMember *d = __builtin_launder(p);
}
struct TestConstSubobject {
TestConstMember x;
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_const_subobject
extern "C" void test_builtin_launder_const_subobject(TestConstSubobject *p) {
// CHECK: entry
// CHECK-NOT: @llvm.launder.invariant.group
// CHECK: ret void
TestConstSubobject *d = __builtin_launder(p);
}
struct TestConstObject {
const struct TestConstMember x;
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_const_object
extern "C" void test_builtin_launder_const_object(TestConstObject *p) {
// CHECK: entry
// CHECK-NOT: @llvm.launder.invariant.group
// CHECK: ret void
TestConstObject *d = __builtin_launder(p);
}
struct TestReferenceMember {
int &x;
};
// CHECK-LABEL: define{{.*}} void @test_builtin_launder_reference_member
extern "C" void test_builtin_launder_reference_member(TestReferenceMember *p) {
// CHECK: entry
// CHECK-NOT: @llvm.launder.invariant.group
// CHECK: ret void
TestReferenceMember *d = __builtin_launder(p);
}
} // namespace pessimizing_cases