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