Skip to content

Commit 7f195b3

Browse files
authored
[CIR] Initialize vptr in dynamic classes (#152574)
This adds support for initializing the vptr member of a dynamic class in the constructor of that class. This does not include support for lowering the `cir.vtable.address_point` operation to the LLVM dialect. That handling will be added in a follow-up patch.
1 parent 7f22f5b commit 7f195b3

File tree

13 files changed

+447
-16
lines changed

13 files changed

+447
-16
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ struct MissingFeatures {
192192
static bool constantFoldSwitchStatement() { return false; }
193193
static bool constructABIArgDirectExtend() { return false; }
194194
static bool coverageMapping() { return false; }
195+
static bool createInvariantGroup() { return false; }
195196
static bool createProfileWeightsForLoop() { return false; }
196197
static bool ctorMemcpyizer() { return false; }
197198
static bool cudaSupport() { return false; }
@@ -263,6 +264,7 @@ struct MissingFeatures {
263264
static bool appleKext() { return false; }
264265
static bool dtorCleanups() { return false; }
265266
static bool vtableInitialization() { return false; }
267+
static bool vtableRelativeLayout() { return false; }
266268
static bool msvcBuiltins() { return false; }
267269
static bool vlas() { return false; }
268270

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,17 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
244244
}
245245
bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); }
246246

247+
// Fetch the type representing a pointer to unsigned int8 values.
248+
cir::PointerType getUInt8PtrTy() { return typeCache.UInt8PtrTy; }
249+
250+
/// Get a CIR anonymous record type.
251+
cir::RecordType getAnonRecordTy(llvm::ArrayRef<mlir::Type> members,
252+
bool packed = false, bool padded = false) {
253+
assert(!cir::MissingFeatures::astRecordDeclAttr());
254+
auto kind = cir::RecordType::RecordKind::Struct;
255+
return getType<cir::RecordType>(members, packed, padded, kind);
256+
}
257+
247258
//
248259
// Constant creation helpers
249260
// -------------------------

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ class CIRGenCXXABI {
8080
bool forVirtualBase, bool delegating,
8181
Address thisAddr, QualType thisTy) = 0;
8282

83+
/// Checks if ABI requires extra virtual offset for vtable field.
84+
virtual bool
85+
isVirtualOffsetNeededForVTableField(CIRGenFunction &cgf,
86+
CIRGenFunction::VPtr vptr) = 0;
87+
8388
/// Returns true if the given destructor type should be emitted as a linkonce
8489
/// delegating thunk, regardless of whether the dtor is defined in this TU or
8590
/// not.
@@ -90,6 +95,26 @@ class CIRGenCXXABI {
9095
getCXXDestructorLinkage(GVALinkage linkage, const CXXDestructorDecl *dtor,
9196
CXXDtorType dt) const;
9297

98+
/// Get the address of the vtable for the given record decl which should be
99+
/// used for the vptr at the given offset in RD.
100+
virtual cir::GlobalOp getAddrOfVTable(const CXXRecordDecl *rd,
101+
CharUnits vptrOffset) = 0;
102+
103+
/// Get the address point of the vtable for the given base subobject.
104+
virtual mlir::Value
105+
getVTableAddressPoint(BaseSubobject base,
106+
const CXXRecordDecl *vtableClass) = 0;
107+
108+
/// Get the address point of the vtable for the given base subobject while
109+
/// building a constructor or a destructor.
110+
virtual mlir::Value getVTableAddressPointInStructor(
111+
CIRGenFunction &cgf, const CXXRecordDecl *vtableClass, BaseSubobject base,
112+
const CXXRecordDecl *nearestVBase) = 0;
113+
114+
/// Checks if ABI requires to initialize vptrs for given dynamic class.
115+
virtual bool
116+
doStructorsInitializeVPtrs(const clang::CXXRecordDecl *vtableClass) = 0;
117+
93118
/// Returns true if the given constructor or destructor is one of the kinds
94119
/// that the ABI says returns 'this' (only applies when called non-virtually
95120
/// for destructors).

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 92 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -199,10 +199,6 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
199199
return;
200200
}
201201

202-
// If there are no member initializers, we can just return.
203-
if (cd->getNumCtorInitializers() == 0)
204-
return;
205-
206202
const CXXRecordDecl *classDecl = cd->getParent();
207203

208204
// This code doesn't use range-based iteration because we may need to emit
@@ -227,7 +223,8 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
227223
}
228224

