Skip to content

Commit ffe3768

Browse files
authored
[CIR] Add support for emitting vtables (#154808)
This adds a simplified version of the code to emit vtables. It does not yet handle RTTI or cases that require multiple vtables.
1 parent b998750 commit ffe3768

File tree

12 files changed

+410
-9
lines changed

12 files changed

+410
-9
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ struct MissingFeatures {
279279
static bool appleKext() { return false; }
280280
static bool dtorCleanups() { return false; }
281281
static bool vtableInitialization() { return false; }
282+
static bool vtableEmitMetadata() { return false; }
282283
static bool vtableRelativeLayout() { return false; }
283284
static bool msvcBuiltins() { return false; }
284285
static bool vaArgABILowering() { return false; }

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ class CIRGenCXXABI {
9595
isVirtualOffsetNeededForVTableField(CIRGenFunction &cgf,
9696
CIRGenFunction::VPtr vptr) = 0;
9797

98+
/// Emits the VTable definitions required for the given record type.
99+
virtual void emitVTableDefinitions(CIRGenVTables &cgvt,
100+
const CXXRecordDecl *rd) = 0;
101+
98102
/// Returns true if the given destructor type should be emitted as a linkonce
99103
/// delegating thunk, regardless of whether the dtor is defined in this TU or
100104
/// not.

clang/lib/CIR/CodeGen/CIRGenCall.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ CIRGenFunctionInfo::create(CanQualType resultType,
4242
return fi;
4343
}
4444

45+
cir::FuncType CIRGenTypes::getFunctionType(GlobalDecl gd) {
46+
const CIRGenFunctionInfo &fi = arrangeGlobalDeclaration(gd);
47+
return getFunctionType(fi);
48+
}
49+
4550
cir::FuncType CIRGenTypes::getFunctionType(const CIRGenFunctionInfo &info) {
4651
mlir::Type resultType = convertType(info.getReturnType());
4752
SmallVector<mlir::Type, 8> argTypes;
@@ -55,6 +60,16 @@ cir::FuncType CIRGenTypes::getFunctionType(const CIRGenFunctionInfo &info) {
5560
info.isVariadic());
5661
}
5762

63+
cir::FuncType CIRGenTypes::getFunctionTypeForVTable(GlobalDecl gd) {
64+
const CXXMethodDecl *md = cast<CXXMethodDecl>(gd.getDecl());
65+
const FunctionProtoType *fpt = md->getType()->getAs<FunctionProtoType>();
66+
67+
if (!isFuncTypeConvertible(fpt))
68+
cgm.errorNYI("getFunctionTypeForVTable: non-convertible function type");
69+
70+
return getFunctionType(gd);
71+
}
72+
5873
CIRGenCallee CIRGenCallee::prepareConcreteCallee(CIRGenFunction &cgf) const {
5974
if (isVirtual()) {
6075
const CallExpr *ce = getVirtualCallExpr();

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
8181
CIRGenFunction &cgf, const clang::CXXRecordDecl *vtableClass,
8282
clang::BaseSubobject base,
8383
const clang::CXXRecordDecl *nearestVBase) override;
84+
void emitVTableDefinitions(CIRGenVTables &cgvt,
85+
const CXXRecordDecl *rd) override;
8486

8587
bool doStructorsInitializeVPtrs(const CXXRecordDecl *vtableClass) override {
8688
return true;
@@ -270,6 +272,67 @@ bool CIRGenItaniumCXXABI::needsVTTParameter(GlobalDecl gd) {
270272
return false;
271273
}
272274

275+
void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
276+
const CXXRecordDecl *rd) {
277+
cir::GlobalOp vtable = getAddrOfVTable(rd, CharUnits());
278+
if (vtable.hasInitializer())
279+
return;
280+
281+
ItaniumVTableContext &vtContext = cgm.getItaniumVTableContext();
282+
const VTableLayout &vtLayout = vtContext.getVTableLayout(rd);
283+
cir::GlobalLinkageKind linkage = cgm.getVTableLinkage(rd);
284+
mlir::Attribute rtti =
285+
cgm.getAddrOfRTTIDescriptor(cgm.getLoc(rd->getBeginLoc()),
286+
cgm.getASTContext().getCanonicalTagType(rd));
287+
288+
// Classic codegen uses ConstantInitBuilder here, which is a very general
289+
// and feature-rich class to generate initializers for global values.
290+
// For now, this is using a simpler approach to create the initializer in CIR.
291+
cgvt.createVTableInitializer(vtable, vtLayout, rtti,
292+
cir::isLocalLinkage(linkage));
293+
294+
// Set the correct linkage.
295+
vtable.setLinkage(linkage);
296+
297+
if (cgm.supportsCOMDAT() && cir::isWeakForLinker(linkage))
298+
vtable.setComdat(true);
299+
300+
// Set the right visibility.
301+
cgm.setGVProperties(vtable, rd);
302+
303+
// If this is the magic class __cxxabiv1::__fundamental_type_info,
304+
// we will emit the typeinfo for the fundamental types. This is the
305+
// same behaviour as GCC.
306+
const DeclContext *DC = rd->getDeclContext();
307+
if (rd->getIdentifier() &&
308+
rd->getIdentifier()->isStr("__fundamental_type_info") &&
309+
isa<NamespaceDecl>(DC) && cast<NamespaceDecl>(DC)->getIdentifier() &&
310+
cast<NamespaceDecl>(DC)->getIdentifier()->isStr("__cxxabiv1") &&
311+
DC->getParent()->isTranslationUnit()) {
312+
cgm.errorNYI(rd->getSourceRange(),
313+
"emitVTableDefinitions: __fundamental_type_info");
314+
}
315+
316+
auto vtableAsGlobalValue = dyn_cast<cir::CIRGlobalValueInterface>(*vtable);
317+
assert(vtableAsGlobalValue && "VTable must support CIRGlobalValueInterface");
318+
// Always emit type metadata on non-available_externally definitions, and on
319+
// available_externally definitions if we are performing whole program
320+
// devirtualization. For WPD we need the type metadata on all vtable
321+
// definitions to ensure we associate derived classes with base classes
322+
// defined in headers but with a strong definition only in a shared
323+
// library.
324+
assert(!cir::MissingFeatures::vtableEmitMetadata());
325+
if (cgm.getCodeGenOpts().WholeProgramVTables) {
326+
cgm.errorNYI(rd->getSourceRange(),
327+
"emitVTableDefinitions: WholeProgramVTables");
328+
}
329+
330+
assert(!cir::MissingFeatures::vtableRelativeLayout());
331+
if (vtContext.isRelativeLayout()) {
332+
cgm.errorNYI(rd->getSourceRange(), "vtableRelativeLayout");
333+
}
334+
}
335+
273336
void CIRGenItaniumCXXABI::emitDestructorCall(
274337
CIRGenFunction &cgf, const CXXDestructorDecl *dd, CXXDtorType type,
275338
bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy) {

clang/lib/CIR/CodeGen/CIRGenModule.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,7 @@ void CIRGenModule::emitGlobalDefinition(clang::GlobalDecl gd,
845845
emitGlobalFunctionDefinition(gd, op);
846846

847847
if (method->isVirtual())
848-
errorNYI(method->getSourceRange(), "virtual member function");
848+
getVTables().emitThunks(gd);
849849

850850
return;
851851
}
@@ -2151,6 +2151,18 @@ bool CIRGenModule::verifyModule() const {
21512151
return mlir::verify(theModule).succeeded();
21522152
}
21532153

2154+
mlir::Attribute CIRGenModule::getAddrOfRTTIDescriptor(mlir::Location loc,
2155+
QualType ty, bool forEh) {
2156+
// Return a bogus pointer if RTTI is disabled, unless it's for EH.
2157+
// FIXME: should we even be calling this method if RTTI is disabled
2158+
// and it's not for EH?
2159+
if (!shouldEmitRTTI(forEh))
2160+
return builder.getConstNullPtrAttr(builder.getUInt8PtrTy());
2161+
2162+
errorNYI(loc, "getAddrOfRTTIDescriptor");
2163+
return mlir::Attribute();
2164+
}
2165+
21542166
// TODO(cir): this can be shared with LLVM codegen.
21552167
CharUnits CIRGenModule::computeNonVirtualBaseClassOffset(
21562168
const CXXRecordDecl *derivedClass,

clang/lib/CIR/CodeGen/CIRGenModule.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,16 @@ class CIRGenModule : public CIRGenTypeCache {
190190
mlir::Location loc, llvm::StringRef name, mlir::Type ty,
191191
cir::GlobalLinkageKind linkage, clang::CharUnits alignment);
192192

193+
void emitVTable(const CXXRecordDecl *rd);
194+
195+
/// Return the appropriate linkage for the vtable, VTT, and type information
196+
/// of the given class.
197+
cir::GlobalLinkageKind getVTableLinkage(const CXXRecordDecl *rd);
198+
199+
/// Get the address of the RTTI descriptor for the given type.
200+
mlir::Attribute getAddrOfRTTIDescriptor(mlir::Location loc, QualType ty,
201+
bool forEH = false);
202+
193203
/// Return a constant array for the given string.
194204
mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e);
195205

@@ -290,6 +300,13 @@ class CIRGenModule : public CIRGenTypeCache {
290300
getAddrOfGlobal(clang::GlobalDecl gd,
291301
ForDefinition_t isForDefinition = NotForDefinition);
292302

303+
// Return whether RTTI information should be emitted for this target.
304+
bool shouldEmitRTTI(bool forEH = false) {
305+
return (forEH || getLangOpts().RTTI) && !getLangOpts().CUDAIsDevice &&
306+
!(getLangOpts().OpenMP && getLangOpts().OpenMPIsTargetDevice &&
307+
getTriple().isNVPTX());
308+
}
309+
293310
/// Emit type info if type of an expression is a variably modified
294311
/// type. Also emit proper debug info for cast types.
295312
void emitExplicitCastExprType(const ExplicitCastExpr *e,

clang/lib/CIR/CodeGen/CIRGenTypes.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ class CIRGenTypes {
130130
/// Get the CIR function type for \arg Info.
131131
cir::FuncType getFunctionType(const CIRGenFunctionInfo &info);
132132

133+
cir::FuncType getFunctionType(clang::GlobalDecl gd);
134+
135+
/// Get the CIR function type for use in a vtable, given a CXXMethodDecl. If
136+
/// the method has an incomplete return type, and/or incomplete argument
137+
/// types, this will return the opaque type.
138+
cir::FuncType getFunctionTypeForVTable(clang::GlobalDecl gd);
139+
133140
// The arrangement methods are split into three families:
134141
// - those meant to drive the signature and prologue/epilogue
135142
// of a function declaration or definition,

0 commit comments

Comments
 (0)