Skip to content

Commit dc7ae0c

Browse files
committed
[CIR] Add initial support for virtual base classes
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 5d54d34 commit dc7ae0c

File tree

8 files changed

+287
-63
lines changed

8 files changed

+287
-63
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: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -238,31 +238,44 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
238238
bool constructVBases = ctorType != Ctor_Base &&
239239
classDecl->getNumVBases() != 0 &&
240240
!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()) {
241+
if (constructVBases &&
242+
!cgm.getTarget().getCXXABI().hasConstructorVariants()) {
249243
cgm.errorNYI(cd->getSourceRange(),
250-
"emitCtorPrologue: virtual base initializer");
244+
"emitCtorPrologue: virtual base without variants");
251245
return;
252246
}
253247

254-
// Handle non-virtual base initializers.
255-
for (; b != e && (*b)->isBaseInitializer(); b++) {
256-
assert(!(*b)->isBaseVirtual());
248+
const mlir::Value oldThisValue = cxxThisValue;
257249

250+
// Initialize virtual bases.
251+
auto emitInitializer = [&](CXXCtorInitializer *baseInit) {
258252
if (cgm.getCodeGenOpts().StrictVTablePointers &&
259253
cgm.getCodeGenOpts().OptimizationLevel > 0 &&
260-
isInitializerOfDynamicClass(*b)) {
254+
isInitializerOfDynamicClass(baseInit)) {
255+
// It's OK to continue after emitting the error here. The missing code
256+
// just "launders" the 'this' pointer.
261257
cgm.errorNYI(cd->getSourceRange(),
262-
"emitCtorPrologue: strict vtable pointers");
263-
return;
258+
"emitCtorPrologue: strict vtable pointers for vbase");
264259
}
265-
emitBaseInitializer(getLoc(cd->getBeginLoc()), classDecl, *b);
260+
emitBaseInitializer(getLoc(cd->getBeginLoc()), classDecl, baseInit);
261+
};
262+
263+
for (; b != e && (*b)->isBaseInitializer() && (*b)->isBaseVirtual(); b++) {
264+
if (!constructVBases)
265+
continue;
266+
emitInitializer(*b);
267+
}
268+
269+
// The loop above and the loop below could obviously be merged in their
270+
// current form, but when we implement support for the MS C++ ABI, we will
271+
// need to insert a branch after the last virtual base initializer, so
272+
// separate loops will be useful then. The missing code is covered by the
273+
// "virtual base without variants" diagnostic above.
274+
275+
// Handle non-virtual base initializers.
276+
for (; b != e && (*b)->isBaseInitializer(); b++) {
277+
assert(!(*b)->isBaseVirtual());
278+
emitInitializer(*b);
266279
}
267280

268281
cxxThisValue = oldThisValue;
@@ -370,7 +383,7 @@ void CIRGenFunction::initializeVTablePointers(mlir::Location loc,
370383
initializeVTablePointer(loc, vptr);
371384

372385
if (rd->getNumVBases())
373-
cgm.errorNYI(loc, "initializeVTablePointers: virtual base");
386+
cgm.getCXXABI().initializeHiddenVirtualInheritanceMembers(*this, rd);
374387
}
375388

376389
CIRGenFunction::VPtrsVector
@@ -418,8 +431,17 @@ void CIRGenFunction::getVTablePointers(BaseSubobject base,
418431
const CXXRecordDecl *nextBaseDecl;
419432

420433
if (nextBase.isVirtual()) {
421-
cgm.errorNYI(rd->getSourceRange(), "getVTablePointers: virtual base");
422-
return;
434+
// Check if we've visited this virtual base before.
435+
if (!vbases.insert(baseDecl).second)
436+
continue;
437+
438+
const ASTRecordLayout &layout =
439+
getContext().getASTRecordLayout(vtableClass);
440+
441+
nextBaseDecl = nearestVBase;
442+
baseOffset = layout.getVBaseClassOffset(baseDecl);
443+
baseOffsetFromNearestVBase = CharUnits::Zero();
444+
baseDeclIsNonVirtualPrimaryBase = false;
423445
} else {
424446
const ASTRecordLayout &layout = getContext().getASTRecordLayout(rd);
425447

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)