229225
const mlir::Value oldThisValue = cxxThisValue;
230-
if (!constructVBases && (*b)->isBaseInitializer() && (*b)->isBaseVirtual()) {
226+
if (!constructVBases && b != e && (*b)->isBaseInitializer() &&
227+
(*b)->isBaseVirtual()) {
231228
cgm.errorNYI(cd->getSourceRange(),
232229
"emitCtorPrologue: virtual base initializer");
233230
return;
@@ -249,11 +246,7 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
249246

250247
cxxThisValue = oldThisValue;
251248

252-
if (classDecl->isDynamicClass()) {
253-
cgm.errorNYI(cd->getSourceRange(),
254-
"emitCtorPrologue: initialize vtable pointers");
255-
return;
256-
}
249+
initializeVTablePointers(getLoc(cd->getBeginLoc()), classDecl);
257250

258251
// Finally, initialize class members.
259252
FieldConstructionScope fcs(*this, loadCXXThisAddress());
@@ -271,6 +264,95 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
271264
}
272265
}
273266

267+
void CIRGenFunction::initializeVTablePointer(mlir::Location loc,
268+
const VPtr &vptr) {
269+
// Compute the address point.
270+
mlir::Value vtableAddressPoint =
271+
cgm.getCXXABI().getVTableAddressPointInStructor(
272+
*this, vptr.vtableClass, vptr.base, vptr.nearestVBase);
273+
274+
if (!vtableAddressPoint)
275+
return;
276+
277+
// Compute where to store the address point.
278+
mlir::Value virtualOffset{};
279+
CharUnits nonVirtualOffset = CharUnits::Zero();
280+
281+
mlir::Type baseValueTy;
282+
if (cgm.getCXXABI().isVirtualOffsetNeededForVTableField(*this, vptr)) {
283+
cgm.errorNYI(loc, "initializeVTablePointer: virtual offset for vtable");
284+
} else {
285+
// We can just use the base offset in the complete class.
286+
nonVirtualOffset = vptr.base.getBaseOffset();
287+
baseValueTy = convertType(getContext().getTagDeclType(vptr.base.getBase()));
288+
}
289+
290+
// Apply the offsets.
291+
Address vtableField = loadCXXThisAddress();
292+
if (!nonVirtualOffset.isZero() || virtualOffset) {
293+
cgm.errorNYI(loc,
294+
"initializeVTablePointer: non-virtual and virtual offset");
295+
}
296+
297+
// Finally, store the address point. Use the same CIR types as the field.
298+
//
299+
// vtable field is derived from `this` pointer, therefore they should be in
300+
// the same addr space.
301+
assert(!cir::MissingFeatures::addressSpace());
302+
// TODO(cir): This should be cir.vtable.get_vptr.
303+
vtableField = builder.createElementBitCast(loc, vtableField,
304+
vtableAddressPoint.getType());
305+
builder.createStore(loc, vtableAddressPoint, vtableField);
306+
assert(!cir::MissingFeatures::opTBAA());
307+
assert(!cir::MissingFeatures::createInvariantGroup());
308+
}
309+
310+
void CIRGenFunction::initializeVTablePointers(mlir::Location loc,
311+
const CXXRecordDecl *rd) {
312+
// Ignore classes without a vtable.
313+
if (!rd->isDynamicClass())
314+
return;
315+
316+
// Initialize the vtable pointers for this class and all of its bases.
317+
if (cgm.getCXXABI().doStructorsInitializeVPtrs(rd))
318+
for (const auto &vptr : getVTablePointers(rd))
319+
initializeVTablePointer(loc, vptr);
320+
321+
if (rd->getNumVBases())
322+
cgm.errorNYI(loc, "initializeVTablePointers: virtual base");
323+
}
324+
325+
CIRGenFunction::VPtrsVector
326+
CIRGenFunction::getVTablePointers(const CXXRecordDecl *vtableClass) {
327+
CIRGenFunction::VPtrsVector vptrsResult;
328+
getVTablePointers(BaseSubobject(vtableClass, CharUnits::Zero()),
329+
/*NearestVBase=*/nullptr,
330+
/*OffsetFromNearestVBase=*/CharUnits::Zero(),
331+
/*BaseIsNonVirtualPrimaryBase=*/false, vtableClass,
332+
vptrsResult);
333+
return vptrsResult;
334+
}
335+
336+
void CIRGenFunction::getVTablePointers(BaseSubobject base,
337+
const CXXRecordDecl *nearestVBase,
338+
CharUnits offsetFromNearestVBase,
339+
bool baseIsNonVirtualPrimaryBase,
340+
const CXXRecordDecl *vtableClass,
341+
VPtrsVector &vptrs) {
342+
// If this base is a non-virtual primary base the address point has already
343+
// been set.
344+
if (!baseIsNonVirtualPrimaryBase) {
345+
// Initialize the vtable pointer for this base.
346+
VPtr vptr = {base, nearestVBase, offsetFromNearestVBase, vtableClass};
347+
vptrs.push_back(vptr);
348+
}
349+
350+
const CXXRecordDecl *rd = base.getBase();
351+
352+
if (rd->getNumBases())
353+
cgm.errorNYI(rd->getSourceRange(), "getVTablePointers: traverse bases");
354+
}
355+
274356
Address CIRGenFunction::loadCXXThisAddress() {
275357
assert(curFuncDecl && "loading 'this' without a func declaration?");
276358
assert(isa<CXXMethodDecl>(curFuncDecl));

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "Address.h"
2424

2525
#include "clang/AST/ASTContext.h"
26+
#include "clang/AST/BaseSubobject.h"
2627
#include "clang/AST/CharUnits.h"
2728
#include "clang/AST/Decl.h"
2829
#include "clang/AST/Stmt.h"
@@ -509,6 +510,25 @@ class CIRGenFunction : public CIRGenTypeCache {
509510
static bool
510511
isConstructorDelegationValid(const clang::CXXConstructorDecl *ctor);
511512

513+
struct VPtr {
514+
clang::BaseSubobject base;
515+
const clang::CXXRecordDecl *nearestVBase;
516+
clang::CharUnits offsetFromNearestVBase;
517+
const clang::CXXRecordDecl *vtableClass;
518+
};
519+
520+
using VPtrsVector = llvm::SmallVector<VPtr, 4>;
521+
VPtrsVector getVTablePointers(const clang::CXXRecordDecl *vtableClass);
522+
void getVTablePointers(clang::BaseSubobject base,
523+
const clang::CXXRecordDecl *nearestVBase,
524+
clang::CharUnits offsetFromNearestVBase,
525+
bool baseIsNonVirtualPrimaryBase,
526+
const clang::CXXRecordDecl *vtableClass,
527+
VPtrsVector &vptrs);
528+
/// Return the Value of the vtable pointer member pointed to by thisAddr.
529+
mlir::Value getVTablePtr(mlir::Location loc, Address thisAddr,
530+
const clang::CXXRecordDecl *vtableClass);
531+
512532
/// A scope within which we are constructing the fields of an object which
513533
/// might use a CXXDefaultInitExpr. This stashes away a 'this' value to use if
514534
/// we need to evaluate the CXXDefaultInitExpr within the evaluation.
@@ -557,6 +577,10 @@ class CIRGenFunction : public CIRGenTypeCache {
557577
return LValue::makeAddr(addr, ty, baseInfo);
558578
}
559579

580+
void initializeVTablePointers(mlir::Location loc,
581+
const clang::CXXRecordDecl *rd);
582+
void initializeVTablePointer(mlir::Location loc, const VPtr &vptr);
583+
560584
/// Return the address of a local variable.
561585
Address getAddrOfLocalVar(const clang::VarDecl *vd) {
562586
auto it = localDeclMap.find(vd);

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "clang/AST/ExprCXX.h"
2424
#include "clang/AST/GlobalDecl.h"
25+
#include "clang/AST/VTableBuilder.h"
2526
#include "clang/CIR/MissingFeatures.h"
2627
#include "llvm/Support/ErrorHandling.h"
2728

@@ -31,6 +32,10 @@ using namespace clang::CIRGen;
3132
namespace {
3233

3334
class CIRGenItaniumCXXABI : public CIRGenCXXABI {
35+
protected:
36+
/// All the vtables which have been defined.
37+
llvm::DenseMap<const CXXRecordDecl *, cir::GlobalOp> vtables;
38+
3439
public:
3540
CIRGenItaniumCXXABI(CIRGenModule &cgm) : CIRGenCXXABI(cgm) {
3641
assert(!cir::MissingFeatures::cxxabiUseARMMethodPtrABI());
@@ -58,6 +63,24 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
5863
// emitted with external linkage or as linkonce if they are inline and used.
5964
return false;
6065
}
66+
67+
bool isVirtualOffsetNeededForVTableField(CIRGenFunction &cgf,
68+
CIRGenFunction::VPtr vptr) override;
69+
70+
cir::GlobalOp getAddrOfVTable(const CXXRecordDecl *rd,
71+
CharUnits vptrOffset) override;
72+
73+
mlir::Value getVTableAddressPoint(BaseSubobject base,
74+
const CXXRecordDecl *vtableClass) override;
75+
76+
mlir::Value getVTableAddressPointInStructor(
77+
CIRGenFunction &cgf, const clang::CXXRecordDecl *vtableClass,
78+
clang::BaseSubobject base,
79+
const clang::CXXRecordDecl *nearestVBase) override;
80+
81+
bool doStructorsInitializeVPtrs(const CXXRecordDecl *vtableClass) override {
82+
return true;
83+
}
6184
};
6285

6386
} // namespace
@@ -278,3 +301,91 @@ CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) {
278301
llvm_unreachable("bad or NYI ABI kind");
279302
}
280303
}
304+
305+
cir::GlobalOp CIRGenItaniumCXXABI::getAddrOfVTable(const CXXRecordDecl *rd,
306+
CharUnits vptrOffset) {
307+
assert(vptrOffset.isZero() && "Itanium ABI only supports zero vptr offsets");
308+
cir::GlobalOp &vtable = vtables[rd];
309+
if (vtable)
310+
return vtable;
311+
312+
// Queue up this vtable for possible deferred emission.
313+
assert(!cir::MissingFeatures::deferredVtables());
314+
315+
SmallString<256> name;
316+
llvm::raw_svector_ostream out(name);
317+
getMangleContext().mangleCXXVTable(rd, out);
318+
319+
const VTableLayout &vtLayout =
320+
cgm.getItaniumVTableContext().getVTableLayout(rd);
321+
mlir::Type vtableType = cgm.getVTables().getVTableType(vtLayout);
322+
323+
// Use pointer alignment for the vtable. Otherwise we would align them based
324+
// on the size of the initializer which doesn't make sense as only single
325+
// values are read.
326+
unsigned ptrAlign = cgm.getItaniumVTableContext().isRelativeLayout()
327+
? 32
328+
: cgm.getTarget().getPointerAlign(LangAS::Default);
329+
330+
vtable = cgm.createOrReplaceCXXRuntimeVariable(
331+
cgm.getLoc(rd->getSourceRange()), name, vtableType,
332+
cir::GlobalLinkageKind::ExternalLinkage,
333+
cgm.getASTContext().toCharUnitsFromBits(ptrAlign));
334+
// LLVM codegen handles unnamedAddr
335+
assert(!cir::MissingFeatures::opGlobalUnnamedAddr());
336+
337+
// In MS C++ if you have a class with virtual functions in which you are using
338+
// selective member import/export, then all virtual functions must be exported
339+
// unless they are inline, otherwise a link error will result. To match this
340+
// behavior, for such classes, we dllimport the vtable if it is defined
341+
// externally and all the non-inline virtual methods are marked dllimport, and
342+
// we dllexport the vtable if it is defined in this TU and all the non-inline
343+
// virtual methods are marked dllexport.
344+
if (cgm.getTarget().hasPS4DLLImportExport())
345+
cgm.errorNYI(rd->getSourceRange(),
346+
"getAddrOfVTable: PS4 DLL import/export");
347+
348+
cgm.setGVProperties(vtable, rd);
349+
return vtable;
350+
}
351+
352+
mlir::Value
353+
CIRGenItaniumCXXABI::getVTableAddressPoint(BaseSubobject base,
354+
const CXXRecordDecl *vtableClass) {
355+
cir::GlobalOp vtable = getAddrOfVTable(vtableClass, CharUnits());
356+
357+
// Find the appropriate vtable within the vtable group, and the address point
358+
// within that vtable.
359+
VTableLayout::AddressPointLocation addressPoint =
360+
cgm.getItaniumVTableContext()
361+
.getVTableLayout(vtableClass)
362+
.getAddressPoint(base);
363+
364+
mlir::OpBuilder &builder = cgm.getBuilder();
365+
auto vtablePtrTy = cir::VPtrType::get(builder.getContext());
366+
367+
return builder.create<cir::VTableAddrPointOp>(
368+
cgm.getLoc(vtableClass->getSourceRange()), vtablePtrTy,
369+
mlir::FlatSymbolRefAttr::get(vtable.getSymNameAttr()),
370+
cir::AddressPointAttr::get(cgm.getBuilder().getContext(),
371+
addressPoint.VTableIndex,
372+
addressPoint.AddressPointIndex));
373+
}
374+
375+
mlir::Value CIRGenItaniumCXXABI::getVTableAddressPointInStructor(
376+
CIRGenFunction &cgf, const clang::CXXRecordDecl *vtableClass,
377+
clang::BaseSubobject base, const clang::CXXRecordDecl *nearestVBase) {
378+
379+
if (base.getBase()->getNumVBases()) {
380+
cgm.errorNYI(cgf.curFuncDecl->getLocation(),
381+
"getVTableAddressPointInStructor: virtual base");
382+
}
383+
return getVTableAddressPoint(base, vtableClass);
384+
}
385+
386+
bool CIRGenItaniumCXXABI::isVirtualOffsetNeededForVTableField(
387+
CIRGenFunction &cgf, CIRGenFunction::VPtr vptr) {
388+
if (vptr.nearestVBase == nullptr)
389+
return false;
390+
return needsVTTParameter(cgf.curGD);
391+
}

0 commit comments

Comments
 (0)