Author: Andy Kaylor
Date: 2025-06-05T10:10:39-07:00
New Revision: 599b2a3475f1c40b34f2414e55de68c67ebe9d21

URL: 
https://github.com/llvm/llvm-project/commit/599b2a3475f1c40b34f2414e55de68c67ebe9d21
DIFF: 
https://github.com/llvm/llvm-project/commit/599b2a3475f1c40b34f2414e55de68c67ebe9d21.diff

LOG: [CIR] Add support for derived class declarations (#142823)

This adds the minimal support for declaring a pointer to a derived
class. This includes only the changes necessary to compute the record
layout for the derived class and declare a variable that points to it.

Support for accessing members of either the derived or base class is
deferred until a later change, as is support for declaring a variable
that is an instance of the derived class.

Added: 
    

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIRTypes.td
    clang/include/clang/CIR/MissingFeatures.h
    clang/lib/CIR/CodeGen/CIRGenBuilder.h
    clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
    clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
    clang/lib/CIR/CodeGen/CIRGenTypes.cpp
    clang/test/CIR/CodeGen/class.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 75eb9cc045a48..b29b703c47139 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -474,6 +474,18 @@ def CIR_RecordType : CIR_Type<"Record", "record",
   let genVerifyDecl = 1;
 
   let builders = [
+    // Create an identified and complete record type.
+    TypeBuilder<(ins
+      "llvm::ArrayRef<mlir::Type>":$members,
+      "mlir::StringAttr":$name,
+      "bool":$packed,
+      "bool":$padded,
+      "RecordKind":$kind
+    ), [{
+      return $_get($_ctxt, members, name, /*complete=*/true, packed, padded,
+                   kind);
+    }]>,
+
     // Create an identified and incomplete record type.
     TypeBuilder<(ins
       "mlir::StringAttr":$name,

diff  --git a/clang/include/clang/CIR/MissingFeatures.h 
b/clang/include/clang/CIR/MissingFeatures.h
index da1946ed73746..7f20424e9b675 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -136,6 +136,7 @@ struct MissingFeatures {
   static bool cxxSupport() { return false; }
   static bool recordZeroInit() { return false; }
   static bool zeroSizeRecordMembers() { return false; }
+  static bool recordLayoutVirtualBases() { return false; }
 
   // Various handling of deferred processing in CIRGenModule.
   static bool cgmRelease() { return false; }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index 5f17b5d58acaa..5f33ae1af35ee 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -96,6 +96,34 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
     llvm_unreachable("Unsupported record kind");
   }
 
+  /// Get a CIR named record type.
+  ///
+  /// If a record already exists and is complete, but the client tries to fetch
+  /// it with a 
diff erent set of attributes, this method will crash.
+  cir::RecordType getCompleteRecordTy(llvm::ArrayRef<mlir::Type> members,
+                                      llvm::StringRef name, bool packed,
+                                      bool padded) {
+    const auto nameAttr = getStringAttr(name);
+    auto kind = cir::RecordType::RecordKind::Struct;
+    assert(!cir::MissingFeatures::astRecordDeclAttr());
+
+    // Create or get the record.
+    auto type =
+        getType<cir::RecordType>(members, nameAttr, packed, padded, kind);
+
+    // If we found an existing type, verify that either it is incomplete or
+    // it matches the requested attributes.
+    assert(!type.isIncomplete() ||
+           (type.getMembers() == members && type.getPacked() == packed &&
+            type.getPadded() == padded));
+
+    // Complete an incomplete record or ensure the existing complete record
+    // matches the requested attributes.
+    type.complete(members, packed, padded);
+
+    return type;
+  }
+
   /// Get an incomplete CIR struct type. If we have a complete record
   /// declaration, we may create an incomplete type and then add the
   /// members, so \p rd here may be complete.

diff  --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h 
b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
index 2ece85b8aa0a3..ac8832b8c9b24 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
@@ -29,10 +29,18 @@ class CIRGenRecordLayout {
   /// as a complete object.
   cir::RecordType completeObjectType;
 
+  /// The CIR type for the non-virtual part of this record layout; used when
+  /// laying it out as a base subobject.
+  cir::RecordType baseSubobjectType;
+
   /// Map from (non-bit-field) record field to the corresponding cir record 
type
   /// field no. This info is populated by the record builder.
   llvm::DenseMap<const clang::FieldDecl *, unsigned> fieldIdxMap;
 
+  // FIXME: Maybe we could use CXXBaseSpecifier as the key and use a single map
+  // for both virtual and non-virtual bases.
+  llvm::DenseMap<const clang::CXXRecordDecl *, unsigned> nonVirtualBases;
+
   /// False if any direct or indirect subobject of this class, when considered
   /// as a complete object, requires a non-zero bitpattern when
   /// zero-initialized.
@@ -45,9 +53,11 @@ class CIRGenRecordLayout {
   unsigned zeroInitializableAsBase : 1;
 
 public:
-  CIRGenRecordLayout(cir::RecordType completeObjectType, bool 
zeroInitializable,
+  CIRGenRecordLayout(cir::RecordType completeObjectType,
+                     cir::RecordType baseSubobjectType, bool zeroInitializable,
                      bool zeroInitializableAsBase)
       : completeObjectType(completeObjectType),
+        baseSubobjectType(baseSubobjectType),
         zeroInitializable(zeroInitializable),
         zeroInitializableAsBase(zeroInitializableAsBase) {}
 
@@ -55,6 +65,10 @@ class CIRGenRecordLayout {
   /// this record.
   cir::RecordType getCIRType() const { return completeObjectType; }
 
+  /// Return the "base subobject" LLVM type associated with
+  /// this record.
+  cir::RecordType getBaseSubobjectCIRType() const { return baseSubobjectType; }
+
   /// Return cir::RecordType element number that corresponds to the field FD.
   unsigned getCIRFieldNo(const clang::FieldDecl *fd) const {
     fd = fd->getCanonicalDecl();

diff  --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index 53aa0aee36fc3..0aeef7fd89aef 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -40,15 +40,18 @@ struct CIRRecordLowering final {
   // member type that ensures correct rounding.
   struct MemberInfo final {
     CharUnits offset;
-    enum class InfoKind { Field } kind;
+    enum class InfoKind { Field, Base } kind;
     mlir::Type data;
     union {
       const FieldDecl *fieldDecl;
-      // CXXRecordDecl will be used here when base types are supported.
+      const CXXRecordDecl *cxxRecordDecl;
     };
     MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
                const FieldDecl *fieldDecl = nullptr)
-        : offset(offset), kind(kind), data(data), fieldDecl(fieldDecl) {};
+        : offset{offset}, kind{kind}, data{data}, fieldDecl{fieldDecl} {}
+    MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
+               const CXXRecordDecl *rd)
+        : offset{offset}, kind{kind}, data{data}, cxxRecordDecl{rd} {}
     // MemberInfos are sorted so we define a < operator.
     bool operator<(const MemberInfo &other) const {
       return offset < other.offset;
@@ -71,6 +74,8 @@ struct CIRRecordLowering final {
   /// Inserts padding everywhere it's needed.
   void insertPadding();
 
+  void accumulateBases(const CXXRecordDecl *cxxRecordDecl);
+  void accumulateVPtrs();
   void accumulateFields();
 
   CharUnits bitsToCharUnits(uint64_t bitOffset) {
@@ -89,6 +94,9 @@ struct CIRRecordLowering final {
   bool isZeroInitializable(const FieldDecl *fd) {
     return cirGenTypes.isZeroInitializable(fd->getType());
   }
+  bool isZeroInitializable(const RecordDecl *rd) {
+    return cirGenTypes.isZeroInitializable(rd);
+  }
 
   /// Wraps cir::IntType with some implicit arguments.
   mlir::Type getUIntNType(uint64_t numBits) {
@@ -112,6 +120,11 @@ struct CIRRecordLowering final {
                : cir::ArrayType::get(type, numberOfChars.getQuantity());
   }
 
+  // Gets the CIR BaseSubobject type from a CXXRecordDecl.
+  mlir::Type getStorageType(const CXXRecordDecl *RD) {
+    return cirGenTypes.getCIRGenRecordLayout(RD).getBaseSubobjectCIRType();
+  }
+
   mlir::Type getStorageType(const FieldDecl *fieldDecl) {
     mlir::Type type = cirGenTypes.convertTypeForMem(fieldDecl->getType());
     if (fieldDecl->isBitField()) {
@@ -145,6 +158,7 @@ struct CIRRecordLowering final {
   // Output fields, consumed by CIRGenTypes::computeRecordLayout
   llvm::SmallVector<mlir::Type, 16> fieldTypes;
   llvm::DenseMap<const FieldDecl *, unsigned> fieldIdxMap;
+  llvm::DenseMap<const CXXRecordDecl *, unsigned> nonVirtualBases;
   cir::CIRDataLayout dataLayout;
 
   LLVM_PREFERRED_TYPE(bool)
@@ -179,24 +193,20 @@ void CIRRecordLowering::lower() {
     return;
   }
 
-  assert(!cir::MissingFeatures::cxxSupport());
-
+  assert(!cir::MissingFeatures::recordLayoutVirtualBases());
   CharUnits size = astRecordLayout.getSize();
 
   accumulateFields();
 
   if (const auto *cxxRecordDecl = dyn_cast<CXXRecordDecl>(recordDecl)) {
-    if (cxxRecordDecl->getNumBases() > 0) {
-      CIRGenModule &cgm = cirGenTypes.getCGModule();
-      cgm.errorNYI(recordDecl->getSourceRange(),
-                   "CIRRecordLowering::lower: derived CXXRecordDecl");
-      return;
-    }
+    accumulateVPtrs();
+    accumulateBases(cxxRecordDecl);
     if (members.empty()) {
       appendPaddingBytes(size);
       assert(!cir::MissingFeatures::bitfields());
       return;
     }
+    assert(!cir::MissingFeatures::recordLayoutVirtualBases());
   }
 
   llvm::stable_sort(members);
@@ -223,8 +233,10 @@ void CIRRecordLowering::fillOutputFields() {
             fieldTypes.size() - 1;
       // A field without storage must be a bitfield.
       assert(!cir::MissingFeatures::bitfields());
+    } else if (member.kind == MemberInfo::InfoKind::Base) {
+      nonVirtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1;
     }
-    assert(!cir::MissingFeatures::cxxSupport());
+    assert(!cir::MissingFeatures::recordLayoutVirtualBases());
   }
 }
 
@@ -254,9 +266,14 @@ void CIRRecordLowering::calculateZeroInit() {
         continue;
       zeroInitializable = zeroInitializableAsBase = false;
       return;
+    } else if (member.kind == MemberInfo::InfoKind::Base) {
+      if (isZeroInitializable(member.cxxRecordDecl))
+        continue;
+      zeroInitializable = false;
+      if (member.kind == MemberInfo::InfoKind::Base)
+        zeroInitializableAsBase = false;
     }
-    // TODO(cir): handle base types
-    assert(!cir::MissingFeatures::cxxSupport());
+    assert(!cir::MissingFeatures::recordLayoutVirtualBases());
   }
 }
 
@@ -317,6 +334,27 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, 
cir::RecordType *ty) {
   lowering.lower();
 
   // If we're in C++, compute the base subobject type.
+  cir::RecordType baseTy;
+  if (llvm::isa<CXXRecordDecl>(rd) && !rd->isUnion() &&
+      !rd->hasAttr<FinalAttr>()) {
+    baseTy = *ty;
+    if (lowering.astRecordLayout.getNonVirtualSize() !=
+        lowering.astRecordLayout.getSize()) {
+      CIRRecordLowering baseLowering(*this, rd, /*Packed=*/lowering.packed);
+      baseLowering.lower();
+      std::string baseIdentifier = getRecordTypeName(rd, ".base");
+      baseTy =
+          builder.getCompleteRecordTy(baseLowering.fieldTypes, baseIdentifier,
+                                      baseLowering.packed, 
baseLowering.padded);
+      // TODO(cir): add something like addRecordTypeName
+
+      // BaseTy and Ty must agree on their packedness for getCIRFieldNo to work
+      // on both of them with the same index.
+      assert(lowering.packed == baseLowering.packed &&
+             "Non-virtual and complete types must agree on packedness");
+    }
+  }
+
   if (llvm::isa<CXXRecordDecl>(rd) && !rd->isUnion() &&
       !rd->hasAttr<FinalAttr>()) {
     if (lowering.astRecordLayout.getNonVirtualSize() !=
@@ -332,10 +370,13 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, 
cir::RecordType *ty) {
   ty->complete(lowering.fieldTypes, lowering.packed, lowering.padded);
 
   auto rl = std::make_unique<CIRGenRecordLayout>(
-      ty ? *ty : cir::RecordType(), (bool)lowering.zeroInitializable,
-      (bool)lowering.zeroInitializableAsBase);
+      ty ? *ty : cir::RecordType{}, baseTy ? baseTy : cir::RecordType{},
+      (bool)lowering.zeroInitializable, 
(bool)lowering.zeroInitializableAsBase);
 
   assert(!cir::MissingFeatures::recordZeroInit());
+
+  rl->nonVirtualBases.swap(lowering.nonVirtualBases);
+
   assert(!cir::MissingFeatures::cxxSupport());
   assert(!cir::MissingFeatures::bitfields());
 
@@ -415,3 +456,38 @@ void CIRRecordLowering::lowerUnion() {
   if (layoutSize % getAlignment(storageType))
     packed = true;
 }
+
+void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) {
+  // If we've got a primary virtual base, we need to add it with the bases.
+  if (astRecordLayout.isPrimaryBaseVirtual()) {
+    cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
+                                       "accumulateBases: primary virtual 
base");
+  }
+
+  // Accumulate the non-virtual bases.
+  for ([[maybe_unused]] const auto &base : cxxRecordDecl->bases()) {
+    if (base.isVirtual()) {
+      cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
+                                         "accumulateBases: virtual base");
+      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();
+    if (!baseDecl->isEmpty() &&
+        !astContext.getASTRecordLayout(baseDecl).getNonVirtualSize().isZero()) 
{
+      
members.push_back(MemberInfo(astRecordLayout.getBaseClassOffset(baseDecl),
+                                   MemberInfo::InfoKind::Base,
+                                   getStorageType(baseDecl), baseDecl));
+    }
+  }
+}
+
+void CIRRecordLowering::accumulateVPtrs() {
+  if (astRecordLayout.hasOwnVFPtr())
+    cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
+                                       "accumulateVPtrs: hasOwnVFPtr");
+  if (astRecordLayout.hasOwnVBPtr())
+    cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
+                                       "accumulateVPtrs: hasOwnVBPtr");
+}

diff  --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp 
b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index d962372a4bcc0..f748649c8ac70 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -239,9 +239,10 @@ mlir::Type CIRGenTypes::convertRecordDeclType(const 
clang::RecordDecl *rd) {
 
   // Force conversion of non-virtual base classes recursively.
   if (const auto *cxxRecordDecl = dyn_cast<CXXRecordDecl>(rd)) {
-    if (cxxRecordDecl->getNumBases() > 0) {
-      cgm.errorNYI(rd->getSourceRange(),
-                   "convertRecordDeclType: derived CXXRecordDecl");
+    for (const auto &base : cxxRecordDecl->bases()) {
+      if (base.isVirtual())
+        continue;
+      convertRecordDeclType(base.getType()->castAs<RecordType>()->getDecl());
     }
   }
 

diff  --git a/clang/test/CIR/CodeGen/class.cpp 
b/clang/test/CIR/CodeGen/class.cpp
index dd280d3d906ba..7c41bdb7112e9 100644
--- a/clang/test/CIR/CodeGen/class.cpp
+++ b/clang/test/CIR/CodeGen/class.cpp
@@ -6,13 +6,19 @@
 // RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
 
 // CIR: !rec_IncompleteC = !cir.record<class "IncompleteC" incomplete>
+// CIR: !rec_Base = !cir.record<class "Base" {!s32i}>
 // CIR: !rec_CompleteC = !cir.record<class "CompleteC" {!s32i, !s8i}>
+// CIR: !rec_Derived = !cir.record<class "Derived" {!rec_Base, !s32i}>
 
 // Note: LLVM and OGCG do not emit the type for incomplete classes.
 
 // LLVM: %class.CompleteC = type { i32, i8 }
+// LLVM: %class.Derived = type { %class.Base, i32 }
+// LLVM: %class.Base = type { i32 }
 
 // OGCG: %class.CompleteC = type { i32, i8 }
+// OGCG: %class.Derived = type { %class.Base, i32 }
+// OGCG: %class.Base = type { i32 }
 
 class IncompleteC;
 IncompleteC *p;
@@ -32,3 +38,16 @@ CompleteC cc;
 // CIR:       cir.global external @cc = #cir.zero : !rec_CompleteC
 // LLVM:  @cc = global %class.CompleteC zeroinitializer
 // OGCG:  @cc = global %class.CompleteC zeroinitializer
+
+class Base {
+public:
+  int a;
+};
+
+class Derived : public Base {
+public:
+  int b;
+};
+
+int use(Derived *d) { return d->b; }
+


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to