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

Reply via email to