Skip to content

Commit e7c9f2d

Browse files
authored
[CIR] Add initial support for virtual base classes (#155534)
This adds support for declaring a class with a virtual base class and initializing the vptr in the constructor. This does not yet handle constructors that require a virtual table table (VTT) implicit argument.
1 parent 9a0a56e commit e7c9f2d

File tree

8 files changed

+284
-72
lines changed

8 files changed

+284
-72
lines changed

clang/lib/CIR/CodeGen/CIRGenCXXABI.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ class CIRGenCXXABI {
3737

3838
void setCXXABIThisValue(CIRGenFunction &cgf, mlir::Value thisPtr);
3939

40+
/// Emit the code to initialize hidden members required to handle virtual
41+
/// inheritance, if needed by the ABI.
42+
virtual void
43+
initializeHiddenVirtualInheritanceMembers(CIRGenFunction &cgf,
44+
const CXXRecordDecl *rd) {}
45+
4046
/// Emit a single constructor/destructor with the gen type from a C++
4147
/// constructor/destructor Decl.
4248
virtual void emitCXXStructor(clang::GlobalDecl gd) = 0;

clang/lib/CIR/CodeGen/CIRGenClass.cpp

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -222,13 +222,7 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
222222

223223
const CXXRecordDecl *classDecl = cd->getParent();
224224

225-
// This code doesn't use range-based iteration because we may need to emit
226-
// code between the virtual base initializers and the non-virtual base or
227-
// between the non-virtual base initializers and the member initializers.
228-
CXXConstructorDecl::init_const_iterator b = cd->init_begin(),
229-
e = cd->init_end();
230-
231-
// Virtual base initializers first, if any. They aren't needed if:
225+
// Virtual base initializers aren't needed if:
232226
// - This is a base ctor variant
233227
// - There are no vbases
234228
// - The class is abstract, so a complete object of it cannot be constructed
@@ -238,31 +232,60 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
238232
bool constructVBases = ctorType != Ctor_Base &&
239233
classDecl->getNumVBases() != 0 &&
240234
!classDecl->isAbstract();
241-
if (constructVBases) {
242-
cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: virtual base");
243-
return;
244-
}
245-
246-
const mlir::Value oldThisValue = cxxThisValue;
247-
if (!constructVBases && b != e && (*b)->isBaseInitializer() &&
248-
(*b)->isBaseVirtual()) {
235+
if (constructVBases &&
236+
!cgm.getTarget().getCXXABI().hasConstructorVariants()) {
249237
cgm.errorNYI(cd->getSourceRange(),
250-
"emitCtorPrologue: virtual base initializer");
238+
"emitCtorPrologue: virtual base without variants");
251239
return;
252240
}
253241

254-
// Handle non-virtual base initializers.
255-
for (; b != e && (*b)->isBaseInitializer(); b++) {
256-
assert(!(*b)->isBaseVirtual());
242+
// Create three separate ranges for the different types of initializers.
243+
auto allInits = cd->inits();
244+
245+
// Find the boundaries between the three groups.
246+
auto virtualBaseEnd = std::find_if(
247+
allInits.begin(), allInits.end(), [](const CXXCtorInitializer *Init) {
248+
return !(Init->isBaseInitializer() && Init->isBaseVirtual());
249+
});
250+
251+
auto nonVirtualBaseEnd = std::find_if(virtualBaseEnd, allInits.end(),
252+
[](const CXXCtorInitializer *Init) {
253+
return !Init->isBaseInitializer();
254+
});
255+
256+
// Create the three ranges.
257+
auto virtualBaseInits = llvm::make_range(allInits.begin(), virtualBaseEnd);
258+
auto nonVirtualBaseInits =
259+
llvm::make_range(virtualBaseEnd, nonVirtualBaseEnd);
260+
auto memberInits = llvm::make_range(nonVirtualBaseEnd, allInits.end());
261+
262+
const mlir::Value oldThisValue = cxxThisValue;
257263

264+
auto emitInitializer = [&](CXXCtorInitializer *baseInit) {
258265
if (cgm.getCodeGenOpts().StrictVTablePointers &&
259266
cgm.getCodeGenOpts().OptimizationLevel > 0 &&
260-
isInitializerOfDynamicClass(*b)) {
267+
isInitializerOfDynamicClass(baseInit)) {
268+
// It's OK to continue after emitting the error here. The missing code
269+
// just "launders" the 'this' pointer.
261270
cgm.errorNYI(cd->getSourceRange(),
262-
"emitCtorPrologue: strict vtable pointers");
263-
return;
271+
"emitCtorPrologue: strict vtable pointers for vbase");
264272
}
265-
emitBaseInitializer(getLoc(cd->getBeginLoc()), classDecl, *b);
273+
emitBaseInitializer(getLoc(cd->getBeginLoc()), classDecl, baseInit);
274+
};
275+
276+
// Process virtual base initializers.
277+
for (CXXCtorInitializer *virtualBaseInit : virtualBaseInits) {
278+
if (!constructVBases)
279+
continue;
280+
emitInitializer(virtualBaseInit);
281+
}
282+
283+
assert(!cir::MissingFeatures::msabi());
284+
285+
// Then, non-virtual base initializers.
286+
for (CXXCtorInitializer *nonVirtualBaseInit : nonVirtualBaseInits) {
287+
assert(!nonVirtualBaseInit->isBaseVirtual());
288+
emitInitializer(nonVirtualBaseInit);
266289
}
267290

268291
cxxThisValue = oldThisValue;
@@ -276,8 +299,7 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
276299
// lowering or optimization phases to keep the memory accesses more
277300
// explicit. For now, we don't insert memcpy at all.
278301
assert(!cir::MissingFeatures::ctorMemcpyizer());
279-
for (; b != e; b++) {
280-
CXXCtorInitializer *member = (*b);
302+
for (CXXCtorInitializer *member : memberInits) {
281303
assert(!member->isBaseInitializer());
282304
assert(member->isAnyMemberInitializer() &&
283305
"Delegating initializer on non-delegating constructor");
@@ -370,7 +392,7 @@ void CIRGenFunction::initializeVTablePointers(mlir::Location loc,
370392
initializeVTablePointer(loc, vptr);
371393

372394
if (rd->getNumVBases())
373-
cgm.errorNYI(loc, "initializeVTablePointers: virtual base");
395+
cgm.getCXXABI().initializeHiddenVirtualInheritanceMembers(*this, rd);
374396
}
375397

376398
CIRGenFunction::VPtrsVector
@@ -418,8 +440,17 @@ void CIRGenFunction::getVTablePointers(BaseSubobject base,
418440
const CXXRecordDecl *nextBaseDecl;
419441

420442
if (nextBase.isVirtual()) {
421-
cgm.errorNYI(rd->getSourceRange(), "getVTablePointers: virtual base");
422-
return;
443+
// Check if we've visited this virtual base before.
444+
if (!vbases.insert(baseDecl).second)
445+
continue;
446+
447+
const ASTRecordLayout &layout =
448+
getContext().getASTRecordLayout(vtableClass);
449+
450+
nextBaseDecl = nearestVBase;
451+
baseOffset = layout.getVBaseClassOffset(baseDecl);
452+
baseOffsetFromNearestVBase = CharUnits::Zero();
453+
baseDeclIsNonVirtualPrimaryBase = false;
423454
} else {
424455
const ASTRecordLayout &layout = getContext().getASTRecordLayout(rd);
425456

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1972,12 +1972,8 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
19721972
delegating = true;
19731973
break;
19741974
case CXXConstructionKind::VirtualBase:
1975-
// This should just set 'forVirtualBase' to true and fall through, but
1976-
// virtual base class support is otherwise missing, so this needs to wait
1977-
// until it can be tested.
1978-
cgm.errorNYI(e->getSourceRange(),
1979-
"emitCXXConstructExpr: virtual base constructor");
1980-
return;
1975+
forVirtualBase = true;
1976+
[[fallthrough]];
19811977
case CXXConstructionKind::NonVirtualBase:
19821978
type = Ctor_Base;
19831979
break;

clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,10 @@ mlir::Value CIRGenItaniumCXXABI::getVTableAddressPointInStructor(
487487
CIRGenFunction &cgf, const clang::CXXRecordDecl *vtableClass,
488488
clang::BaseSubobject base, const clang::CXXRecordDecl *nearestVBase) {
489489

490-
if (base.getBase()->getNumVBases()) {
490+
if ((base.getBase()->getNumVBases() || nearestVBase != nullptr) &&
491+
needsVTTParameter(cgf.curGD)) {
491492
cgm.errorNYI(cgf.curFuncDecl->getLocation(),
492-
"getVTableAddressPointInStructor: virtual base");
493+
"getVTableAddressPointInStructorWithVTT");
493494
}
494495
return getVTableAddressPoint(base, vtableClass);
495496
}

clang/lib/CIR/CodeGen/CIRGenRecordLayout.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,10 @@ class CIRGenRecordLayout {
141141
// for both virtual and non-virtual bases.
142142
llvm::DenseMap<const clang::CXXRecordDecl *, unsigned> nonVirtualBases;
143143

144+
/// Map from virtual bases to their field index in the complete object.
145+
llvm::DenseMap<const clang::CXXRecordDecl *, unsigned>
146+
completeObjectVirtualBases;
147+
144148
/// Map from (bit-field) record field to the corresponding CIR record type
145149
/// field no. This info is populated by record builder.
146150
llvm::DenseMap<const clang::FieldDecl *, CIRGenBitFieldInfo> bitFields;

0 commit comments

Comments
 (0)