Skip to content

Commit 8e5c4c5

Browse files
committed
Support __builtin_launder except in the case of -fstrict-vtable-pointers
1 parent da601b3 commit 8e5c4c5

File tree

3 files changed

+338
-2
lines changed

3 files changed

+338
-2
lines changed

clang/include/clang/CIR/MissingFeatures.h

+1
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ struct MissingFeatures {
229229
static bool emitEmptyRecordCheck() { return false; }
230230
static bool isPPC_FP128Ty() { return false; }
231231
static bool emitBinaryAtomicPostHasInvert() { return false; }
232+
static bool createLaunderInvariantGroup() { return false; }
232233

233234
// Inline assembly
234235
static bool asmGoto() { return false; }

clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp

+47-2
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,43 @@ static mlir::Value MakeAtomicCmpXchgValue(CIRGenFunction &cgf,
330330
return returnBool ? op.getResult(1) : op.getResult(0);
331331
}
332332

333+
static bool
334+
typeRequiresBuiltinLaunderImp(const ASTContext &ctx, QualType ty,
335+
llvm::SmallPtrSetImpl<const Decl *> &seen) {
336+
if (const auto *arr = ctx.getAsArrayType(ty))
337+
ty = ctx.getBaseElementType(arr);
338+
339+
const auto *record = ty->getAsCXXRecordDecl();
340+
if (!record)
341+
return false;
342+
343+
// We've already checked this type, or are in the process of checking it.
344+
if (!seen.insert(record).second)
345+
return false;
346+
347+
assert(record->hasDefinition() &&
348+
"Incomplete types should already be diagnosed");
349+
350+
if (record->isDynamicClass())
351+
return true;
352+
353+
for (FieldDecl *fld : record->fields()) {
354+
if (typeRequiresBuiltinLaunderImp(ctx, fld->getType(), seen))
355+
return true;
356+
}
357+
return false;
358+
}
359+
360+
/// Determine if the specified type requires laundering by checking if it is a
361+
/// dynamic class type or contains a subobject which is a dynamic class type.
362+
static bool typeRequiresBuiltinLaunder(clang::CIRGen::CIRGenModule &cgm,
363+
QualType ty) {
364+
if (!cgm.getCodeGenOpts().StrictVTablePointers)
365+
return false;
366+
llvm::SmallPtrSet<const Decl *, 16> seen;
367+
return typeRequiresBuiltinLaunderImp(cgm.getASTContext(), ty, seen);
368+
}
369+
333370
RValue CIRGenFunction::emitRotate(const CallExpr *E, bool IsRotateRight) {
334371
auto src = emitScalarExpr(E->getArg(0));
335372
auto shiftAmt = emitScalarExpr(E->getArg(1));
@@ -1610,8 +1647,16 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
16101647
llvm_unreachable("BI__builtin_setjmp NYI");
16111648
case Builtin::BI__builtin_longjmp:
16121649
llvm_unreachable("BI__builtin_longjmp NYI");
1613-
case Builtin::BI__builtin_launder:
1614-
llvm_unreachable("BI__builtin_launder NYI");
1650+
case Builtin::BI__builtin_launder: {
1651+
const clang::Expr *arg = E->getArg(0);
1652+
clang::QualType argTy = arg->getType()->getPointeeType();
1653+
mlir::Value ptr = emitScalarExpr(arg);
1654+
if (typeRequiresBuiltinLaunder(CGM, argTy)) {
1655+
assert(!MissingFeatures::createLaunderInvariantGroup());
1656+
llvm_unreachable(" launder.invariant.group NYI ");
1657+
}
1658+
return RValue::get(ptr);
1659+
}
16151660

16161661
case Builtin::BI__sync_fetch_and_add:
16171662
case Builtin::BI__sync_fetch_and_sub:

clang/test/CIR/CodeGen/builtins.cpp

+290
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,293 @@ extern "C" void *test_return_address(void) {
105105
// LLVM-LABEL: @test_return_address
106106
// LLVM: {{%.*}} = call ptr @llvm.returnaddress(i32 1)
107107
}
108+
109+
110+
// Following block of tests are for __builtin_launder
111+
// FIXME: Once we fully __builtin_launder by allowing -fstrict-vtable-pointers,
112+
// we should move following block of tests to a separate file.
113+
namespace launder_test {
114+
//===----------------------------------------------------------------------===//
115+
// Positive Cases
116+
//===----------------------------------------------------------------------===//
117+
118+
struct TestVirtualFn {
119+
virtual void foo() {}
120+
};
121+
122+
// CIR-LABEL: test_builtin_launder_virtual_fn
123+
// LLVM: define{{.*}} void @test_builtin_launder_virtual_fn(ptr [[P:%.*]])
124+
extern "C" void test_builtin_launder_virtual_fn(TestVirtualFn *p) {
125+
// CIR: cir.return
126+
127+
// LLVM: store ptr [[P]], ptr [[P_ADDR:%.*]], align 8
128+
// LLVM-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
129+
// LLVM-NEXT: store ptr [[TMP0]], ptr {{%.*}}
130+
// LLVM-NEXT: ret void
131+
TestVirtualFn *d = __builtin_launder(p);
132+
}
133+
134+
struct TestPolyBase : TestVirtualFn {
135+
};
136+
137+
// CIR-LABEL: test_builtin_launder_poly_base
138+
// LLVM: define{{.*}} void @test_builtin_launder_poly_base(ptr [[P:%.*]])
139+
extern "C" void test_builtin_launder_poly_base(TestPolyBase *p) {
140+
// CIR: cir.return
141+
142+
// LLVM: store ptr [[P]], ptr [[P_ADDR:%.*]], align 8
143+
// LLVM-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
144+
// LLVM-NEXT: store ptr [[TMP0]], ptr {{%.*}}
145+
// LLVM-NEXT: ret void
146+
TestPolyBase *d = __builtin_launder(p);
147+
}
148+
149+
struct TestBase {};
150+
struct TestVirtualBase : virtual TestBase {};
151+
152+
// CIR-LABEL: test_builtin_launder_virtual_base
153+
// LLVM: define{{.*}} void @test_builtin_launder_virtual_base(ptr [[P:%.*]])
154+
extern "C" void test_builtin_launder_virtual_base(TestVirtualBase *p) {
155+
TestVirtualBase *d = __builtin_launder(p);
156+
157+
// CIR: cir.return
158+
159+
// LLVM: store ptr [[P]], ptr [[P_ADDR:%.*]], align 8
160+
// LLVM-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
161+
// LLVM-NEXT: store ptr [[TMP0]], ptr {{%.*}}
162+
// LLVM-NEXT: ret void
163+
}
164+
165+
//===----------------------------------------------------------------------===//
166+
// Negative Cases
167+
//===----------------------------------------------------------------------===//
168+
169+
// CIR-LABEL: test_builtin_launder_ommitted_one
170+
// LLVM: define{{.*}} void @test_builtin_launder_ommitted_one(ptr [[P:%.*]])
171+
extern "C" void test_builtin_launder_ommitted_one(int *p) {
172+
int *d = __builtin_launder(p);
173+
174+
// CIR: cir.return
175+
176+
// LLVM-NEXT: [[P_ADDR:%.*]] = alloca ptr, i64 1, align 8
177+
// LLVM-NEXT: [[D:%.*]] = alloca ptr, i64 1, align 8
178+
// LLVM: store ptr [[P]], ptr [[P_ADDR:%.*]], align 8
179+
// LLVM-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
180+
// LLVM-NEXT: store ptr [[TMP0]], ptr [[D]]
181+
// LLVM-NEXT: ret void
182+
}
183+
184+
struct TestNoInvariant {
185+
int x;
186+
};
187+
188+
// CIR-LABEL: test_builtin_launder_ommitted_two
189+
// LLVM: define{{.*}} void @test_builtin_launder_ommitted_two(ptr [[P:%.*]])
190+
extern "C" void test_builtin_launder_ommitted_two(TestNoInvariant *p) {
191+
TestNoInvariant *d = __builtin_launder(p);
192+
// CIR: cir.return
193+
194+
// LLVM-NOT: llvm.launder.invariant.group
195+
// LLVM-NEXT: [[P_ADDR:%.*]] = alloca ptr, i64 1, align 8
196+
// LLVM-NEXT: [[D:%.*]] = alloca ptr, i64 1, align 8
197+
// LLVM: store ptr [[P]], ptr [[P_ADDR:%.*]], align 8
198+
// LLVM-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR]], align 8
199+
// LLVM-NEXT: store ptr [[TMP0]], ptr [[D]]
200+
// LLVM-NEXT: ret void
201+
}
202+
203+
struct TestVirtualMember {
204+
TestVirtualFn member;
205+
};
206+
207+
// CIR-LABEL: test_builtin_launder_virtual_member
208+
// LLVM: define{{.*}} void @test_builtin_launder_virtual_member
209+
extern "C" void test_builtin_launder_virtual_member(TestVirtualMember *p) {
210+
// CIR: cir.return
211+
212+
// LLVM-NOT: llvm.launder.invariant.group
213+
// LLVM: ret void
214+
TestVirtualMember *d = __builtin_launder(p);
215+
}
216+
217+
struct TestVirtualMemberDepth2 {
218+
TestVirtualMember member;
219+
};
220+
221+
// CIR-LABEL: test_builtin_launder_virtual_member_depth_2
222+
// LLVM: define{{.*}} void @test_builtin_launder_virtual_member_depth_2
223+
extern "C" void test_builtin_launder_virtual_member_depth_2(TestVirtualMemberDepth2 *p) {
224+
// CIR: cir.return
225+
226+
// LLVM-NOT: llvm.launder.invariant.group
227+
// LLVM: ret void
228+
TestVirtualMemberDepth2 *d = __builtin_launder(p);
229+
}
230+
231+
struct TestVirtualReferenceMember {
232+
TestVirtualFn &member;
233+
};
234+
235+
// CIR-LABEL: test_builtin_launder_virtual_reference_member
236+
// LLVM: define{{.*}} void @test_builtin_launder_virtual_reference_member
237+
extern "C" void test_builtin_launder_virtual_reference_member(TestVirtualReferenceMember *p) {
238+
// CIR: cir.return
239+
240+
// LLVM-NOT: llvm.launder.invariant.group
241+
// LLVM: ret void
242+
TestVirtualReferenceMember *d = __builtin_launder(p);
243+
}
244+
245+
struct TestRecursiveMember {
246+
TestRecursiveMember() : member(*this) {}
247+
TestRecursiveMember &member;
248+
};
249+
250+
// CIR-LABEL: test_builtin_launder_recursive_member
251+
// LLVM: define{{.*}} void @test_builtin_launder_recursive_member
252+
extern "C" void test_builtin_launder_recursive_member(TestRecursiveMember *p) {
253+
// CIR: cir.return
254+
255+
// LLVM-NOT: llvm.launder.invariant.group
256+
// LLVM: ret void
257+
TestRecursiveMember *d = __builtin_launder(p);
258+
}
259+
260+
struct TestVirtualRecursiveMember {
261+
TestVirtualRecursiveMember() : member(*this) {}
262+
TestVirtualRecursiveMember &member;
263+
virtual void foo();
264+
};
265+
266+
// CIR-LABEL: test_builtin_launder_virtual_recursive_member
267+
// LLVM: define{{.*}} void @test_builtin_launder_virtual_recursive_member
268+
extern "C" void test_builtin_launder_virtual_recursive_member(TestVirtualRecursiveMember *p) {
269+
// CIR: cir.return
270+
271+
// LLVM-NOT: llvm.launder.invariant.group
272+
// LLVM: ret void
273+
TestVirtualRecursiveMember *d = __builtin_launder(p);
274+
}
275+
276+
// CIR-LABEL: test_builtin_launder_array
277+
// LLVM: define{{.*}} void @test_builtin_launder_array
278+
extern "C" void test_builtin_launder_array(TestVirtualFn (&Arr)[5]) {
279+
// CIR: cir.return
280+
281+
// LLVM-NOT: llvm.launder.invariant.group
282+
// LLVM: ret void
283+
TestVirtualFn *d = __builtin_launder(Arr);
284+
}
285+
286+
// CIR-LABEL: test_builtin_launder_array_nested
287+
// LLVM: define{{.*}} void @test_builtin_launder_array_nested
288+
extern "C" void test_builtin_launder_array_nested(TestVirtualFn (&Arr)[5][2]) {
289+
// CIR: cir.return
290+
291+
// LLVM-NOT: llvm.launder.invariant.group
292+
// LLVM: ret void
293+
using RetTy = TestVirtualFn(*)[2];
294+
RetTy d = __builtin_launder(Arr);
295+
}
296+
297+
// CIR-LABEL: test_builtin_launder_array_no_invariant
298+
// LLVM: define{{.*}} void @test_builtin_launder_array_no_invariant
299+
extern "C" void test_builtin_launder_array_no_invariant(TestNoInvariant (&Arr)[5]) {
300+
// CIR: cir.return
301+
302+
// LLVM-NOT: llvm.launder.invariant.group
303+
// LLVM: ret void
304+
TestNoInvariant *d = __builtin_launder(Arr);
305+
}
306+
307+
// CIR-LABEL: test_builtin_launder_array_nested_no_invariant
308+
// LLVM: define{{.*}} void @test_builtin_launder_array_nested_no_invariant
309+
extern "C" void test_builtin_launder_array_nested_no_invariant(TestNoInvariant (&Arr)[5][2]) {
310+
// CIR: cir.return
311+
312+
// LLVM-NOT: llvm.launder.invariant.group
313+
// LLVM: ret void
314+
using RetTy = TestNoInvariant(*)[2];
315+
RetTy d = __builtin_launder(Arr);
316+
}
317+
318+
template <class Member>
319+
struct WithMember {
320+
Member mem;
321+
};
322+
323+
template struct WithMember<TestVirtualFn[5]>;
324+
325+
// CIR-LABEL: test_builtin_launder_member_array
326+
// LLVM: define{{.*}} void @test_builtin_launder_member_array
327+
extern "C" void test_builtin_launder_member_array(WithMember<TestVirtualFn[5]> *p) {
328+
// CIR: cir.return
329+
330+
// LLVM-NOT: llvm.launder.invariant.group
331+
// LLVM: ret void
332+
auto *d = __builtin_launder(p);
333+
}
334+
335+
template struct WithMember<TestVirtualFn[5][2]>;
336+
337+
// CIR-LABEL: test_builtin_launder_member_array_nested
338+
// LLVM: define{{.*}} void @test_builtin_launder_member_array_nested
339+
extern "C" void test_builtin_launder_member_array_nested(WithMember<TestVirtualFn[5][2]> *p) {
340+
// CIR: cir.return
341+
342+
// LLVM-NOT: llvm.launder.invariant.group
343+
// LLVM: ret void
344+
auto *d = __builtin_launder(p);
345+
}
346+
347+
template struct WithMember<TestNoInvariant[5]>;
348+
349+
// CIR-LABEL: test_builtin_launder_member_array_no_invariant
350+
// LLVM: define{{.*}} void @test_builtin_launder_member_array_no_invariant
351+
extern "C" void test_builtin_launder_member_array_no_invariant(WithMember<TestNoInvariant[5]> *p) {
352+
// CIR: cir.return
353+
354+
// LLVM-NOT: llvm.launder.invariant.group
355+
// LLVM: ret void
356+
auto *d = __builtin_launder(p);
357+
}
358+
359+
template struct WithMember<TestNoInvariant[5][2]>;
360+
361+
// CIR-LABEL: test_builtin_launder_member_array_nested_no_invariant
362+
// LLVM: define{{.*}} void @test_builtin_launder_member_array_nested_no_invariant
363+
extern "C" void test_builtin_launder_member_array_nested_no_invariant(WithMember<TestNoInvariant[5][2]> *p) {
364+
// CIR: cir.return
365+
366+
// LLVM-NOT: llvm.launder.invariant.group
367+
// LLVM: ret void
368+
auto *d = __builtin_launder(p);
369+
}
370+
371+
template <class T>
372+
struct WithBase : T {};
373+
374+
template struct WithBase<TestNoInvariant>;
375+
376+
// CIR-LABEL: test_builtin_launder_base_no_invariant
377+
// LLVM: define{{.*}} void @test_builtin_launder_base_no_invariant
378+
extern "C" void test_builtin_launder_base_no_invariant(WithBase<TestNoInvariant> *p) {
379+
// CIR: cir.return
380+
381+
// LLVM-NOT: llvm.launder.invariant.group
382+
// LLVM: ret void
383+
auto *d = __builtin_launder(p);
384+
}
385+
386+
template struct WithBase<TestVirtualFn>;
387+
388+
// CIR-LABEL: test_builtin_launder_base
389+
// LLVM: define{{.*}} void @test_builtin_launder_base
390+
extern "C" void test_builtin_launder_base(WithBase<TestVirtualFn> *p) {
391+
// CIR: cir.return
392+
393+
// LLVM-NOT: llvm.launder.invariant.group
394+
// LLVM: ret void
395+
auto *d = __builtin_launder(p);
396+
}
397+
}

0 commit comments

Comments
 (0)