-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[CIR] Add initial support for virtual base classes #155534
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
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.
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) ChangesThis 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. Patch is 26.14 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/155534.diff 8 Files Affected:
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index b5f2e1a067274..df7ffbb4a2759 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -37,6 +37,12 @@ class CIRGenCXXABI {
void setCXXABIThisValue(CIRGenFunction &cgf, mlir::Value thisPtr);
+ /// Emit the code to initialize hidden members required to handle virtual
+ /// inheritance, if needed by the ABI.
+ virtual void
+ initializeHiddenVirtualInheritanceMembers(CIRGenFunction &cgf,
+ const CXXRecordDecl *rd) {}
+
/// Emit a single constructor/destructor with the gen type from a C++
/// constructor/destructor Decl.
virtual void emitCXXStructor(clang::GlobalDecl gd) = 0;
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 9106e5e4824ff..ac923635192aa 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -238,31 +238,44 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
bool constructVBases = ctorType != Ctor_Base &&
classDecl->getNumVBases() != 0 &&
!classDecl->isAbstract();
- if (constructVBases) {
- cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: virtual base");
- return;
- }
-
- const mlir::Value oldThisValue = cxxThisValue;
- if (!constructVBases && b != e && (*b)->isBaseInitializer() &&
- (*b)->isBaseVirtual()) {
+ if (constructVBases &&
+ !cgm.getTarget().getCXXABI().hasConstructorVariants()) {
cgm.errorNYI(cd->getSourceRange(),
- "emitCtorPrologue: virtual base initializer");
+ "emitCtorPrologue: virtual base without variants");
return;
}
- // Handle non-virtual base initializers.
- for (; b != e && (*b)->isBaseInitializer(); b++) {
- assert(!(*b)->isBaseVirtual());
+ const mlir::Value oldThisValue = cxxThisValue;
+ // Initialize virtual bases.
+ auto emitInitializer = [&](CXXCtorInitializer *baseInit) {
if (cgm.getCodeGenOpts().StrictVTablePointers &&
cgm.getCodeGenOpts().OptimizationLevel > 0 &&
- isInitializerOfDynamicClass(*b)) {
+ isInitializerOfDynamicClass(baseInit)) {
+ // It's OK to continue after emitting the error here. The missing code
+ // just "launders" the 'this' pointer.
cgm.errorNYI(cd->getSourceRange(),
- "emitCtorPrologue: strict vtable pointers");
- return;
+ "emitCtorPrologue: strict vtable pointers for vbase");
}
- emitBaseInitializer(getLoc(cd->getBeginLoc()), classDecl, *b);
+ emitBaseInitializer(getLoc(cd->getBeginLoc()), classDecl, baseInit);
+ };
+
+ for (; b != e && (*b)->isBaseInitializer() && (*b)->isBaseVirtual(); b++) {
+ if (!constructVBases)
+ continue;
+ emitInitializer(*b);
+ }
+
+ // The loop above and the loop below could obviously be merged in their
+ // current form, but when we implement support for the MS C++ ABI, we will
+ // need to insert a branch after the last virtual base initializer, so
+ // separate loops will be useful then. The missing code is covered by the
+ // "virtual base without variants" diagnostic above.
+
+ // Handle non-virtual base initializers.
+ for (; b != e && (*b)->isBaseInitializer(); b++) {
+ assert(!(*b)->isBaseVirtual());
+ emitInitializer(*b);
}
cxxThisValue = oldThisValue;
@@ -370,7 +383,7 @@ void CIRGenFunction::initializeVTablePointers(mlir::Location loc,
initializeVTablePointer(loc, vptr);
if (rd->getNumVBases())
- cgm.errorNYI(loc, "initializeVTablePointers: virtual base");
+ cgm.getCXXABI().initializeHiddenVirtualInheritanceMembers(*this, rd);
}
CIRGenFunction::VPtrsVector
@@ -418,8 +431,17 @@ void CIRGenFunction::getVTablePointers(BaseSubobject base,
const CXXRecordDecl *nextBaseDecl;
if (nextBase.isVirtual()) {
- cgm.errorNYI(rd->getSourceRange(), "getVTablePointers: virtual base");
- return;
+ // Check if we've visited this virtual base before.
+ if (!vbases.insert(baseDecl).second)
+ continue;
+
+ const ASTRecordLayout &layout =
+ getContext().getASTRecordLayout(vtableClass);
+
+ nextBaseDecl = nearestVBase;
+ baseOffset = layout.getVBaseClassOffset(baseDecl);
+ baseOffsetFromNearestVBase = CharUnits::Zero();
+ baseDeclIsNonVirtualPrimaryBase = false;
} else {
const ASTRecordLayout &layout = getContext().getASTRecordLayout(rd);
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 1afac6dd52c2d..469879371eb1d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1972,12 +1972,8 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
delegating = true;
break;
case CXXConstructionKind::VirtualBase:
- // This should just set 'forVirtualBase' to true and fall through, but
- // virtual base class support is otherwise missing, so this needs to wait
- // until it can be tested.
- cgm.errorNYI(e->getSourceRange(),
- "emitCXXConstructExpr: virtual base constructor");
- return;
+ forVirtualBase = true;
+ [[fallthrough]];
case CXXConstructionKind::NonVirtualBase:
type = Ctor_Base;
break;
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index aaf7dc767d888..4fd5a278e1a99 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -487,9 +487,10 @@ mlir::Value CIRGenItaniumCXXABI::getVTableAddressPointInStructor(
CIRGenFunction &cgf, const clang::CXXRecordDecl *vtableClass,
clang::BaseSubobject base, const clang::CXXRecordDecl *nearestVBase) {
- if (base.getBase()->getNumVBases()) {
+ if ((base.getBase()->getNumVBases() || nearestVBase != nullptr) &&
+ needsVTTParameter(cgf.curGD)) {
cgm.errorNYI(cgf.curFuncDecl->getLocation(),
- "getVTableAddressPointInStructor: virtual base");
+ "getVTableAddressPointInStructorWithVTT");
}
return getVTableAddressPoint(base, vtableClass);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
index b28afe42c39a0..914ef16c2a5ee 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
@@ -141,6 +141,10 @@ class CIRGenRecordLayout {
// for both virtual and non-virtual bases.
llvm::DenseMap<const clang::CXXRecordDecl *, unsigned> nonVirtualBases;
+ /// Map from virtual bases to their field index in the complete object.
+ llvm::DenseMap<const clang::CXXRecordDecl *, unsigned>
+ completeObjectVirtualBases;
+
/// Map from (bit-field) record field to the corresponding CIR record type
/// field no. This info is populated by record builder.
llvm::DenseMap<const clang::FieldDecl *, CIRGenBitFieldInfo> bitFields;
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index 1764967329969..b27d89707101e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -41,7 +41,7 @@ struct CIRRecordLowering final {
// member type that ensures correct rounding.
struct MemberInfo final {
CharUnits offset;
- enum class InfoKind { VFPtr, Field, Base } kind;
+ enum class InfoKind { VFPtr, Field, Base, VBase, Scissor } kind;
mlir::Type data;
union {
const FieldDecl *fieldDecl;
@@ -71,17 +71,18 @@ struct CIRRecordLowering final {
void setBitFieldInfo(const FieldDecl *fd, CharUnits startOffset,
mlir::Type storageType);
- void lower();
+ void lower(bool NonVirtualBaseType);
void lowerUnion();
/// Determines if we need a packed llvm struct.
- void determinePacked();
+ void determinePacked(bool nvBaseType);
/// Inserts padding everywhere it's needed.
void insertPadding();
void computeVolatileBitfields();
- void accumulateBases(const CXXRecordDecl *cxxRecordDecl);
+ void accumulateBases();
void accumulateVPtrs();
+ void accumulateVBases();
void accumulateFields();
RecordDecl::field_iterator
accumulateBitFields(RecordDecl::field_iterator field,
@@ -96,6 +97,17 @@ struct CIRRecordLowering final {
/// Helper function to check if the target machine is BigEndian.
bool isBigEndian() const { return astContext.getTargetInfo().isBigEndian(); }
+ // The Itanium base layout rule allows virtual bases to overlap
+ // other bases, which complicates layout in specific ways.
+ //
+ // Note specifically that the ms_struct attribute doesn't change this.
+ bool isOverlappingVBaseABI() {
+ return !astContext.getTargetInfo().getCXXABI().isMicrosoft();
+ }
+ // Recursively searches all of the bases to find out if a vbase is
+ // not the primary vbase of some base class.
+ bool hasOwnStorage(const CXXRecordDecl *decl, const CXXRecordDecl *query);
+
CharUnits bitsToCharUnits(uint64_t bitOffset) {
return astContext.toCharUnitsFromBits(bitOffset);
}
@@ -184,6 +196,7 @@ struct CIRRecordLowering final {
CIRGenBuilderTy &builder;
const ASTContext &astContext;
const RecordDecl *recordDecl;
+ const CXXRecordDecl *cxxRecordDecl;
const ASTRecordLayout &astRecordLayout;
// Helpful intermediate data-structures
std::vector<MemberInfo> members;
@@ -192,6 +205,7 @@ struct CIRRecordLowering final {
llvm::DenseMap<const FieldDecl *, CIRGenBitFieldInfo> bitFields;
llvm::DenseMap<const FieldDecl *, unsigned> fieldIdxMap;
llvm::DenseMap<const CXXRecordDecl *, unsigned> nonVirtualBases;
+ llvm::DenseMap<const CXXRecordDecl *, unsigned> virtualBases;
cir::CIRDataLayout dataLayout;
LLVM_PREFERRED_TYPE(bool)
@@ -211,13 +225,14 @@ struct CIRRecordLowering final {
CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes,
const RecordDecl *recordDecl, bool packed)
- : cirGenTypes(cirGenTypes), builder(cirGenTypes.getBuilder()),
- astContext(cirGenTypes.getASTContext()), recordDecl(recordDecl),
- astRecordLayout(
- cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)),
- dataLayout(cirGenTypes.getCGModule().getModule()),
- zeroInitializable(true), zeroInitializableAsBase(true), packed(packed),
- padded(false) {}
+ : cirGenTypes{cirGenTypes}, builder{cirGenTypes.getBuilder()},
+ astContext{cirGenTypes.getASTContext()}, recordDecl{recordDecl},
+ cxxRecordDecl{llvm::dyn_cast<CXXRecordDecl>(recordDecl)},
+ astRecordLayout{
+ cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)},
+ dataLayout{cirGenTypes.getCGModule().getModule()},
+ zeroInitializable{true}, zeroInitializableAsBase{true}, packed{packed},
+ padded{false} {}
void CIRRecordLowering::setBitFieldInfo(const FieldDecl *fd,
CharUnits startOffset,
@@ -246,27 +261,28 @@ void CIRRecordLowering::setBitFieldInfo(const FieldDecl *fd,
info.volatileStorageOffset = CharUnits::Zero();
}
-void CIRRecordLowering::lower() {
+void CIRRecordLowering::lower(bool nonVirtualBaseType) {
if (recordDecl->isUnion()) {
lowerUnion();
computeVolatileBitfields();
return;
}
- assert(!cir::MissingFeatures::recordLayoutVirtualBases());
- CharUnits size = astRecordLayout.getSize();
+ CharUnits size = nonVirtualBaseType ? astRecordLayout.getNonVirtualSize()
+ : astRecordLayout.getSize();
accumulateFields();
- if (const auto *cxxRecordDecl = dyn_cast<CXXRecordDecl>(recordDecl)) {
+ if (cxxRecordDecl) {
accumulateVPtrs();
- accumulateBases(cxxRecordDecl);
+ accumulateBases();
if (members.empty()) {
appendPaddingBytes(size);
computeVolatileBitfields();
return;
}
- assert(!cir::MissingFeatures::recordLayoutVirtualBases());
+ if (!nonVirtualBaseType)
+ accumulateVBases();
}
llvm::stable_sort(members);
@@ -275,7 +291,7 @@ void CIRRecordLowering::lower() {
assert(!cir::MissingFeatures::recordZeroInit());
members.push_back(makeStorageInfo(size, getUIntNType(8)));
- determinePacked();
+ determinePacked(nonVirtualBaseType);
insertPadding();
members.pop_back();
@@ -298,8 +314,9 @@ void CIRRecordLowering::fillOutputFields() {
setBitFieldInfo(member.fieldDecl, member.offset, fieldTypes.back());
} else if (member.kind == MemberInfo::InfoKind::Base) {
nonVirtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1;
+ } else if (member.kind == MemberInfo::InfoKind::VBase) {
+ virtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1;
}
- assert(!cir::MissingFeatures::recordLayoutVirtualBases());
}
}
@@ -426,8 +443,9 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
limitOffset = bitsToCharUnits(getFieldBitOffset(*probe));
goto FoundLimit;
}
- assert(!cir::MissingFeatures::cxxSupport());
- limitOffset = astRecordLayout.getDataSize();
+ limitOffset = cxxRecordDecl ? astRecordLayout.getNonVirtualSize()
+ : astRecordLayout.getDataSize();
+
FoundLimit:
CharUnits typeSize = getSize(type);
if (beginOffset + typeSize <= limitOffset) {
@@ -524,24 +542,25 @@ void CIRRecordLowering::calculateZeroInit() {
continue;
zeroInitializable = zeroInitializableAsBase = false;
return;
- } else if (member.kind == MemberInfo::InfoKind::Base) {
+ } else if (member.kind == MemberInfo::InfoKind::Base ||
+ member.kind == MemberInfo::InfoKind::VBase) {
if (isZeroInitializable(member.cxxRecordDecl))
continue;
zeroInitializable = false;
if (member.kind == MemberInfo::InfoKind::Base)
zeroInitializableAsBase = false;
}
- assert(!cir::MissingFeatures::recordLayoutVirtualBases());
}
}
-void CIRRecordLowering::determinePacked() {
+void CIRRecordLowering::determinePacked(bool nvBaseType) {
if (packed)
return;
CharUnits alignment = CharUnits::One();
-
- // TODO(cir): handle non-virtual base types
- assert(!cir::MissingFeatures::cxxSupport());
+ CharUnits nvAlignment = CharUnits::One();
+ CharUnits nvSize = !nvBaseType && cxxRecordDecl
+ ? astRecordLayout.getNonVirtualSize()
+ : CharUnits::Zero();
for (const MemberInfo &member : members) {
if (!member.data)
@@ -550,12 +569,19 @@ void CIRRecordLowering::determinePacked() {
// then the entire record must be packed.
if (member.offset % getAlignment(member.data))
packed = true;
+ if (member.offset < nvSize)
+ nvAlignment = std::max(nvAlignment, getAlignment(member.data));
alignment = std::max(alignment, getAlignment(member.data));
}
// If the size of the record (the capstone's offset) is not a multiple of the
// record's alignment, it must be packed.
if (members.back().offset % alignment)
packed = true;
+ // If the non-virtual sub-object is not a multiple of the non-virtual
+ // sub-object's alignment, it must be packed. We cannot have a packed
+ // non-virtual sub-object and an unpacked complete object or vise versa.
+ if (nvSize % nvAlignment)
+ packed = true;
// Update the alignment of the sentinel.
if (!packed)
members.back().data = getUIntNType(astContext.toBits(alignment));
@@ -589,7 +615,7 @@ std::unique_ptr<CIRGenRecordLayout>
CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
CIRRecordLowering lowering(*this, rd, /*packed=*/false);
assert(ty->isIncomplete() && "recomputing record layout?");
- lowering.lower();
+ lowering.lower(/*nonVirtualBaseType=*/false);
// If we're in C++, compute the base subobject type.
cir::RecordType baseTy;
@@ -599,7 +625,7 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
if (lowering.astRecordLayout.getNonVirtualSize() !=
lowering.astRecordLayout.getSize()) {
CIRRecordLowering baseLowering(*this, rd, /*Packed=*/lowering.packed);
- baseLowering.lower();
+ baseLowering.lower(/*NonVirtualBaseType=*/true);
std::string baseIdentifier = getRecordTypeName(rd, ".base");
baseTy =
builder.getCompleteRecordTy(baseLowering.fieldTypes, baseIdentifier,
@@ -626,8 +652,8 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
assert(!cir::MissingFeatures::recordZeroInit());
rl->nonVirtualBases.swap(lowering.nonVirtualBases);
+ rl->completeObjectVirtualBases.swap(lowering.virtualBases);
- assert(!cir::MissingFeatures::cxxSupport());
assert(!cir::MissingFeatures::bitfields());
// Add all the field numbers.
@@ -754,6 +780,17 @@ void CIRRecordLowering::lowerUnion() {
packed = true;
}
+bool CIRRecordLowering::hasOwnStorage(const CXXRecordDecl *decl,
+ const CXXRecordDecl *query) {
+ const ASTRecordLayout &declLayout = astContext.getASTRecordLayout(decl);
+ if (declLayout.isPrimaryBaseVirtual() && declLayout.getPrimaryBase() == query)
+ return false;
+ for (const auto &base : decl->bases())
+ if (!hasOwnStorage(base.getType()->getAsCXXRecordDecl(), query))
+ return false;
+ return true;
+}
+
/// The AAPCS that defines that, when possible, bit-fields should
/// be accessed using containers of the declared type width:
/// When a volatile bit-field is read, and its container does not overlap with
@@ -873,7 +910,7 @@ void CIRRecordLowering::computeVolatileBitfields() {
}
}
-void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) {
+void CIRRecordLowering::accumulateBases() {
// If we've got a primary virtual base, we need to add it with the bases.
if (astRecordLayout.isPrimaryBaseVirtual()) {
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
@@ -881,12 +918,9 @@ void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) {
}
// Accumulate the non-virtual bases.
- for ([[maybe_unused]] const auto &base : cxxRecordDecl->bases()) {
- if (base.isVirtual()) {
- cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
- "accumulateBases: virtual base");
+ for (const auto &base : cxxRecordDecl->bases()) {
+ if (base.isVirtual())
continue;
- }
// Bases can be zero-sized even if not technically empty if they
// contain only a trailing array member.
const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl();
@@ -899,6 +933,52 @@ void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) {
}
}
+void CIRRecordLowering::accumulateVBases() {
+ CharUnits scissorOffset = astRecordLayout.getNonVirtualSize();
+ // In the itanium ABI, it's possible to place a vbase at a dsize that is
+ // smaller than the nvsize. Here we check to see if such a base is placed
+ // before the nvsize and set the scissor offset to that, instead of the
+ // nvsize.
+ if (isOverlappingVBaseABI()) {
+ for (const auto &base : cxxRecordDecl->vbases()) {
+ const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl();
+ if (baseDecl->isEmpty())
+ continue;
+ // If the vbase is a primary virtual base of some base, then it doesn't
+ // get its own storage location but instead lives inside of that base.
+ if (astContext.isNearlyEmpty(baseDecl) &&
+ !hasOwnStorage(cxxRecordDecl, baseDecl))
+ continue;
+ scissorOffset = std::min(scissorOffset,
+ astRecordLayout.getVBaseClassOffset(baseDecl));
+ }
+ }
+ members.push_back(MemberInfo(scissorOffset, MemberInfo::InfoKind::Scissor,
+ mlir::Type{}, cxxRecordDecl));
+ for (const auto &base : cxxRecordDecl->vbases()) {
+ const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl();
+ if (baseDecl->isEmpty())
+ continue;
+ CharUnits offset = astRecordLayout.getVBaseClassOffset(baseDecl);
+ // If the vbase is a primary virtual base of some base, the...
[truncated]
|
@llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) ChangesThis 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. Patch is 26.14 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/155534.diff 8 Files Affected:
diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
index b5f2e1a067274..df7ffbb4a2759 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h
@@ -37,6 +37,12 @@ class CIRGenCXXABI {
void setCXXABIThisValue(CIRGenFunction &cgf, mlir::Value thisPtr);
+ /// Emit the code to initialize hidden members required to handle virtual
+ /// inheritance, if needed by the ABI.
+ virtual void
+ initializeHiddenVirtualInheritanceMembers(CIRGenFunction &cgf,
+ const CXXRecordDecl *rd) {}
+
/// Emit a single constructor/destructor with the gen type from a C++
/// constructor/destructor Decl.
virtual void emitCXXStructor(clang::GlobalDecl gd) = 0;
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 9106e5e4824ff..ac923635192aa 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -238,31 +238,44 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd,
bool constructVBases = ctorType != Ctor_Base &&
classDecl->getNumVBases() != 0 &&
!classDecl->isAbstract();
- if (constructVBases) {
- cgm.errorNYI(cd->getSourceRange(), "emitCtorPrologue: virtual base");
- return;
- }
-
- const mlir::Value oldThisValue = cxxThisValue;
- if (!constructVBases && b != e && (*b)->isBaseInitializer() &&
- (*b)->isBaseVirtual()) {
+ if (constructVBases &&
+ !cgm.getTarget().getCXXABI().hasConstructorVariants()) {
cgm.errorNYI(cd->getSourceRange(),
- "emitCtorPrologue: virtual base initializer");
+ "emitCtorPrologue: virtual base without variants");
return;
}
- // Handle non-virtual base initializers.
- for (; b != e && (*b)->isBaseInitializer(); b++) {
- assert(!(*b)->isBaseVirtual());
+ const mlir::Value oldThisValue = cxxThisValue;
+ // Initialize virtual bases.
+ auto emitInitializer = [&](CXXCtorInitializer *baseInit) {
if (cgm.getCodeGenOpts().StrictVTablePointers &&
cgm.getCodeGenOpts().OptimizationLevel > 0 &&
- isInitializerOfDynamicClass(*b)) {
+ isInitializerOfDynamicClass(baseInit)) {
+ // It's OK to continue after emitting the error here. The missing code
+ // just "launders" the 'this' pointer.
cgm.errorNYI(cd->getSourceRange(),
- "emitCtorPrologue: strict vtable pointers");
- return;
+ "emitCtorPrologue: strict vtable pointers for vbase");
}
- emitBaseInitializer(getLoc(cd->getBeginLoc()), classDecl, *b);
+ emitBaseInitializer(getLoc(cd->getBeginLoc()), classDecl, baseInit);
+ };
+
+ for (; b != e && (*b)->isBaseInitializer() && (*b)->isBaseVirtual(); b++) {
+ if (!constructVBases)
+ continue;
+ emitInitializer(*b);
+ }
+
+ // The loop above and the loop below could obviously be merged in their
+ // current form, but when we implement support for the MS C++ ABI, we will
+ // need to insert a branch after the last virtual base initializer, so
+ // separate loops will be useful then. The missing code is covered by the
+ // "virtual base without variants" diagnostic above.
+
+ // Handle non-virtual base initializers.
+ for (; b != e && (*b)->isBaseInitializer(); b++) {
+ assert(!(*b)->isBaseVirtual());
+ emitInitializer(*b);
}
cxxThisValue = oldThisValue;
@@ -370,7 +383,7 @@ void CIRGenFunction::initializeVTablePointers(mlir::Location loc,
initializeVTablePointer(loc, vptr);
if (rd->getNumVBases())
- cgm.errorNYI(loc, "initializeVTablePointers: virtual base");
+ cgm.getCXXABI().initializeHiddenVirtualInheritanceMembers(*this, rd);
}
CIRGenFunction::VPtrsVector
@@ -418,8 +431,17 @@ void CIRGenFunction::getVTablePointers(BaseSubobject base,
const CXXRecordDecl *nextBaseDecl;
if (nextBase.isVirtual()) {
- cgm.errorNYI(rd->getSourceRange(), "getVTablePointers: virtual base");
- return;
+ // Check if we've visited this virtual base before.
+ if (!vbases.insert(baseDecl).second)
+ continue;
+
+ const ASTRecordLayout &layout =
+ getContext().getASTRecordLayout(vtableClass);
+
+ nextBaseDecl = nearestVBase;
+ baseOffset = layout.getVBaseClassOffset(baseDecl);
+ baseOffsetFromNearestVBase = CharUnits::Zero();
+ baseDeclIsNonVirtualPrimaryBase = false;
} else {
const ASTRecordLayout &layout = getContext().getASTRecordLayout(rd);
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index 1afac6dd52c2d..469879371eb1d 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -1972,12 +1972,8 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e,
delegating = true;
break;
case CXXConstructionKind::VirtualBase:
- // This should just set 'forVirtualBase' to true and fall through, but
- // virtual base class support is otherwise missing, so this needs to wait
- // until it can be tested.
- cgm.errorNYI(e->getSourceRange(),
- "emitCXXConstructExpr: virtual base constructor");
- return;
+ forVirtualBase = true;
+ [[fallthrough]];
case CXXConstructionKind::NonVirtualBase:
type = Ctor_Base;
break;
diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
index aaf7dc767d888..4fd5a278e1a99 100644
--- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
@@ -487,9 +487,10 @@ mlir::Value CIRGenItaniumCXXABI::getVTableAddressPointInStructor(
CIRGenFunction &cgf, const clang::CXXRecordDecl *vtableClass,
clang::BaseSubobject base, const clang::CXXRecordDecl *nearestVBase) {
- if (base.getBase()->getNumVBases()) {
+ if ((base.getBase()->getNumVBases() || nearestVBase != nullptr) &&
+ needsVTTParameter(cgf.curGD)) {
cgm.errorNYI(cgf.curFuncDecl->getLocation(),
- "getVTableAddressPointInStructor: virtual base");
+ "getVTableAddressPointInStructorWithVTT");
}
return getVTableAddressPoint(base, vtableClass);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
index b28afe42c39a0..914ef16c2a5ee 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
@@ -141,6 +141,10 @@ class CIRGenRecordLayout {
// for both virtual and non-virtual bases.
llvm::DenseMap<const clang::CXXRecordDecl *, unsigned> nonVirtualBases;
+ /// Map from virtual bases to their field index in the complete object.
+ llvm::DenseMap<const clang::CXXRecordDecl *, unsigned>
+ completeObjectVirtualBases;
+
/// Map from (bit-field) record field to the corresponding CIR record type
/// field no. This info is populated by record builder.
llvm::DenseMap<const clang::FieldDecl *, CIRGenBitFieldInfo> bitFields;
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index 1764967329969..b27d89707101e 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -41,7 +41,7 @@ struct CIRRecordLowering final {
// member type that ensures correct rounding.
struct MemberInfo final {
CharUnits offset;
- enum class InfoKind { VFPtr, Field, Base } kind;
+ enum class InfoKind { VFPtr, Field, Base, VBase, Scissor } kind;
mlir::Type data;
union {
const FieldDecl *fieldDecl;
@@ -71,17 +71,18 @@ struct CIRRecordLowering final {
void setBitFieldInfo(const FieldDecl *fd, CharUnits startOffset,
mlir::Type storageType);
- void lower();
+ void lower(bool NonVirtualBaseType);
void lowerUnion();
/// Determines if we need a packed llvm struct.
- void determinePacked();
+ void determinePacked(bool nvBaseType);
/// Inserts padding everywhere it's needed.
void insertPadding();
void computeVolatileBitfields();
- void accumulateBases(const CXXRecordDecl *cxxRecordDecl);
+ void accumulateBases();
void accumulateVPtrs();
+ void accumulateVBases();
void accumulateFields();
RecordDecl::field_iterator
accumulateBitFields(RecordDecl::field_iterator field,
@@ -96,6 +97,17 @@ struct CIRRecordLowering final {
/// Helper function to check if the target machine is BigEndian.
bool isBigEndian() const { return astContext.getTargetInfo().isBigEndian(); }
+ // The Itanium base layout rule allows virtual bases to overlap
+ // other bases, which complicates layout in specific ways.
+ //
+ // Note specifically that the ms_struct attribute doesn't change this.
+ bool isOverlappingVBaseABI() {
+ return !astContext.getTargetInfo().getCXXABI().isMicrosoft();
+ }
+ // Recursively searches all of the bases to find out if a vbase is
+ // not the primary vbase of some base class.
+ bool hasOwnStorage(const CXXRecordDecl *decl, const CXXRecordDecl *query);
+
CharUnits bitsToCharUnits(uint64_t bitOffset) {
return astContext.toCharUnitsFromBits(bitOffset);
}
@@ -184,6 +196,7 @@ struct CIRRecordLowering final {
CIRGenBuilderTy &builder;
const ASTContext &astContext;
const RecordDecl *recordDecl;
+ const CXXRecordDecl *cxxRecordDecl;
const ASTRecordLayout &astRecordLayout;
// Helpful intermediate data-structures
std::vector<MemberInfo> members;
@@ -192,6 +205,7 @@ struct CIRRecordLowering final {
llvm::DenseMap<const FieldDecl *, CIRGenBitFieldInfo> bitFields;
llvm::DenseMap<const FieldDecl *, unsigned> fieldIdxMap;
llvm::DenseMap<const CXXRecordDecl *, unsigned> nonVirtualBases;
+ llvm::DenseMap<const CXXRecordDecl *, unsigned> virtualBases;
cir::CIRDataLayout dataLayout;
LLVM_PREFERRED_TYPE(bool)
@@ -211,13 +225,14 @@ struct CIRRecordLowering final {
CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes,
const RecordDecl *recordDecl, bool packed)
- : cirGenTypes(cirGenTypes), builder(cirGenTypes.getBuilder()),
- astContext(cirGenTypes.getASTContext()), recordDecl(recordDecl),
- astRecordLayout(
- cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)),
- dataLayout(cirGenTypes.getCGModule().getModule()),
- zeroInitializable(true), zeroInitializableAsBase(true), packed(packed),
- padded(false) {}
+ : cirGenTypes{cirGenTypes}, builder{cirGenTypes.getBuilder()},
+ astContext{cirGenTypes.getASTContext()}, recordDecl{recordDecl},
+ cxxRecordDecl{llvm::dyn_cast<CXXRecordDecl>(recordDecl)},
+ astRecordLayout{
+ cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)},
+ dataLayout{cirGenTypes.getCGModule().getModule()},
+ zeroInitializable{true}, zeroInitializableAsBase{true}, packed{packed},
+ padded{false} {}
void CIRRecordLowering::setBitFieldInfo(const FieldDecl *fd,
CharUnits startOffset,
@@ -246,27 +261,28 @@ void CIRRecordLowering::setBitFieldInfo(const FieldDecl *fd,
info.volatileStorageOffset = CharUnits::Zero();
}
-void CIRRecordLowering::lower() {
+void CIRRecordLowering::lower(bool nonVirtualBaseType) {
if (recordDecl->isUnion()) {
lowerUnion();
computeVolatileBitfields();
return;
}
- assert(!cir::MissingFeatures::recordLayoutVirtualBases());
- CharUnits size = astRecordLayout.getSize();
+ CharUnits size = nonVirtualBaseType ? astRecordLayout.getNonVirtualSize()
+ : astRecordLayout.getSize();
accumulateFields();
- if (const auto *cxxRecordDecl = dyn_cast<CXXRecordDecl>(recordDecl)) {
+ if (cxxRecordDecl) {
accumulateVPtrs();
- accumulateBases(cxxRecordDecl);
+ accumulateBases();
if (members.empty()) {
appendPaddingBytes(size);
computeVolatileBitfields();
return;
}
- assert(!cir::MissingFeatures::recordLayoutVirtualBases());
+ if (!nonVirtualBaseType)
+ accumulateVBases();
}
llvm::stable_sort(members);
@@ -275,7 +291,7 @@ void CIRRecordLowering::lower() {
assert(!cir::MissingFeatures::recordZeroInit());
members.push_back(makeStorageInfo(size, getUIntNType(8)));
- determinePacked();
+ determinePacked(nonVirtualBaseType);
insertPadding();
members.pop_back();
@@ -298,8 +314,9 @@ void CIRRecordLowering::fillOutputFields() {
setBitFieldInfo(member.fieldDecl, member.offset, fieldTypes.back());
} else if (member.kind == MemberInfo::InfoKind::Base) {
nonVirtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1;
+ } else if (member.kind == MemberInfo::InfoKind::VBase) {
+ virtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1;
}
- assert(!cir::MissingFeatures::recordLayoutVirtualBases());
}
}
@@ -426,8 +443,9 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
limitOffset = bitsToCharUnits(getFieldBitOffset(*probe));
goto FoundLimit;
}
- assert(!cir::MissingFeatures::cxxSupport());
- limitOffset = astRecordLayout.getDataSize();
+ limitOffset = cxxRecordDecl ? astRecordLayout.getNonVirtualSize()
+ : astRecordLayout.getDataSize();
+
FoundLimit:
CharUnits typeSize = getSize(type);
if (beginOffset + typeSize <= limitOffset) {
@@ -524,24 +542,25 @@ void CIRRecordLowering::calculateZeroInit() {
continue;
zeroInitializable = zeroInitializableAsBase = false;
return;
- } else if (member.kind == MemberInfo::InfoKind::Base) {
+ } else if (member.kind == MemberInfo::InfoKind::Base ||
+ member.kind == MemberInfo::InfoKind::VBase) {
if (isZeroInitializable(member.cxxRecordDecl))
continue;
zeroInitializable = false;
if (member.kind == MemberInfo::InfoKind::Base)
zeroInitializableAsBase = false;
}
- assert(!cir::MissingFeatures::recordLayoutVirtualBases());
}
}
-void CIRRecordLowering::determinePacked() {
+void CIRRecordLowering::determinePacked(bool nvBaseType) {
if (packed)
return;
CharUnits alignment = CharUnits::One();
-
- // TODO(cir): handle non-virtual base types
- assert(!cir::MissingFeatures::cxxSupport());
+ CharUnits nvAlignment = CharUnits::One();
+ CharUnits nvSize = !nvBaseType && cxxRecordDecl
+ ? astRecordLayout.getNonVirtualSize()
+ : CharUnits::Zero();
for (const MemberInfo &member : members) {
if (!member.data)
@@ -550,12 +569,19 @@ void CIRRecordLowering::determinePacked() {
// then the entire record must be packed.
if (member.offset % getAlignment(member.data))
packed = true;
+ if (member.offset < nvSize)
+ nvAlignment = std::max(nvAlignment, getAlignment(member.data));
alignment = std::max(alignment, getAlignment(member.data));
}
// If the size of the record (the capstone's offset) is not a multiple of the
// record's alignment, it must be packed.
if (members.back().offset % alignment)
packed = true;
+ // If the non-virtual sub-object is not a multiple of the non-virtual
+ // sub-object's alignment, it must be packed. We cannot have a packed
+ // non-virtual sub-object and an unpacked complete object or vise versa.
+ if (nvSize % nvAlignment)
+ packed = true;
// Update the alignment of the sentinel.
if (!packed)
members.back().data = getUIntNType(astContext.toBits(alignment));
@@ -589,7 +615,7 @@ std::unique_ptr<CIRGenRecordLayout>
CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
CIRRecordLowering lowering(*this, rd, /*packed=*/false);
assert(ty->isIncomplete() && "recomputing record layout?");
- lowering.lower();
+ lowering.lower(/*nonVirtualBaseType=*/false);
// If we're in C++, compute the base subobject type.
cir::RecordType baseTy;
@@ -599,7 +625,7 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
if (lowering.astRecordLayout.getNonVirtualSize() !=
lowering.astRecordLayout.getSize()) {
CIRRecordLowering baseLowering(*this, rd, /*Packed=*/lowering.packed);
- baseLowering.lower();
+ baseLowering.lower(/*NonVirtualBaseType=*/true);
std::string baseIdentifier = getRecordTypeName(rd, ".base");
baseTy =
builder.getCompleteRecordTy(baseLowering.fieldTypes, baseIdentifier,
@@ -626,8 +652,8 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
assert(!cir::MissingFeatures::recordZeroInit());
rl->nonVirtualBases.swap(lowering.nonVirtualBases);
+ rl->completeObjectVirtualBases.swap(lowering.virtualBases);
- assert(!cir::MissingFeatures::cxxSupport());
assert(!cir::MissingFeatures::bitfields());
// Add all the field numbers.
@@ -754,6 +780,17 @@ void CIRRecordLowering::lowerUnion() {
packed = true;
}
+bool CIRRecordLowering::hasOwnStorage(const CXXRecordDecl *decl,
+ const CXXRecordDecl *query) {
+ const ASTRecordLayout &declLayout = astContext.getASTRecordLayout(decl);
+ if (declLayout.isPrimaryBaseVirtual() && declLayout.getPrimaryBase() == query)
+ return false;
+ for (const auto &base : decl->bases())
+ if (!hasOwnStorage(base.getType()->getAsCXXRecordDecl(), query))
+ return false;
+ return true;
+}
+
/// The AAPCS that defines that, when possible, bit-fields should
/// be accessed using containers of the declared type width:
/// When a volatile bit-field is read, and its container does not overlap with
@@ -873,7 +910,7 @@ void CIRRecordLowering::computeVolatileBitfields() {
}
}
-void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) {
+void CIRRecordLowering::accumulateBases() {
// If we've got a primary virtual base, we need to add it with the bases.
if (astRecordLayout.isPrimaryBaseVirtual()) {
cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
@@ -881,12 +918,9 @@ void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) {
}
// Accumulate the non-virtual bases.
- for ([[maybe_unused]] const auto &base : cxxRecordDecl->bases()) {
- if (base.isVirtual()) {
- cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
- "accumulateBases: virtual base");
+ for (const auto &base : cxxRecordDecl->bases()) {
+ if (base.isVirtual())
continue;
- }
// Bases can be zero-sized even if not technically empty if they
// contain only a trailing array member.
const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl();
@@ -899,6 +933,52 @@ void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) {
}
}
+void CIRRecordLowering::accumulateVBases() {
+ CharUnits scissorOffset = astRecordLayout.getNonVirtualSize();
+ // In the itanium ABI, it's possible to place a vbase at a dsize that is
+ // smaller than the nvsize. Here we check to see if such a base is placed
+ // before the nvsize and set the scissor offset to that, instead of the
+ // nvsize.
+ if (isOverlappingVBaseABI()) {
+ for (const auto &base : cxxRecordDecl->vbases()) {
+ const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl();
+ if (baseDecl->isEmpty())
+ continue;
+ // If the vbase is a primary virtual base of some base, then it doesn't
+ // get its own storage location but instead lives inside of that base.
+ if (astContext.isNearlyEmpty(baseDecl) &&
+ !hasOwnStorage(cxxRecordDecl, baseDecl))
+ continue;
+ scissorOffset = std::min(scissorOffset,
+ astRecordLayout.getVBaseClassOffset(baseDecl));
+ }
+ }
+ members.push_back(MemberInfo(scissorOffset, MemberInfo::InfoKind::Scissor,
+ mlir::Type{}, cxxRecordDecl));
+ for (const auto &base : cxxRecordDecl->vbases()) {
+ const CXXRecordDecl *baseDecl = base.getType()->getAsCXXRecordDecl();
+ if (baseDecl->isEmpty())
+ continue;
+ CharUnits offset = astRecordLayout.getVBaseClassOffset(baseDecl);
+ // If the vbase is a primary virtual base of some base, the...
[truncated]
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 small comments, else this lgtm.
}; | ||
|
||
for (; b != e && (*b)->isBaseInitializer() && (*b)->isBaseVirtual(); b++) { | ||
if (!constructVBases) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this inside the loop rather than preventing this loop from happening at all?
Also, is there any sort of range loop we can use here? Thisis awful ugly...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is inside the loop because if we are not constructing vbases we need to update the iterator to move past them. The initializers are ordered such that all virtual bases occur before the first non-virtual base. This loop is necessary to position the iterator for the loop below.
The MS CXXABI throws a bit of a wrench into the possibility of a range loop, because we need to insert a branch to a continue block between the virtual initializers and the non-virtual initializers, but we can probably use a flag to check for the conditions to do that. I'll play around with this loop in OGCG and see if I can simplify it without breaking anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, right! I missed the same iterators. Anything you can do to make this less awful would be appreciated :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm proceeding with the implementation change modeled after the classic codegen change proposed in #155668. If that ends up being rejected, I'll update this accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just one question:
@@ -899,6 +933,52 @@ void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) { | |||
} | |||
} | |||
|
|||
void CIRRecordLowering::accumulateVBases() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that the implementation we’re using is the one from classic CodeGen in version 18, but it changed in version 19. It might be worth updating, since the newer version looks shorter and cleaner:
Version 18:
llvm-project/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp
Lines 670 to 676 in 3b5b5c1
void CGRecordLowering::accumulateVBases() { | |
CharUnits ScissorOffset = Layout.getNonVirtualSize(); | |
// In the itanium ABI, it's possible to place a vbase at a dsize that is | |
// smaller than the nvsize. Here we check to see if such a base is placed | |
// before the nvsize and set the scissor offset to that, instead of the | |
// nvsize. | |
if (isOverlappingVBaseABI()) |
Version 19:
llvm-project/clang/lib/CodeGen/CGRecordLayoutBuilder.cpp
Lines 896 to 901 in cd70802
void CGRecordLowering::accumulateVBases() { | |
for (const auto &Base : RD->vbases()) { | |
const CXXRecordDecl *BaseDecl = Base.getType()->getAsCXXRecordDecl(); | |
if (isEmptyRecordForLayout(Context, Base.getType())) | |
continue; | |
CharUnits Offset = Layout.getVBaseClassOffset(BaseDecl); |
What do you think, @andykaylor ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, we should definitely update to the newer code structure. Thanks for catching that!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing else to add - LGTM after comments from others are addressed
clang/lib/CodeGen/CGClass.cpp
Outdated
@@ -180,7 +180,11 @@ CharUnits CodeGenModule::computeNonVirtualBaseClassOffset( | |||
// Get the layout. | |||
const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); | |||
|
|||
const auto *BaseDecl = Base->getType()->castAsCXXRecordDecl(); | |||
const auto *BaseDecl = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ignore these changes. They're artifacts from another PR.
✅ With the latest revision this PR passed the C/C++ code formatter. |
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.