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