Skip to content

Commit 9f7bb1c

Browse files
authored
[CIR] Add support for emitting VTTs and related ojects (#155721)
This adds support for emitting virtual table tables (VTTs) and construction vtables.
1 parent 2a49ebf commit 9f7bb1c

File tree

6 files changed

+333
-25
lines changed

6 files changed

+333
-25
lines changed

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ class CIRGenCXXABI {
107107
virtual void emitVTableDefinitions(CIRGenVTables &cgvt,
108108
const CXXRecordDecl *rd) = 0;
109109

110+
/// Emit any tables needed to implement virtual inheritance. For Itanium,
111+
/// this emits virtual table tables.
112+
virtual void emitVirtualInheritanceTables(const CXXRecordDecl *rd) = 0;
113+
110114
/// Returns true if the given destructor type should be emitted as a linkonce
111115
/// delegating thunk, regardless of whether the dtor is defined in this TU or
112116
/// not.

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
8585
const clang::CXXRecordDecl *nearestVBase) override;
8686
void emitVTableDefinitions(CIRGenVTables &cgvt,
8787
const CXXRecordDecl *rd) override;
88+
void emitVirtualInheritanceTables(const CXXRecordDecl *rd) override;
8889

8990
bool doStructorsInitializeVPtrs(const CXXRecordDecl *vtableClass) override {
9091
return true;
@@ -335,6 +336,13 @@ void CIRGenItaniumCXXABI::emitVTableDefinitions(CIRGenVTables &cgvt,
335336
}
336337
}
337338

339+
void CIRGenItaniumCXXABI::emitVirtualInheritanceTables(
340+
const CXXRecordDecl *rd) {
341+
CIRGenVTables &vtables = cgm.getVTables();
342+
cir::GlobalOp vtt = vtables.getAddrOfVTT(rd);
343+
vtables.emitVTTDefinition(vtt, cgm.getVTableLinkage(rd), rd);
344+
}
345+
338346
void CIRGenItaniumCXXABI::emitDestructorCall(
339347
CIRGenFunction &cgf, const CXXDestructorDecl *dd, CXXDtorType type,
340348
bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy) {

clang/lib/CIR/CodeGen/CIRGenVTables.cpp

Lines changed: 173 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "CIRGenCXXABI.h"
1616
#include "CIRGenModule.h"
1717
#include "mlir/IR/Types.h"
18+
#include "clang/AST/VTTBuilder.h"
1819
#include "clang/AST/VTableBuilder.h"
1920
#include "llvm/ADT/SmallVector.h"
2021

@@ -60,7 +61,7 @@ void CIRGenVTables::generateClassData(const CXXRecordDecl *rd) {
6061
assert(!cir::MissingFeatures::generateDebugInfo());
6162

6263
if (rd->getNumVBases())
63-
cgm.errorNYI(rd->getSourceRange(), "emitVirtualInheritanceTables");
64+
cgm.getCXXABI().emitVirtualInheritanceTables(rd);
6465

6566
cgm.getCXXABI().emitVTableDefinitions(*this, rd);
6667
}
@@ -76,12 +77,6 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
7677
assert(!cir::MissingFeatures::vtableRelativeLayout());
7778

7879
switch (component.getKind()) {
79-
case VTableComponent::CK_VCallOffset:
80-
cgm.errorNYI("getVTableComponent: VCallOffset");
81-
return mlir::Attribute();
82-
case VTableComponent::CK_VBaseOffset:
83-
cgm.errorNYI("getVTableComponent: VBaseOffset");
84-
return mlir::Attribute();
8580
case VTableComponent::CK_CompleteDtorPointer:
8681
cgm.errorNYI("getVTableComponent: CompleteDtorPointer");
8782
return mlir::Attribute();
@@ -92,6 +87,14 @@ mlir::Attribute CIRGenVTables::getVTableComponent(
9287
cgm.errorNYI("getVTableComponent: UnusedFunctionPointer");
9388
return mlir::Attribute();
9489

90+
case VTableComponent::CK_VCallOffset:
91+
return builder.getConstPtrAttr(builder.getUInt8PtrTy(),
92+
component.getVCallOffset().getQuantity());
93+
94+
case VTableComponent::CK_VBaseOffset:
95+
return builder.getConstPtrAttr(builder.getUInt8PtrTy(),
96+
component.getVBaseOffset().getQuantity());
97+
9598
case VTableComponent::CK_OffsetToTop:
9699
return builder.getConstPtrAttr(builder.getUInt8PtrTy(),
97100
component.getOffsetToTop().getQuantity());
@@ -175,6 +178,66 @@ void CIRGenVTables::createVTableInitializer(cir::GlobalOp &vtableOp,
175178
cgm.setInitializer(vtableOp, vtableAttr);
176179
}
177180

181+
cir::GlobalOp CIRGenVTables::generateConstructionVTable(
182+
const CXXRecordDecl *rd, const BaseSubobject &base, bool baseIsVirtual,
183+
cir::GlobalLinkageKind linkage, VTableAddressPointsMapTy &addressPoints) {
184+
assert(!cir::MissingFeatures::generateDebugInfo());
185+
186+
std::unique_ptr<VTableLayout> vtLayout(
187+
getItaniumVTableContext().createConstructionVTableLayout(
188+
base.getBase(), base.getBaseOffset(), baseIsVirtual, rd));
189+
190+
// Add the address points.
191+
addressPoints = vtLayout->getAddressPoints();
192+
193+
// Get the mangled construction vtable name.
194+
SmallString<256> outName;
195+
llvm::raw_svector_ostream out(outName);
196+
cast<ItaniumMangleContext>(cgm.getCXXABI().getMangleContext())
197+
.mangleCXXCtorVTable(rd, base.getBaseOffset().getQuantity(),
198+
base.getBase(), out);
199+
SmallString<256> name(outName);
200+
201+
assert(!cir::MissingFeatures::vtableRelativeLayout());
202+
203+
cir::RecordType vtType = getVTableType(*vtLayout);
204+
205+
// Construction vtable symbols are not part of the Itanium ABI, so we cannot
206+
// guarantee that they actually will be available externally. Instead, when
207+
// emitting an available_externally VTT, we provide references to an internal
208+
// linkage construction vtable. The ABI only requires complete-object vtables
209+
// to be the same for all instances of a type, not construction vtables.
210+
if (linkage == cir::GlobalLinkageKind::AvailableExternallyLinkage)
211+
linkage = cir::GlobalLinkageKind::InternalLinkage;
212+
213+
llvm::Align align = cgm.getDataLayout().getABITypeAlign(vtType);
214+
mlir::Location loc = cgm.getLoc(rd->getSourceRange());
215+
216+
// Create the variable that will hold the construction vtable.
217+
cir::GlobalOp vtable = cgm.createOrReplaceCXXRuntimeVariable(
218+
loc, name, vtType, linkage, CharUnits::fromQuantity(align));
219+
220+
// V-tables are always unnamed_addr.
221+
assert(!cir::MissingFeatures::opGlobalUnnamedAddr());
222+
223+
mlir::Attribute rtti = cgm.getAddrOfRTTIDescriptor(
224+
loc, cgm.getASTContext().getCanonicalTagType(base.getBase()));
225+
226+
// Create and set the initializer.
227+
createVTableInitializer(vtable, *vtLayout, rtti,
228+
cir::isLocalLinkage(vtable.getLinkage()));
229+
230+
// Set properties only after the initializer has been set to ensure that the
231+
// GV is treated as definition and not declaration.
232+
assert(!vtable.isDeclaration() && "Shouldn't set properties on declaration");
233+
cgm.setGVProperties(vtable, rd);
234+
235+
assert(!cir::MissingFeatures::vtableEmitMetadata());
236+
assert(!cir::MissingFeatures::vtableRelativeLayout());
237+
238+
return vtable;
239+
}
240+
178241
/// Compute the required linkage of the vtable for the given class.
179242
///
180243
/// Note that we only call this at the end of the translation unit.
@@ -226,6 +289,109 @@ cir::GlobalLinkageKind CIRGenModule::getVTableLinkage(const CXXRecordDecl *rd) {
226289
return cir::GlobalLinkageKind::ExternalLinkage;
227290
}
228291

292+
cir::GlobalOp CIRGenVTables::getAddrOfVTT(const CXXRecordDecl *rd) {
293+
assert(rd->getNumVBases() && "Only classes with virtual bases need a VTT");
294+
295+
SmallString<256> outName;
296+
llvm::raw_svector_ostream out(outName);
297+
cast<ItaniumMangleContext>(cgm.getCXXABI().getMangleContext())
298+
.mangleCXXVTT(rd, out);
299+
StringRef name = outName.str();
300+
301+
// This will also defer the definition of the VTT.
302+
(void)cgm.getCXXABI().getAddrOfVTable(rd, CharUnits());
303+
304+
VTTBuilder builder(cgm.getASTContext(), rd, /*GenerateDefinition=*/false);
305+
306+
auto arrayType = cir::ArrayType::get(cgm.getBuilder().getUInt8PtrTy(),
307+
builder.getVTTComponents().size());
308+
llvm::Align align =
309+
cgm.getDataLayout().getABITypeAlign(cgm.getBuilder().getUInt8PtrTy());
310+
cir::GlobalOp vtt = cgm.createOrReplaceCXXRuntimeVariable(
311+
cgm.getLoc(rd->getSourceRange()), name, arrayType,
312+
cir::GlobalLinkageKind::ExternalLinkage, CharUnits::fromQuantity(align));
313+
cgm.setGVProperties(vtt, rd);
314+
return vtt;
315+
}
316+
317+
static cir::GlobalOp
318+
getAddrOfVTTVTable(CIRGenVTables &cgvt, CIRGenModule &cgm,
319+
const CXXRecordDecl *mostDerivedClass,
320+
const VTTVTable &vtable, cir::GlobalLinkageKind linkage,
321+
VTableLayout::AddressPointsMapTy &addressPoints) {
322+
if (vtable.getBase() == mostDerivedClass) {
323+
assert(vtable.getBaseOffset().isZero() &&
324+
"Most derived class vtable must have a zero offset!");
325+
// This is a regular vtable.
326+
return cgm.getCXXABI().getAddrOfVTable(mostDerivedClass, CharUnits());
327+
}
328+
return cgvt.generateConstructionVTable(
329+
mostDerivedClass, vtable.getBaseSubobject(), vtable.isVirtual(), linkage,
330+
addressPoints);
331+
}
332+
333+
/// Emit the definition of the given vtable.
334+
void CIRGenVTables::emitVTTDefinition(cir::GlobalOp vttOp,
335+
cir::GlobalLinkageKind linkage,
336+
const CXXRecordDecl *rd) {
337+
VTTBuilder builder(cgm.getASTContext(), rd, /*GenerateDefinition=*/true);
338+
339+
mlir::MLIRContext *mlirContext = &cgm.getMLIRContext();
340+
341+
auto arrayType = cir::ArrayType::get(cgm.getBuilder().getUInt8PtrTy(),
342+
builder.getVTTComponents().size());
343+
344+
SmallVector<cir::GlobalOp> vtables;
345+
SmallVector<VTableAddressPointsMapTy> vtableAddressPoints;
346+
for (const VTTVTable &vtt : builder.getVTTVTables()) {
347+
vtableAddressPoints.push_back(VTableAddressPointsMapTy());
348+
vtables.push_back(getAddrOfVTTVTable(*this, cgm, rd, vtt, linkage,
349+
vtableAddressPoints.back()));
350+
}
351+
352+
SmallVector<mlir::Attribute> vttComponents;
353+
for (const VTTComponent &vttComponent : builder.getVTTComponents()) {
354+
const VTTVTable &vttVT = builder.getVTTVTables()[vttComponent.VTableIndex];
355+
cir::GlobalOp vtable = vtables[vttComponent.VTableIndex];
356+
VTableLayout::AddressPointLocation addressPoint;
357+
if (vttVT.getBase() == rd) {
358+
// Just get the address point for the regular vtable.
359+
addressPoint =
360+
getItaniumVTableContext().getVTableLayout(rd).getAddressPoint(
361+
vttComponent.VTableBase);
362+
} else {
363+
addressPoint = vtableAddressPoints[vttComponent.VTableIndex].lookup(
364+
vttComponent.VTableBase);
365+
assert(addressPoint.AddressPointIndex != 0 &&
366+
"Did not find ctor vtable address point!");
367+
}
368+
369+
mlir::Attribute indices[2] = {
370+
cgm.getBuilder().getI32IntegerAttr(addressPoint.VTableIndex),
371+
cgm.getBuilder().getI32IntegerAttr(addressPoint.AddressPointIndex),
372+
};
373+
374+
auto indicesAttr = mlir::ArrayAttr::get(mlirContext, indices);
375+
cir::GlobalViewAttr init = cgm.getBuilder().getGlobalViewAttr(
376+
cgm.getBuilder().getUInt8PtrTy(), vtable, indicesAttr);
377+
378+
vttComponents.push_back(init);
379+
}
380+
381+
auto init = cir::ConstArrayAttr::get(
382+
arrayType, mlir::ArrayAttr::get(mlirContext, vttComponents));
383+
384+
vttOp.setInitialValueAttr(init);
385+
386+
// Set the correct linkage.
387+
vttOp.setLinkage(linkage);
388+
mlir::SymbolTable::setSymbolVisibility(
389+
vttOp, CIRGenModule::getMLIRVisibility(vttOp));
390+
391+
if (cgm.supportsCOMDAT() && vttOp.isWeakForLinker())
392+
vttOp.setComdat(true);
393+
}
394+
229395
void CIRGenVTables::emitThunks(GlobalDecl gd) {
230396
const CXXMethodDecl *md =
231397
cast<CXXMethodDecl>(gd.getDecl())->getCanonicalDecl();

clang/lib/CIR/CodeGen/CIRGenVTables.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ class CIRGenVTables {
3030

3131
clang::VTableContextBase *vtContext;
3232

33+
/// Address points for a single vtable.
34+
using VTableAddressPointsMapTy = clang::VTableLayout::AddressPointsMapTy;
35+
3336
mlir::Attribute
3437
getVTableComponent(const VTableLayout &layout, unsigned componentIndex,
3538
mlir::Attribute rtti, unsigned &nextVTableThunkIndex,
@@ -55,6 +58,19 @@ class CIRGenVTables {
5558
return *llvm::cast<clang::ItaniumVTableContext>(vtContext);
5659
}
5760

61+
/// Generate a construction vtable for the given base subobject.
62+
cir::GlobalOp
63+
generateConstructionVTable(const CXXRecordDecl *rd, const BaseSubobject &base,
64+
bool baseIsVirtual, cir::GlobalLinkageKind linkage,
65+
VTableAddressPointsMapTy &addressPoints);
66+
67+
/// Get the address of the VTT for the given record decl.
68+
cir::GlobalOp getAddrOfVTT(const CXXRecordDecl *rd);
69+
70+
/// Emit the definition of the given vtable.
71+
void emitVTTDefinition(cir::GlobalOp vttOp, cir::GlobalLinkageKind linkage,
72+
const CXXRecordDecl *rd);
73+
5874
/// Emit the associated thunks for the given global decl.
5975
void emitThunks(GlobalDecl gd);
6076

clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,6 @@ void CIRDataLayout::reset(mlir::DataLayoutSpecInterface spec) {
2424
}
2525

2626
llvm::Align CIRDataLayout::getAlignment(mlir::Type ty, bool useABIAlign) const {
27-
if (auto recTy = llvm::dyn_cast<cir::RecordType>(ty)) {
28-
// Packed record types always have an ABI alignment of one.
29-
if (recTy && recTy.getPacked() && useABIAlign)
30-
return llvm::Align(1);
31-
32-
// Get the layout annotation... which is lazily created on demand.
33-
llvm_unreachable("getAlignment()) for record type is not implemented");
34-
}
35-
3627
// FIXME(cir): This does not account for differnt address spaces, and relies
3728
// on CIR's data layout to give the proper alignment.
3829
assert(!cir::MissingFeatures::addressSpace());

0 commit comments

Comments
 (0)