Clang Project

clang_source_code/test/CodeGenCXX/builtin-launder.cpp
1// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -fstrict-vtable-pointers -o - %s \
2// RUN: | FileCheck --check-prefixes=CHECK,CHECK-STRICT %s
3// RUN: %clang_cc1 -triple=x86_64-linux-gnu -emit-llvm -o - %s \
4// RUN: | FileCheck --check-prefixes=CHECK,CHECK-NONSTRICT %s
5
6//===----------------------------------------------------------------------===//
7//                            Positive Cases
8//===----------------------------------------------------------------------===//
9
10struct TestVirtualFn {
11  virtual void foo() {}
12};
13
14// CHECK-LABEL: define void @test_builtin_launder_virtual_fn
15extern "C" void test_builtin_launder_virtual_fn(TestVirtualFn *p) {
16  // CHECK: store [[TYPE:%[^ ]+]] %p, [[TYPE]]* %p.addr
17  // CHECK-NEXT: [[TMP0:%.*]] = load [[TYPE]], [[TYPE]]* %p.addr
18
19  // CHECK-NONSTRICT-NEXT: store [[TYPE]] [[TMP0]], [[TYPE]]* %d
20
21  // CHECK-STRICT-NEXT: [[TMP1:%.*]] = bitcast [[TYPE]] [[TMP0]] to i8*
22  // CHECK-STRICT-NEXT: [[TMP2:%.*]] = call i8* @llvm.launder.invariant.group.p0i8(i8* [[TMP1]])
23  // CHECK-STRICT-NEXT: [[TMP3:%.*]] = bitcast i8* [[TMP2]] to [[TYPE]]
24  // CHECK-STRICT-NEXT: store [[TYPE]] [[TMP3]], [[TYPE]]* %d
25
26  // CHECK-NEXT: ret void
27  TestVirtualFn *d = __builtin_launder(p);
28}
29
30struct TestPolyBase : TestVirtualFn {
31};
32
33// CHECK-LABEL: define void @test_builtin_launder_poly_base
34extern "C" void test_builtin_launder_poly_base(TestPolyBase *p) {
35  // CHECK-STRICT-NOT: ret void
36  // CHECK-STRICT: @llvm.launder.invariant.group
37
38  // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
39
40  // CHECK: ret void
41  TestPolyBase *d = __builtin_launder(p);
42}
43
44struct TestBase {};
45struct TestVirtualBase : virtual TestBase {};
46
47// CHECK-LABEL: define void @test_builtin_launder_virtual_base
48extern "C" void test_builtin_launder_virtual_base(TestVirtualBase *p) {
49  // CHECK-STRICT-NOT: ret void
50  // CHECK-STRICT: @llvm.launder.invariant.group
51
52  // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
53
54  // CHECK: ret void
55  TestVirtualBase *d = __builtin_launder(p);
56}
57
58//===----------------------------------------------------------------------===//
59//                            Negative Cases
60//===----------------------------------------------------------------------===//
61
62// CHECK-LABEL: define void @test_builtin_launder_ommitted_one
63extern "C" void test_builtin_launder_ommitted_one(int *p) {
64  // CHECK: entry
65  // CHECK-NEXT: %p.addr = alloca i32*
66  // CHECK-NEXT: %d = alloca i32*
67  // CHECK-NEXT: store i32* %p, i32** %p.addr, align 8
68  // CHECK-NEXT: [[TMP:%.*]] = load i32*, i32** %p.addr
69  // CHECK-NEXT: store i32* [[TMP]], i32** %d
70  // CHECK-NEXT: ret void
71  int *d = __builtin_launder(p);
72}
73
74struct TestNoInvariant {
75  int x;
76};
77
78// CHECK-LABEL: define void @test_builtin_launder_ommitted_two
79extern "C" void test_builtin_launder_ommitted_two(TestNoInvariant *p) {
80  // CHECK: entry
81  // CHECK-NOT: llvm.launder.invariant.group
82  // CHECK-NEXT: %p.addr = alloca [[TYPE:%.*]], align 8
83  // CHECK-NEXT: %d = alloca [[TYPE]]
84  // CHECK-NEXT: store [[TYPE]] %p, [[TYPE]]* %p.addr
85  // CHECK-NEXT: [[TMP:%.*]] = load [[TYPE]], [[TYPE]]* %p.addr
86  // CHECK-NEXT: store [[TYPE]] [[TMP]], [[TYPE]]* %d
87  // CHECK-NEXT: ret void
88  TestNoInvariant *d = __builtin_launder(p);
89}
90
91struct TestVirtualMember {
92  TestVirtualFn member;
93};
94
95// CHECK-LABEL: define void @test_builtin_launder_virtual_member
96extern "C" void test_builtin_launder_virtual_member(TestVirtualMember *p) {
97  // CHECK: entry
98  // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
99  // CHECK-STRICT: @llvm.launder.invariant.group
100  // CHECK: ret void
101  TestVirtualMember *d = __builtin_launder(p);
102}
103
104struct TestVirtualMemberDepth2 {
105  TestVirtualMember member;
106};
107
108// CHECK-LABEL: define void @test_builtin_launder_virtual_member_depth_2
109extern "C" void test_builtin_launder_virtual_member_depth_2(TestVirtualMemberDepth2 *p) {
110  // CHECK: entry
111  // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
112  // CHECK-STRICT: @llvm.launder.invariant.group
113  // CHECK: ret void
114  TestVirtualMemberDepth2 *d = __builtin_launder(p);
115}
116
117struct TestVirtualReferenceMember {
118  TestVirtualFn &member;
119};
120
121// CHECK-LABEL: define void @test_builtin_launder_virtual_reference_member
122extern "C" void test_builtin_launder_virtual_reference_member(TestVirtualReferenceMember *p) {
123  // CHECK: entry
124  // CHECK-NOT: @llvm.launder.invariant.group
125  // CHECK: ret void
126  TestVirtualReferenceMember *d = __builtin_launder(p);
127}
128
129struct TestRecursiveMember {
130  TestRecursiveMember() : member(*this) {}
131  TestRecursiveMember &member;
132};
133
134// CHECK-LABEL: define void @test_builtin_launder_recursive_member
135extern "C" void test_builtin_launder_recursive_member(TestRecursiveMember *p) {
136  // CHECK: entry
137  // CHECK-NOT: @llvm.launder.invariant.group
138  // CHECK: ret void
139  TestRecursiveMember *d = __builtin_launder(p);
140}
141
142struct TestVirtualRecursiveMember {
143  TestVirtualRecursiveMember() : member(*this) {}
144  TestVirtualRecursiveMember &member;
145  virtual void foo();
146};
147
148// CHECK-LABEL: define void @test_builtin_launder_virtual_recursive_member
149extern "C" void test_builtin_launder_virtual_recursive_member(TestVirtualRecursiveMember *p) {
150  // CHECK: entry
151  // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
152  // CHECK-STRICT: @llvm.launder.invariant.group
153  // CHECK: ret void
154  TestVirtualRecursiveMember *d = __builtin_launder(p);
155}
156
157// CHECK-LABEL: define void @test_builtin_launder_array(
158extern "C" void test_builtin_launder_array(TestVirtualFn (&Arr)[5]) {
159  // CHECK: entry
160  // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
161  // CHECK-STRICT: @llvm.launder.invariant.group
162  // CHECK: ret void
163  TestVirtualFn *d = __builtin_launder(Arr);
164}
165
166// CHECK-LABEL: define void @test_builtin_launder_array_nested(
167extern "C" void test_builtin_launder_array_nested(TestVirtualFn (&Arr)[5][2]) {
168  // CHECK: entry
169  // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
170  // CHECK-STRICT: @llvm.launder.invariant.group
171  // CHECK: ret void
172  using RetTy = TestVirtualFn(*)[2];
173  RetTy d = __builtin_launder(Arr);
174}
175
176// CHECK-LABEL: define void @test_builtin_launder_array_no_invariant(
177extern "C" void test_builtin_launder_array_no_invariant(TestNoInvariant (&Arr)[5]) {
178  // CHECK: entry
179  // CHECK-NOT: @llvm.launder.invariant.group
180  // CHECK: ret void
181  TestNoInvariant *d = __builtin_launder(Arr);
182}
183
184// CHECK-LABEL: define void @test_builtin_launder_array_nested_no_invariant(
185extern "C" void test_builtin_launder_array_nested_no_invariant(TestNoInvariant (&Arr)[5][2]) {
186  // CHECK: entry
187  // CHECK-NOT: @llvm.launder.invariant.group
188  // CHECK: ret void
189  using RetTy = TestNoInvariant(*)[2];
190  RetTy d = __builtin_launder(Arr);
191}
192
193template <class Member>
194struct WithMember {
195  Member mem;
196};
197
198template struct WithMember<TestVirtualFn[5]>;
199
200// CHECK-LABEL: define void @test_builtin_launder_member_array(
201extern "C" void test_builtin_launder_member_array(WithMember<TestVirtualFn[5]> *p) {
202  // CHECK: entry
203  // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
204  // CHECK-STRICT: @llvm.launder.invariant.group
205  // CHECK: ret void
206  auto *d = __builtin_launder(p);
207}
208
209template struct WithMember<TestVirtualFn[5][2]>;
210
211// CHECK-LABEL: define void @test_builtin_launder_member_array_nested(
212extern "C" void test_builtin_launder_member_array_nested(WithMember<TestVirtualFn[5][2]> *p) {
213  // CHECK: entry
214  // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
215  // CHECK-STRICT: @llvm.launder.invariant.group
216  // CHECK: ret void
217  auto *d = __builtin_launder(p);
218}
219
220template struct WithMember<TestNoInvariant[5]>;
221
222// CHECK-LABEL: define void @test_builtin_launder_member_array_no_invariant(
223extern "C" void test_builtin_launder_member_array_no_invariant(WithMember<TestNoInvariant[5]> *p) {
224  // CHECK: entry
225  // CHECK-NOT: @llvm.launder.invariant.group
226  // CHECK: ret void
227  auto *d = __builtin_launder(p);
228}
229
230template struct WithMember<TestNoInvariant[5][2]>;
231
232// CHECK-LABEL: define void @test_builtin_launder_member_array_nested_no_invariant(
233extern "C" void test_builtin_launder_member_array_nested_no_invariant(WithMember<TestNoInvariant[5][2]> *p) {
234  // CHECK: entry
235  // CHECK-NOT: @llvm.launder.invariant.group
236  // CHECK: ret void
237  auto *d = __builtin_launder(p);
238}
239
240template <class T>
241struct WithBase : T {};
242
243template struct WithBase<TestNoInvariant>;
244
245// CHECK-LABEL: define void @test_builtin_launder_base_no_invariant(
246extern "C" void test_builtin_launder_base_no_invariant(WithBase<TestNoInvariant> *p) {
247  // CHECK: entry
248  // CHECK-NOT: @llvm.launder.invariant.group
249  // CHECK: ret void
250  auto *d = __builtin_launder(p);
251}
252
253template struct WithBase<TestVirtualFn>;
254
255// CHECK-LABEL: define void @test_builtin_launder_base(
256extern "C" void test_builtin_launder_base(WithBase<TestVirtualFn> *p) {
257  // CHECK: entry
258  // CHECK-NONSTRICT-NOT: @llvm.launder.invariant.group
259  // CHECK-STRICT: @llvm.launder.invariant.group
260  // CHECK: ret void
261  auto *d = __builtin_launder(p);
262}
263
264/// The test cases in this namespace technically need to be laundered according
265/// to the language in the standard (ie they have const or reference subobjects)
266/// but LLVM doesn't currently optimize on these cases -- so Clang emits
267/// __builtin_launder as a nop.
268///
269/// NOTE: Adding optimizations for these cases later is an LTO ABI break. That's
270/// probably OK for now -- but is something to keep in mind.
271namespace pessimizing_cases {
272
273struct TestConstMember {
274  const int x;
275};
276
277// CHECK-LABEL: define void @test_builtin_launder_const_member
278extern "C" void test_builtin_launder_const_member(TestConstMember *p) {
279  // CHECK: entry
280  // CHECK-NOT: @llvm.launder.invariant.group
281  // CHECK: ret void
282  TestConstMember *d = __builtin_launder(p);
283}
284
285struct TestConstSubobject {
286  TestConstMember x;
287};
288
289// CHECK-LABEL: define void @test_builtin_launder_const_subobject
290extern "C" void test_builtin_launder_const_subobject(TestConstSubobject *p) {
291  // CHECK: entry
292  // CHECK-NOT: @llvm.launder.invariant.group
293  // CHECK: ret void
294  TestConstSubobject *d = __builtin_launder(p);
295}
296
297struct TestConstObject {
298  const struct TestConstMember x;
299};
300
301// CHECK-LABEL: define void @test_builtin_launder_const_object
302extern "C" void test_builtin_launder_const_object(TestConstObject *p) {
303  // CHECK: entry
304  // CHECK-NOT: @llvm.launder.invariant.group
305  // CHECK: ret void
306  TestConstObject *d = __builtin_launder(p);
307}
308
309struct TestReferenceMember {
310  int &x;
311};
312
313// CHECK-LABEL: define void @test_builtin_launder_reference_member
314extern "C" void test_builtin_launder_reference_member(TestReferenceMember *p) {
315  // CHECK: entry
316  // CHECK-NOT: @llvm.launder.invariant.group
317  // CHECK: ret void
318  TestReferenceMember *d = __builtin_launder(p);
319}
320
321} // namespace pessimizing_cases
322