https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/155534
>From dc7ae0c41669c04d1288a882929058ec08501042 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Fri, 11 Jul 2025 13:18:29 -0700 Subject: [PATCH 1/4] [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. --- clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 6 + clang/lib/CIR/CodeGen/CIRGenClass.cpp | 60 ++++--- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 8 +- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 5 +- clang/lib/CIR/CodeGen/CIRGenRecordLayout.h | 4 + .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 152 +++++++++++++----- clang/test/CIR/CodeGen/vbase.cpp | 70 ++++++++ clang/test/CIR/CodeGen/vtt.cpp | 45 ++++++ 8 files changed, 287 insertions(+), 63 deletions(-) create mode 100644 clang/test/CIR/CodeGen/vbase.cpp create mode 100644 clang/test/CIR/CodeGen/vtt.cpp 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, then it doesn't + // get its own storage location but instead lives inside of that base. + if (isOverlappingVBaseABI() && astContext.isNearlyEmpty(baseDecl) && + !hasOwnStorage(cxxRecordDecl, baseDecl)) { + members.push_back( + MemberInfo(offset, MemberInfo::InfoKind::VBase, nullptr, baseDecl)); + continue; + } + // If we've got a vtordisp, add it as a storage type. + if (astRecordLayout.getVBaseOffsetsMap() + .find(baseDecl) + ->second.hasVtorDisp()) + members.push_back(makeStorageInfo(offset - CharUnits::fromQuantity(4), + getUIntNType(32))); + members.push_back(MemberInfo(offset, MemberInfo::InfoKind::VBase, + getStorageType(baseDecl), baseDecl)); + } +} + void CIRRecordLowering::accumulateVPtrs() { if (astRecordLayout.hasOwnVFPtr()) members.push_back(MemberInfo(CharUnits::Zero(), MemberInfo::InfoKind::VFPtr, diff --git a/clang/test/CIR/CodeGen/vbase.cpp b/clang/test/CIR/CodeGen/vbase.cpp new file mode 100644 index 0000000000000..1d1b5e083bfc9 --- /dev/null +++ b/clang/test/CIR/CodeGen/vbase.cpp @@ -0,0 +1,70 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG + +struct A { + int a; +}; + +struct B: virtual A { + int b; +}; + +void ppp() { B b; } + +// Note: OGCG speculatively emits the VTT and VTables. This is not yet implemented in CIR. + +// Vtable definition for B +// CIR: cir.global "private" external @_ZTV1B + +// LLVM: @_ZTV1B = external global { [3 x ptr] } + +// OGCG: @_ZTV1B = linkonce_odr unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr inttoptr (i64 12 to ptr), ptr null, ptr @_ZTI1B] }, comdat, align 8 + +// Constructor for A +// CIR: cir.func comdat linkonce_odr @_ZN1AC2Ev(%arg0: !cir.ptr<!rec_A> +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_A>, !cir.ptr<!cir.ptr<!rec_A>>, ["this", init] +// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_A>, !cir.ptr<!cir.ptr<!rec_A>> +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_A>>, !cir.ptr<!rec_A> +// CIR: cir.return + +// LLVM: define{{.*}} void @_ZN1AC2Ev(ptr %[[THIS_ARG:.*]]) { +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr +// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// LLVM: ret void + +// Note: OGCG elides the constructor for A. This is not yet implemented in CIR. + +// Constructor for B +// CIR: cir.func comdat linkonce_odr @_ZN1BC1Ev(%arg0: !cir.ptr<!rec_B> +// CIR: %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_B>, !cir.ptr<!cir.ptr<!rec_B>>, ["this", init] +// CIR: cir.store %arg0, %[[THIS_ADDR]] : !cir.ptr<!rec_B>, !cir.ptr<!cir.ptr<!rec_B>> +// CIR: %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : !cir.ptr<!cir.ptr<!rec_B>>, !cir.ptr<!rec_B> +// CIR: %[[BASE_A_ADDR:.*]] = cir.base_class_addr %[[THIS]] : !cir.ptr<!rec_B> nonnull [12] -> !cir.ptr<!rec_A> +// CIR: cir.call @_ZN1AC2Ev(%[[BASE_A_ADDR]]) nothrow : (!cir.ptr<!rec_A>) -> () +// CIR: %[[VTABLE:.*]] = cir.vtable.address_point(@_ZTV1B, address_point = <index = 0, offset = 3>) : !cir.vptr +// CIR: %[[B_VPTR:.*]] = cir.vtable.get_vptr %[[THIS]] : !cir.ptr<!rec_B> -> !cir.ptr<!cir.vptr> +// CIR: cir.store align(8) %[[VTABLE]], %[[B_VPTR]] : !cir.vptr, !cir.ptr<!cir.vptr> +// CIR: cir.return + +// LLVM: define{{.*}} void @_ZN1BC1Ev(ptr %[[THIS_ARG:.*]]) { +// LLVM: %[[THIS_ADDR:.*]] = alloca ptr +// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] +// LLVM: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// LLVM: %[[BASE_A_ADDR:.*]] = getelementptr i8, ptr %[[THIS]], i32 12 +// LLVM: call void @_ZN1AC2Ev(ptr %[[BASE_A_ADDR]]) +// LLVM: store ptr getelementptr inbounds nuw (i8, ptr @_ZTV1B, i64 24), ptr %[[THIS]] +// LLVM: ret void + +// OGCG: define{{.*}} void @_ZN1BC1Ev(ptr {{.*}} %[[THIS_ARG:.*]]) +// OGCG: %[[THIS_ADDR:.*]] = alloca ptr +// OGCG: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]] +// OGCG: %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]] +// OGCG: %[[BASE_A_ADDR:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i64 12 +// OGCG: store ptr getelementptr inbounds inrange(-24, 0) ({ [3 x ptr] }, ptr @_ZTV1B, i32 0, i32 0, i32 3), ptr %[[THIS]] +// OGCG: ret void + diff --git a/clang/test/CIR/CodeGen/vtt.cpp b/clang/test/CIR/CodeGen/vtt.cpp new file mode 100644 index 0000000000000..631aab428840a --- /dev/null +++ b/clang/test/CIR/CodeGen/vtt.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s + +// Note: This test will be expanded to verify VTT emission and VTT implicit +// argument handling. For now, it's just test the record layout. + +class A { +public: + int a; + virtual void v() {} +}; + +class B : public virtual A { +public: + int b; + virtual void w(); +}; + +class C : public virtual A { +public: + long c; + virtual void x() {} +}; + +class D : public B, public C { +public: + long d; + virtual void y() {} +}; + +// This is just here to force the record types to be emitted. +void f(D *d) {} + +// CIR: !rec_A2Ebase = !cir.record<struct "A.base" packed {!cir.vptr, !s32i}> +// CIR: !rec_B2Ebase = !cir.record<struct "B.base" packed {!cir.vptr, !s32i}> +// CIR: !rec_C2Ebase = !cir.record<struct "C.base" {!cir.vptr, !s64i}> +// CIR: !rec_D = !cir.record<class "D" {!rec_B2Ebase, !rec_C2Ebase, !s64i, !rec_A2Ebase}> + +// Nothing interesting to see here yet. +// LLVM: define{{.*}} void @_Z1fP1D +// OGCG: define{{.*}} void @_Z1fP1D >From 5f2fd84ef5211f80a4e2f0a8ece6fbb490791f02 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Wed, 27 Aug 2025 14:46:25 -0700 Subject: [PATCH 2/4] Incorporate review feedback --- clang/lib/CIR/CodeGen/CIRGenClass.cpp | 51 +++++++++------- .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 36 +++-------- clang/lib/CodeGen/CGClass.cpp | 60 +++++++++++++++---- 3 files changed, 84 insertions(+), 63 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index ac923635192aa..f3de8cd50cdc0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -222,13 +222,7 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, const CXXRecordDecl *classDecl = cd->getParent(); - // This code doesn't use range-based iteration because we may need to emit - // code between the virtual base initializers and the non-virtual base or - // between the non-virtual base initializers and the member initializers. - CXXConstructorDecl::init_const_iterator b = cd->init_begin(), - e = cd->init_end(); - - // Virtual base initializers first, if any. They aren't needed if: + // Virtual base initializers aren't needed if: // - This is a base ctor variant // - There are no vbases // - The class is abstract, so a complete object of it cannot be constructed @@ -245,9 +239,28 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, return; } + // Create three separate ranges for the different types of initializers. + auto allInits = cd->inits(); + + // Find the boundaries between the three groups. + auto virtualBaseEnd = std::find_if( + allInits.begin(), allInits.end(), [](const CXXCtorInitializer *Init) { + return !(Init->isBaseInitializer() && Init->isBaseVirtual()); + }); + + auto nonVirtualBaseEnd = std::find_if(virtualBaseEnd, allInits.end(), + [](const CXXCtorInitializer *Init) { + return !Init->isBaseInitializer(); + }); + + // Create the three ranges. + auto virtualBaseInits = llvm::make_range(allInits.begin(), virtualBaseEnd); + auto nonVirtualBaseInits = + llvm::make_range(virtualBaseEnd, nonVirtualBaseEnd); + auto memberInits = llvm::make_range(nonVirtualBaseEnd, allInits.end()); + const mlir::Value oldThisValue = cxxThisValue; - // Initialize virtual bases. auto emitInitializer = [&](CXXCtorInitializer *baseInit) { if (cgm.getCodeGenOpts().StrictVTablePointers && cgm.getCodeGenOpts().OptimizationLevel > 0 && @@ -260,22 +273,19 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, emitBaseInitializer(getLoc(cd->getBeginLoc()), classDecl, baseInit); }; - for (; b != e && (*b)->isBaseInitializer() && (*b)->isBaseVirtual(); b++) { + // Process virtual base initializers. + for (CXXCtorInitializer *virtualBaseInit : virtualBaseInits) { if (!constructVBases) continue; - emitInitializer(*b); + emitInitializer(virtualBaseInit); } - // 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. + assert(!cir::MissingFeatures::msabi()); - // Handle non-virtual base initializers. - for (; b != e && (*b)->isBaseInitializer(); b++) { - assert(!(*b)->isBaseVirtual()); - emitInitializer(*b); + // Then, non-virtual base initializers. + for (CXXCtorInitializer *nonVirtualBaseInit : nonVirtualBaseInits) { + assert(!nonVirtualBaseInit->isBaseVirtual()); + emitInitializer(nonVirtualBaseInit); } cxxThisValue = oldThisValue; @@ -289,8 +299,7 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, // lowering or optimization phases to keep the memory accesses more // explicit. For now, we don't insert memcpy at all. assert(!cir::MissingFeatures::ctorMemcpyizer()); - for (; b != e; b++) { - CXXCtorInitializer *member = (*b); + for (CXXCtorInitializer *member : memberInits) { assert(!member->isBaseInitializer()); assert(member->isAnyMemberInitializer() && "Delegating initializer on non-delegating constructor"); diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index b27d89707101e..3603fd1f2d291 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, VBase, Scissor } kind; + enum class InfoKind { VFPtr, Field, Base, VBase } kind; mlir::Type data; union { const FieldDecl *fieldDecl; @@ -934,44 +934,22 @@ void CIRRecordLowering::accumulateBases() { } 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()) + if (isEmptyRecordForLayout(astContext, base.getType())) continue; CharUnits offset = astRecordLayout.getVBaseClassOffset(baseDecl); // 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 (isOverlappingVBaseABI() && astContext.isNearlyEmpty(baseDecl) && + if (isOverlappingVBaseABI() && + astContext.isNearlyEmpty(baseDecl) && !hasOwnStorage(cxxRecordDecl, baseDecl)) { - members.push_back( - MemberInfo(offset, MemberInfo::InfoKind::VBase, nullptr, baseDecl)); + members.push_back(MemberInfo(offset, MemberInfo::InfoKind::VBase, nullptr, + baseDecl)); continue; } // If we've got a vtordisp, add it as a storage type. - if (astRecordLayout.getVBaseOffsetsMap() - .find(baseDecl) - ->second.hasVtorDisp()) + if (astRecordLayout.getVBaseOffsetsMap().find(baseDecl)->second.hasVtorDisp()) members.push_back(makeStorageInfo(offset - CharUnits::fromQuantity(4), getUIntNType(32))); members.push_back(MemberInfo(offset, MemberInfo::InfoKind::VBase, diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index bae55aa1e1928..e9a92ae0f01cb 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -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 = + cast<CXXRecordDecl>( + Base->getType()->castAs<RecordType>()->getOriginalDecl()) + ->getDefinitionOrSelf(); + // Add the offset. Offset += Layout.getBaseClassOffset(BaseDecl); @@ -298,7 +302,9 @@ Address CodeGenFunction::GetAddressOfBaseClass( // *start* with a step down to the correct virtual base subobject, // and hence will not require any further steps. if ((*Start)->isVirtual()) { - VBase = (*Start)->getType()->castAsCXXRecordDecl(); + VBase = cast<CXXRecordDecl>( + (*Start)->getType()->castAs<RecordType>()->getOriginalDecl()) + ->getDefinitionOrSelf(); ++Start; } @@ -553,7 +559,10 @@ static void EmitBaseInitializer(CodeGenFunction &CGF, Address ThisPtr = CGF.LoadCXXThisAddress(); - const auto *BaseClassDecl = BaseInit->getBaseClass()->castAsCXXRecordDecl(); + const Type *BaseType = BaseInit->getBaseClass(); + const auto *BaseClassDecl = + cast<CXXRecordDecl>(BaseType->castAs<RecordType>()->getOriginalDecl()) + ->getDefinitionOrSelf(); bool isBaseVirtual = BaseInit->isBaseVirtual(); @@ -1258,7 +1267,10 @@ namespace { static bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit) { const Type *BaseType = BaseInit->getBaseClass(); - return BaseType->castAsCXXRecordDecl()->isDynamicClass(); + const auto *BaseClassDecl = + cast<CXXRecordDecl>(BaseType->castAs<RecordType>()->getOriginalDecl()) + ->getDefinitionOrSelf(); + return BaseClassDecl->isDynamicClass(); } /// EmitCtorPrologue - This routine generates necessary code to initialize @@ -1365,7 +1377,10 @@ HasTrivialDestructorBody(ASTContext &Context, if (I.isVirtual()) continue; - const auto *NonVirtualBase = I.getType()->castAsCXXRecordDecl(); + const CXXRecordDecl *NonVirtualBase = + cast<CXXRecordDecl>( + I.getType()->castAs<RecordType>()->getOriginalDecl()) + ->getDefinitionOrSelf(); if (!HasTrivialDestructorBody(Context, NonVirtualBase, MostDerivedClassDecl)) return false; @@ -1374,7 +1389,10 @@ HasTrivialDestructorBody(ASTContext &Context, if (BaseClassDecl == MostDerivedClassDecl) { // Check virtual bases. for (const auto &I : BaseClassDecl->vbases()) { - const auto *VirtualBase = I.getType()->castAsCXXRecordDecl(); + const auto *VirtualBase = + cast<CXXRecordDecl>( + I.getType()->castAs<RecordType>()->getOriginalDecl()) + ->getDefinitionOrSelf(); if (!HasTrivialDestructorBody(Context, VirtualBase, MostDerivedClassDecl)) return false; @@ -1390,10 +1408,13 @@ FieldHasTrivialDestructorBody(ASTContext &Context, { QualType FieldBaseElementType = Context.getBaseElementType(Field->getType()); - auto *FieldClassDecl = FieldBaseElementType->getAsCXXRecordDecl(); - if (!FieldClassDecl) + const RecordType *RT = FieldBaseElementType->getAs<RecordType>(); + if (!RT) return true; + auto *FieldClassDecl = + cast<CXXRecordDecl>(RT->getOriginalDecl())->getDefinitionOrSelf(); + // The destructor for an implicit anonymous union member is never invoked. if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion()) return true; @@ -1886,7 +1907,11 @@ void CodeGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD, // We push them in the forward order so that they'll be popped in // the reverse order. for (const auto &Base : ClassDecl->vbases()) { - auto *BaseClassDecl = Base.getType()->castAsCXXRecordDecl(); + auto *BaseClassDecl = + cast<CXXRecordDecl>( + Base.getType()->castAs<RecordType>()->getOriginalDecl()) + ->getDefinitionOrSelf(); + if (BaseClassDecl->hasTrivialDestructor()) { // Under SanitizeMemoryUseAfterDtor, poison the trivial base class // memory. For non-trival base classes the same is done in the class @@ -2105,7 +2130,10 @@ void CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *ctor, void CodeGenFunction::destroyCXXObject(CodeGenFunction &CGF, Address addr, QualType type) { - const CXXDestructorDecl *dtor = type->castAsCXXRecordDecl()->getDestructor(); + const RecordType *rtype = type->castAs<RecordType>(); + const auto *record = + cast<CXXRecordDecl>(rtype->getOriginalDecl())->getDefinitionOrSelf(); + const CXXDestructorDecl *dtor = record->getDestructor(); assert(!dtor->isTrivial()); CGF.EmitCXXDestructorCall(dtor, Dtor_Complete, /*for vbase*/ false, /*Delegating=*/false, addr, type); @@ -2624,7 +2652,10 @@ void CodeGenFunction::getVTablePointers(BaseSubobject Base, // Traverse bases. for (const auto &I : RD->bases()) { - auto *BaseDecl = I.getType()->castAsCXXRecordDecl(); + auto *BaseDecl = cast<CXXRecordDecl>( + I.getType()->castAs<RecordType>()->getOriginalDecl()) + ->getDefinitionOrSelf(); + // Ignore classes without a vtable. if (!BaseDecl->isDynamicClass()) continue; @@ -2819,10 +2850,13 @@ void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T, Address Derived, if (!getLangOpts().CPlusPlus) return; - const auto *ClassDecl = T->getAsCXXRecordDecl(); - if (!ClassDecl) + auto *ClassTy = T->getAs<RecordType>(); + if (!ClassTy) return; + const auto *ClassDecl = + cast<CXXRecordDecl>(ClassTy->getOriginalDecl())->getDefinitionOrSelf(); + if (!ClassDecl->isCompleteDefinition() || !ClassDecl->isDynamicClass()) return; >From f1d23b35eeda3c1a18513dfc2a40c19d48a096b4 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Wed, 27 Aug 2025 14:51:38 -0700 Subject: [PATCH 3/4] Revert unintended clang/codegen changes --- clang/lib/CodeGen/CGClass.cpp | 60 ++++++++--------------------------- 1 file changed, 13 insertions(+), 47 deletions(-) diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index e9a92ae0f01cb..bae55aa1e1928 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -180,11 +180,7 @@ CharUnits CodeGenModule::computeNonVirtualBaseClassOffset( // Get the layout. const ASTRecordLayout &Layout = Context.getASTRecordLayout(RD); - const auto *BaseDecl = - cast<CXXRecordDecl>( - Base->getType()->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); - + const auto *BaseDecl = Base->getType()->castAsCXXRecordDecl(); // Add the offset. Offset += Layout.getBaseClassOffset(BaseDecl); @@ -302,9 +298,7 @@ Address CodeGenFunction::GetAddressOfBaseClass( // *start* with a step down to the correct virtual base subobject, // and hence will not require any further steps. if ((*Start)->isVirtual()) { - VBase = cast<CXXRecordDecl>( - (*Start)->getType()->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); + VBase = (*Start)->getType()->castAsCXXRecordDecl(); ++Start; } @@ -559,10 +553,7 @@ static void EmitBaseInitializer(CodeGenFunction &CGF, Address ThisPtr = CGF.LoadCXXThisAddress(); - const Type *BaseType = BaseInit->getBaseClass(); - const auto *BaseClassDecl = - cast<CXXRecordDecl>(BaseType->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); + const auto *BaseClassDecl = BaseInit->getBaseClass()->castAsCXXRecordDecl(); bool isBaseVirtual = BaseInit->isBaseVirtual(); @@ -1267,10 +1258,7 @@ namespace { static bool isInitializerOfDynamicClass(const CXXCtorInitializer *BaseInit) { const Type *BaseType = BaseInit->getBaseClass(); - const auto *BaseClassDecl = - cast<CXXRecordDecl>(BaseType->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); - return BaseClassDecl->isDynamicClass(); + return BaseType->castAsCXXRecordDecl()->isDynamicClass(); } /// EmitCtorPrologue - This routine generates necessary code to initialize @@ -1377,10 +1365,7 @@ HasTrivialDestructorBody(ASTContext &Context, if (I.isVirtual()) continue; - const CXXRecordDecl *NonVirtualBase = - cast<CXXRecordDecl>( - I.getType()->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); + const auto *NonVirtualBase = I.getType()->castAsCXXRecordDecl(); if (!HasTrivialDestructorBody(Context, NonVirtualBase, MostDerivedClassDecl)) return false; @@ -1389,10 +1374,7 @@ HasTrivialDestructorBody(ASTContext &Context, if (BaseClassDecl == MostDerivedClassDecl) { // Check virtual bases. for (const auto &I : BaseClassDecl->vbases()) { - const auto *VirtualBase = - cast<CXXRecordDecl>( - I.getType()->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); + const auto *VirtualBase = I.getType()->castAsCXXRecordDecl(); if (!HasTrivialDestructorBody(Context, VirtualBase, MostDerivedClassDecl)) return false; @@ -1408,13 +1390,10 @@ FieldHasTrivialDestructorBody(ASTContext &Context, { QualType FieldBaseElementType = Context.getBaseElementType(Field->getType()); - const RecordType *RT = FieldBaseElementType->getAs<RecordType>(); - if (!RT) + auto *FieldClassDecl = FieldBaseElementType->getAsCXXRecordDecl(); + if (!FieldClassDecl) return true; - auto *FieldClassDecl = - cast<CXXRecordDecl>(RT->getOriginalDecl())->getDefinitionOrSelf(); - // The destructor for an implicit anonymous union member is never invoked. if (FieldClassDecl->isUnion() && FieldClassDecl->isAnonymousStructOrUnion()) return true; @@ -1907,11 +1886,7 @@ void CodeGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD, // We push them in the forward order so that they'll be popped in // the reverse order. for (const auto &Base : ClassDecl->vbases()) { - auto *BaseClassDecl = - cast<CXXRecordDecl>( - Base.getType()->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); - + auto *BaseClassDecl = Base.getType()->castAsCXXRecordDecl(); if (BaseClassDecl->hasTrivialDestructor()) { // Under SanitizeMemoryUseAfterDtor, poison the trivial base class // memory. For non-trival base classes the same is done in the class @@ -2130,10 +2105,7 @@ void CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *ctor, void CodeGenFunction::destroyCXXObject(CodeGenFunction &CGF, Address addr, QualType type) { - const RecordType *rtype = type->castAs<RecordType>(); - const auto *record = - cast<CXXRecordDecl>(rtype->getOriginalDecl())->getDefinitionOrSelf(); - const CXXDestructorDecl *dtor = record->getDestructor(); + const CXXDestructorDecl *dtor = type->castAsCXXRecordDecl()->getDestructor(); assert(!dtor->isTrivial()); CGF.EmitCXXDestructorCall(dtor, Dtor_Complete, /*for vbase*/ false, /*Delegating=*/false, addr, type); @@ -2652,10 +2624,7 @@ void CodeGenFunction::getVTablePointers(BaseSubobject Base, // Traverse bases. for (const auto &I : RD->bases()) { - auto *BaseDecl = cast<CXXRecordDecl>( - I.getType()->castAs<RecordType>()->getOriginalDecl()) - ->getDefinitionOrSelf(); - + auto *BaseDecl = I.getType()->castAsCXXRecordDecl(); // Ignore classes without a vtable. if (!BaseDecl->isDynamicClass()) continue; @@ -2850,13 +2819,10 @@ void CodeGenFunction::EmitVTablePtrCheckForCast(QualType T, Address Derived, if (!getLangOpts().CPlusPlus) return; - auto *ClassTy = T->getAs<RecordType>(); - if (!ClassTy) + const auto *ClassDecl = T->getAsCXXRecordDecl(); + if (!ClassDecl) return; - const auto *ClassDecl = - cast<CXXRecordDecl>(ClassTy->getOriginalDecl())->getDefinitionOrSelf(); - if (!ClassDecl->isCompleteDefinition() || !ClassDecl->isDynamicClass()) return; >From 758e7e2ebde91fe17b125ab14a471914479d63f4 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Wed, 27 Aug 2025 14:57:50 -0700 Subject: [PATCH 4/4] Fix formatting --- clang/lib/CIR/CodeGen/CIRGenClass.cpp | 2 +- clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index f3de8cd50cdc0..9a27932c12dff 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -258,7 +258,7 @@ void CIRGenFunction::emitCtorPrologue(const CXXConstructorDecl *cd, auto nonVirtualBaseInits = llvm::make_range(virtualBaseEnd, nonVirtualBaseEnd); auto memberInits = llvm::make_range(nonVirtualBaseEnd, allInits.end()); - + const mlir::Value oldThisValue = cxxThisValue; auto emitInitializer = [&](CXXCtorInitializer *baseInit) { diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 3603fd1f2d291..6c7cf75aa2c99 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -941,15 +941,16 @@ void CIRRecordLowering::accumulateVBases() { CharUnits offset = astRecordLayout.getVBaseClassOffset(baseDecl); // 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 (isOverlappingVBaseABI() && - astContext.isNearlyEmpty(baseDecl) && + if (isOverlappingVBaseABI() && astContext.isNearlyEmpty(baseDecl) && !hasOwnStorage(cxxRecordDecl, baseDecl)) { - members.push_back(MemberInfo(offset, MemberInfo::InfoKind::VBase, nullptr, - baseDecl)); + members.push_back( + MemberInfo(offset, MemberInfo::InfoKind::VBase, nullptr, baseDecl)); continue; } // If we've got a vtordisp, add it as a storage type. - if (astRecordLayout.getVBaseOffsetsMap().find(baseDecl)->second.hasVtorDisp()) + if (astRecordLayout.getVBaseOffsetsMap() + .find(baseDecl) + ->second.hasVtorDisp()) members.push_back(makeStorageInfo(offset - CharUnits::fromQuantity(4), getUIntNType(32))); members.push_back(MemberInfo(offset, MemberInfo::InfoKind::VBase, _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits