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 
10 struct TestVirtualFn {
fooTestVirtualFn11   virtual void foo() {}
12 };
13 
14 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_fn
test_builtin_launder_virtual_fn(TestVirtualFn * p)15 extern "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 
30 struct TestPolyBase : TestVirtualFn {
31 };
32 
33 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_poly_base
test_builtin_launder_poly_base(TestPolyBase * p)34 extern "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 
44 struct TestBase {};
45 struct TestVirtualBase : virtual TestBase {};
46 
47 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_base
test_builtin_launder_virtual_base(TestVirtualBase * p)48 extern "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
test_builtin_launder_ommitted_one(int * p)63 extern "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 
74 struct TestNoInvariant {
75   int x;
76 };
77 
78 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_ommitted_two
test_builtin_launder_ommitted_two(TestNoInvariant * p)79 extern "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 
91 struct TestVirtualMember {
92   TestVirtualFn member;
93 };
94 
95 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_member
test_builtin_launder_virtual_member(TestVirtualMember * p)96 extern "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 
104 struct TestVirtualMemberDepth2 {
105   TestVirtualMember member;
106 };
107 
108 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_member_depth_2
test_builtin_launder_virtual_member_depth_2(TestVirtualMemberDepth2 * p)109 extern "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 
117 struct TestVirtualReferenceMember {
118   TestVirtualFn &member;
119 };
120 
121 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_reference_member
test_builtin_launder_virtual_reference_member(TestVirtualReferenceMember * p)122 extern "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 
129 struct TestRecursiveMember {
TestRecursiveMemberTestRecursiveMember130   TestRecursiveMember() : member(*this) {}
131   TestRecursiveMember &member;
132 };
133 
134 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_recursive_member
test_builtin_launder_recursive_member(TestRecursiveMember * p)135 extern "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 
142 struct TestVirtualRecursiveMember {
TestVirtualRecursiveMemberTestVirtualRecursiveMember143   TestVirtualRecursiveMember() : member(*this) {}
144   TestVirtualRecursiveMember &member;
145   virtual void foo();
146 };
147 
148 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_virtual_recursive_member
test_builtin_launder_virtual_recursive_member(TestVirtualRecursiveMember * p)149 extern "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(
test_builtin_launder_array(TestVirtualFn (& Arr)[5])158 extern "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(
test_builtin_launder_array_nested(TestVirtualFn (& Arr)[5][2])167 extern "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(
test_builtin_launder_array_no_invariant(TestNoInvariant (& Arr)[5])177 extern "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(
test_builtin_launder_array_nested_no_invariant(TestNoInvariant (& Arr)[5][2])185 extern "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 
193 template <class Member>
194 struct WithMember {
195   Member mem;
196 };
197 
198 template struct WithMember<TestVirtualFn[5]>;
199 
200 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array(
test_builtin_launder_member_array(WithMember<TestVirtualFn[5]> * p)201 extern "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 
209 template struct WithMember<TestVirtualFn[5][2]>;
210 
211 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array_nested(
test_builtin_launder_member_array_nested(WithMember<TestVirtualFn[5][2]> * p)212 extern "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 
220 template struct WithMember<TestNoInvariant[5]>;
221 
222 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array_no_invariant(
test_builtin_launder_member_array_no_invariant(WithMember<TestNoInvariant[5]> * p)223 extern "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 
230 template struct WithMember<TestNoInvariant[5][2]>;
231 
232 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_member_array_nested_no_invariant(
test_builtin_launder_member_array_nested_no_invariant(WithMember<TestNoInvariant[5][2]> * p)233 extern "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 
240 template <class T>
241 struct WithBase : T {};
242 
243 template struct WithBase<TestNoInvariant>;
244 
245 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_base_no_invariant(
test_builtin_launder_base_no_invariant(WithBase<TestNoInvariant> * p)246 extern "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 
253 template struct WithBase<TestVirtualFn>;
254 
255 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_base(
test_builtin_launder_base(WithBase<TestVirtualFn> * p)256 extern "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.
271 namespace pessimizing_cases {
272 
273 struct TestConstMember {
274   const int x;
275 };
276 
277 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_const_member
test_builtin_launder_const_member(TestConstMember * p)278 extern "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 
285 struct TestConstSubobject {
286   TestConstMember x;
287 };
288 
289 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_const_subobject
test_builtin_launder_const_subobject(TestConstSubobject * p)290 extern "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 
297 struct TestConstObject {
298   const struct TestConstMember x;
299 };
300 
301 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_const_object
test_builtin_launder_const_object(TestConstObject * p)302 extern "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 
309 struct TestReferenceMember {
310   int &x;
311 };
312 
313 // CHECK-LABEL: define{{.*}} void @test_builtin_launder_reference_member
test_builtin_launder_reference_member(TestReferenceMember * p)314 extern "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