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