[llvm-branch-commits] [clang] [CIR] Mark record members as data, pad, or empty in CIRGen (PR #215175)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sun Aug 9 21:08:23 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Adam Smith (adams381)

<details>
<summary>Changes</summary>

The per-member marks landed with no producer, so every record still reads as though all of its members held data.  A record type still can't say whether it carries anything for argument passing.  The x86_64 classifier has to know that before it can drop an empty class from a signature.

CIRGen fills the marks in now.  Every member goes through one `addField` that takes its kind, so a new push site can't quietly inherit a default.

A field's mark comes from `isEmptyFieldForABI`, ported from `isEmptyField` in `ABIInfoImpl.cpp`.  Taking the ABI predicate rather than the layout one is what gets C right.  Given `struct E {}`, a struct holding one `E` is empty for the ABI in C but not in C++.

An assert on every record checks the marks against `isEmptyRecordForABI`, so the existing `-fclangir` tests exercise them.

One case is now NYI.  A `[[no_unique_address]]` field that `isEmptyFieldForLayout` drops from the layout can still hold ABI data.  With the field gone there is no member left to mark, so CIRGen says so rather than emit a record that understates what it holds.

This is the second of three PRs.  Nothing reads the marks yet.  The `padded` bool stays, and `computeStructDataSize` still uses it.  The next PR will properly use the marks and remove `padded`.

Depends on [#<!-- -->215174](https://github.com/llvm/llvm-project/pull/215174).

Assisted-by: Cursor / claude-opus-5


---

Patch is 62.37 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/215175.diff


27 Files Affected:

- (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+1-1) 
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+3-2) 
- (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.cpp (+2-1) 
- (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+5-13) 
- (modified) clang/lib/CIR/CodeGen/CIRGenRecordLayout.h (+1-1) 
- (modified) clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp (+169-38) 
- (modified) clang/lib/CIR/CodeGen/TargetInfo.cpp (+56) 
- (modified) clang/lib/CIR/CodeGen/TargetInfo.h (+23) 
- (modified) clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp (+6-3) 
- (modified) clang/test/CIR/CodeGen/bitfields.c (+1-1) 
- (modified) clang/test/CIR/CodeGen/cleanup.cpp (+1-1) 
- (modified) clang/test/CIR/CodeGen/dumb-record.cpp (+2-2) 
- (modified) clang/test/CIR/CodeGen/empty-union.cpp (+1-1) 
- (modified) clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp (+1-1) 
- (modified) clang/test/CIR/CodeGen/member-functions.cpp (+1-1) 
- (modified) clang/test/CIR/CodeGen/no-unique-address.cpp (+2-2) 
- (modified) clang/test/CIR/CodeGen/paren-list-agg-init.cpp (+6-6) 
- (modified) clang/test/CIR/CodeGen/pointer-to-empty-data-member.cpp (+5-5) 
- (added) clang/test/CIR/CodeGen/record-member-kinds-nyi.cpp (+14) 
- (added) clang/test/CIR/CodeGen/record-member-kinds.c (+97) 
- (added) clang/test/CIR/CodeGen/record-member-kinds.cpp (+141) 
- (modified) clang/test/CIR/CodeGen/record-type-metadata.cpp (+2-2) 
- (modified) clang/test/CIR/CodeGen/struct.c (+1-1) 
- (modified) clang/test/CIR/CodeGen/template-specialization.cpp (+1-1) 
- (modified) clang/test/CIR/CodeGen/vtt.cpp (+2-2) 
- (modified) clang/test/CIR/CodeGenCXX/zero_init_bases.cpp (+1-1) 
- (modified) clang/test/CIR/CodeGenCoroutines/coro-task.cpp (+9-9) 


``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 71585cd83fb66..9a726a3619ffc 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1564,7 +1564,7 @@ def CIR_BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> {
       The CIR representation of the struct `S` might look like:
       ```
         !rec_S = !cir.record<struct "S" packed padded {!u64i, !u16i,
-                                                       !cir.array<!u8i x 2>}>
+                                                   pad !cir.array<!u8i x 2>}>
       ```
       And the bitfield info attribute for member `a` would be:
       ```
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index d0f3c9ee6715f..513d765fff23d 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3752,7 +3752,7 @@ def CIR_SetBitfieldOp : CIR_Op<"set_bitfield"> {
     ```
     // 'e' is in the storage with the index 1
     !record_type = !cir.record<struct "S" packed padded {!u64i, !u16i,
-                               !cir.array<!u8i x 2>} #cir.record.decl.ast>
+                               pad !cir.array<!u8i x 2>} #cir.record.decl.ast>
     #bfi_e = #cir.bitfield_info<name = "e", storage_type = !u16i, size = 15,
                                 offset = 0, is_signed = true>
 
@@ -3842,7 +3842,8 @@ def CIR_GetBitfieldOp : CIR_Op<"get_bitfield"> {
 
     ```
     // 'e' is in the storage with the index 1
-    !cir.record<struct "S" packed padded {!u64i, !u16i, !cir.array<!u8i x 2>}>
+    !cir.record<struct "S" packed padded {!u64i, !u16i,
+                                          pad !cir.array<!u8i x 2>}>
     #bfi_e = #cir.bitfield_info<name = "e", storage_type = !u16i, size = 15,
                                 offset = 0, is_signed = true>
 
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
index a562c4b7b763f..d339c35fbffa0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
@@ -208,7 +208,8 @@ cir::RecordType clang::CIRGen::CIRGenBuilderTy::getCompleteRecordType(
   if (name.empty())
     return getAnonRecordTy(members, packed, padded);
 
-  return getCompleteNamedRecordType(members, packed, padded, name);
+  return getCompleteNamedRecordType(members, packed, padded, name,
+                                    /*memberKinds=*/{});
 }
 
 mlir::Attribute clang::CIRGen::CIRGenBuilderTy::getConstRecordOrZeroAttr(
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index c906e65a132c2..f414f67c76fbe 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -154,26 +154,18 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
   ///
   /// If a record already exists and is complete, but the client tries to fetch
   /// it with a different set of attributes, this method will crash.
-  cir::RecordType getCompleteNamedRecordType(llvm::ArrayRef<mlir::Type> members,
-                                             bool packed, bool padded,
-                                             llvm::StringRef name) {
+  cir::RecordType getCompleteNamedRecordType(
+      llvm::ArrayRef<mlir::Type> members, bool packed, bool padded,
+      llvm::StringRef name, llvm::ArrayRef<cir::RecordMemberKind> memberKinds) {
     const auto nameAttr = getStringAttr(name);
     assert(!cir::MissingFeatures::astRecordDeclAttr());
 
     // Create or get the struct type (named anonymous struct helper — always
     // struct, never class or union at this call site).
     auto type = cir::StructType::get(getContext(), members, nameAttr, packed,
-                                     padded, /*is_class=*/false);
+                                     padded, /*is_class=*/false, memberKinds);
 
-    // 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);
+    type.complete(members, packed, padded, memberKinds);
 
     return type;
   }
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
index 9733d18f52ce8..c4968f3024d99 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
@@ -46,7 +46,7 @@ namespace clang::CIRGen {
 /// struct to a 4-byte alignment.
 ///
 ///   !rec_S = !cir.record<struct "S" padded {!s8i, !s8i, !s8i, !u16i,
-///   !cir.array<!u8i x 3>}>
+///   pad !cir.array<!u8i x 3>}>
 ///
 /// When generating code to access more_bits, we'll generate something
 /// essentially like this:
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index e4476b88ec6f7..8258f1bf1aa33 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -13,6 +13,7 @@
 #include "CIRGenBuilder.h"
 #include "CIRGenModule.h"
 #include "CIRGenTypes.h"
+#include "TargetInfo.h"
 
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
@@ -43,16 +44,23 @@ struct CIRRecordLowering final {
     CharUnits offset;
     enum class InfoKind { VFPtr, Field, Base, VBase } kind;
     mlir::Type data;
+    /// What this member holds, recorded on the CIR record type so that later
+    /// passes can tell declared storage from compiler-inserted padding.  Every
+    /// constructor takes it, so a new member cannot be added without deciding.
+    cir::RecordMemberKind memberKind;
     union {
       const FieldDecl *fieldDecl;
       const CXXRecordDecl *cxxRecordDecl;
     };
     MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
+               cir::RecordMemberKind memberKind,
                const FieldDecl *fieldDecl = nullptr)
-        : offset{offset}, kind{kind}, data{data}, fieldDecl{fieldDecl} {}
+        : offset{offset}, kind{kind}, data{data}, memberKind{memberKind},
+          fieldDecl{fieldDecl} {}
     MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
-               const CXXRecordDecl *rd)
-        : offset{offset}, kind{kind}, data{data}, cxxRecordDecl{rd} {}
+               cir::RecordMemberKind memberKind, const CXXRecordDecl *rd)
+        : offset{offset}, kind{kind}, data{data}, memberKind{memberKind},
+          cxxRecordDecl{rd} {}
     // MemberInfos are sorted so we define a < operator.
     bool operator<(const MemberInfo &other) const {
       return offset < other.offset;
@@ -63,8 +71,9 @@ struct CIRRecordLowering final {
                     bool packed);
 
   /// Constructs a MemberInfo instance from an offset and mlir::Type.
-  MemberInfo makeStorageInfo(CharUnits offset, mlir::Type data) {
-    return MemberInfo(offset, MemberInfo::InfoKind::Field, data);
+  MemberInfo makeStorageInfo(CharUnits offset, mlir::Type data,
+                             cir::RecordMemberKind memberKind) {
+    return MemberInfo(offset, MemberInfo::InfoKind::Field, data, memberKind);
   }
 
   // Layout routines.
@@ -83,7 +92,7 @@ struct CIRRecordLowering final {
   void accumulateBases();
   void accumulateVPtrs();
   void accumulateVBases();
-  void accumulateFields();
+  void accumulateFields(bool nonVirtualBaseType);
   RecordDecl::field_iterator
   accumulateBitFields(RecordDecl::field_iterator field,
                       RecordDecl::field_iterator fieldEnd);
@@ -141,6 +150,22 @@ struct CIRRecordLowering final {
     return cirGenTypes.isZeroInitializable(rd);
   }
 
+  /// The mark for a field.
+  cir::RecordMemberKind getFieldMemberKind(const FieldDecl *fd) {
+    return isEmptyFieldForABI(astContext, fd) ? cir::RecordMemberKind::Empty
+                                              : cir::RecordMemberKind::Data;
+  }
+
+  /// The mark for a base subobject.  A base contributes no ABI data when it is
+  /// empty for the ABI, which is not the same as CXXRecordDecl::isEmpty(): a
+  /// base holding only unnamed bit-fields is laid out but carries no data.
+  cir::RecordMemberKind getBaseMemberKind(const CXXRecordDecl *baseDecl) {
+    return isEmptyRecordForABI(astContext,
+                               astContext.getCanonicalTagType(baseDecl))
+               ? cir::RecordMemberKind::Empty
+               : cir::RecordMemberKind::Data;
+  }
+
   /// Wraps cir::IntType with some implicit arguments.
   mlir::Type getUIntNType(uint64_t numBits) {
     unsigned alignedBits = llvm::PowerOf2Ceil(numBits);
@@ -204,10 +229,26 @@ struct CIRRecordLowering final {
       assert(!unionPadding && "at most one union tail-padding type");
       unionPadding = padTy;
     } else {
-      fieldTypes.push_back(padTy);
+      addField(padTy, cir::RecordMemberKind::Pad);
     }
   }
 
+  /// The single entry point for appending an output field.
+  void addField(mlir::Type ty, cir::RecordMemberKind memberKind) {
+    fieldTypes.push_back(ty);
+    fieldKinds.push_back(memberKind);
+  }
+
+  void clearFields() {
+    fieldTypes.clear();
+    fieldKinds.clear();
+  }
+
+  llvm::ArrayRef<mlir::Type> getFieldTypes() const { return fieldTypes; }
+  llvm::ArrayRef<cir::RecordMemberKind> getFieldKinds() const {
+    return fieldKinds;
+  }
+
   CIRGenTypes &cirGenTypes;
   CIRGenBuilderTy &builder;
   const ASTContext &astContext;
@@ -216,8 +257,6 @@ struct CIRRecordLowering final {
   const ASTRecordLayout &astRecordLayout;
   // Helpful intermediate data-structures
   std::vector<MemberInfo> members;
-  // Output fields, consumed by CIRGenTypes::computeRecordLayout
-  llvm::SmallVector<mlir::Type, 16> fieldTypes;
   mlir::Type unionPadding;
   llvm::DenseMap<const FieldDecl *, CIRGenBitFieldInfo> bitFields;
   llvm::DenseMap<const FieldDecl *, unsigned> fieldIdxMap;
@@ -233,8 +272,19 @@ struct CIRRecordLowering final {
   unsigned packed : 1;
   LLVM_PREFERRED_TYPE(bool)
   unsigned padded : 1;
+  /// Whether a field was dropped whose data no member can represent.  Reported
+  /// as NYI.  Until that gap is closed the completed type reads as ABI-empty
+  /// when it is not, so the differential asserts below cannot run on it.
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned droppedFieldHoldingData : 1;
 
 private:
+  // Output fields, consumed by CIRGenTypes::computeRecordLayout.  Private so
+  // that every append goes through addField and fieldKinds stays parallel to
+  // fieldTypes.
+  llvm::SmallVector<mlir::Type, 16> fieldTypes;
+  llvm::SmallVector<cir::RecordMemberKind> fieldKinds;
+
   CIRRecordLowering(const CIRRecordLowering &) = delete;
   void operator=(const CIRRecordLowering &) = delete;
 }; // CIRRecordLowering
@@ -249,7 +299,7 @@ CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes,
           cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)},
       dataLayout{cirGenTypes.getCGModule().getModule()},
       zeroInitializable{true}, zeroInitializableAsBase{true}, packed{packed},
-      padded{false} {}
+      padded{false}, droppedFieldHoldingData{false} {}
 
 void CIRRecordLowering::setBitFieldInfo(const FieldDecl *fd,
                                         CharUnits startOffset,
@@ -288,7 +338,7 @@ void CIRRecordLowering::lower(bool nonVirtualBaseType) {
   CharUnits size = nonVirtualBaseType ? astRecordLayout.getNonVirtualSize()
                                       : astRecordLayout.getSize();
 
-  accumulateFields();
+  accumulateFields(nonVirtualBaseType);
 
   if (cxxRecordDecl) {
     accumulateVPtrs();
@@ -306,7 +356,10 @@ void CIRRecordLowering::lower(bool nonVirtualBaseType) {
   // TODO: Verify bitfield clipping
   assert(!cir::MissingFeatures::checkBitfieldClipping());
 
-  members.push_back(makeStorageInfo(size, getUIntNType(8)));
+  // The sentinel is popped before fillOutputFields, so its kind never reaches
+  // the type.
+  members.push_back(
+      makeStorageInfo(size, getUIntNType(8), cir::RecordMemberKind::Data));
   determinePacked(nonVirtualBaseType);
   insertPadding();
   members.pop_back();
@@ -318,8 +371,11 @@ void CIRRecordLowering::lower(bool nonVirtualBaseType) {
 
 void CIRRecordLowering::fillOutputFields() {
   for (const MemberInfo &member : members) {
+    // A bit-field occupant and a primary virtual base without own storage both
+    // carry null data, so the kind must be appended inside this guard or every
+    // later mark shifts by one while the two lengths still agree.
     if (member.data)
-      fieldTypes.push_back(member.data);
+      addField(member.data, member.memberKind);
     if (member.kind == MemberInfo::InfoKind::Field) {
       if (member.fieldDecl)
         fieldIdxMap[member.fieldDecl->getCanonicalDecl()] =
@@ -352,6 +408,9 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
     // used to determine if the ASTRecordLayout is treating these two bitfields
     // as contiguous. StartBitOffset is offset of the beginning of the Run.
     uint64_t startBitOffset, tail = 0;
+    // Where the current run's storage member sits in members, so that a named
+    // occupant joining the run can promote it to data.
+    size_t runStorageIdx = 0;
     for (; field != fieldEnd && field->isBitField(); ++field) {
       // Zero-width bitfields end runs.
       if (field->isZeroLengthBitField()) {
@@ -368,15 +427,22 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
         tail = startBitOffset + dataLayout.getTypeAllocSizeInBits(type);
         // Add the storage member to the record.  This must be added to the
         // record before the bitfield members so that it gets laid out before
-        // the bitfields it contains get laid out.
-        members.push_back(
-            makeStorageInfo(bitsToCharUnits(startBitOffset), type));
+        // the bitfields it contains get laid out.  The run is only known one
+        // field at a time here, so the unit starts out holding no data and is
+        // promoted below when a named occupant lands in it.
+        runStorageIdx = members.size();
+        members.push_back(makeStorageInfo(bitsToCharUnits(startBitOffset), type,
+                                          cir::RecordMemberKind::Empty));
       }
+      assert(members[runStorageIdx].offset == bitsToCharUnits(startBitOffset) &&
+             "runStorageIdx must name the current run's storage");
+      if (!field->isUnnamedBitField())
+        members[runStorageIdx].memberKind = cir::RecordMemberKind::Data;
       // Bitfields get the offset of their storage but come afterward and remain
       // there after a stable sort.
       members.push_back(MemberInfo(bitsToCharUnits(startBitOffset),
                                    MemberInfo::InfoKind::Field, nullptr,
-                                   *field));
+                                   cir::RecordMemberKind::Data, *field));
     }
     return field;
   }
@@ -548,11 +614,20 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
           assert(getSize(type) == accessSize &&
                  "Unclipped access must be clipped");
         }
-        members.push_back(makeStorageInfo(beginOffset, type));
+        // An unnamed bit-field of any width occupies no ABI class, so a unit
+        // made up of nothing but those is declared storage holding no data.
+        const bool hasNamedOccupant = llvm::any_of(
+            llvm::make_range(begin, bestEnd),
+            [](const FieldDecl *fd) { return !fd->isUnnamedBitField(); });
+        members.push_back(makeStorageInfo(beginOffset, type,
+                                          hasNamedOccupant
+                                              ? cir::RecordMemberKind::Data
+                                              : cir::RecordMemberKind::Empty));
         for (; begin != bestEnd; ++begin)
           if (!begin->isZeroLengthBitField())
-            members.push_back(MemberInfo(
-                beginOffset, MemberInfo::InfoKind::Field, nullptr, *begin));
+            members.push_back(MemberInfo(beginOffset,
+                                         MemberInfo::InfoKind::Field, nullptr,
+                                         cir::RecordMemberKind::Data, *begin));
       }
       // Reset to start a new span.
       field = bestEnd;
@@ -570,7 +645,7 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
   return field;
 }
 
-void CIRRecordLowering::accumulateFields() {
+void CIRRecordLowering::accumulateFields(bool nonVirtualBaseType) {
   for (RecordDecl::field_iterator field = recordDecl->field_begin(),
                                   fieldEnd = recordDecl->field_end();
        field != fieldEnd;) {
@@ -591,17 +666,34 @@ void CIRRecordLowering::accumulateFields() {
       // problem with taking the address of one of these, so it is in practice
       // not a horrifyingly problematic issue.
       assert(!cir::MissingFeatures::noUniqueAddressLayout());
+      // Dropping the field leaves no member to mark, so its bytes read as
+      // padding.  That is only sound when the field carries no ABI data
+      // either, which isEmptyFieldForLayout does not guarantee.  Report the
+      // gap rather than claim an emptiness the record does not have.  The base
+      // subobject lowering sees the same field, so only the complete object
+      // reports it.
+      if (!isEmptyFieldForABI(astContext, *field)) {
+        if (!nonVirtualBaseType)
+          cirGenTypes.getCGModule().errorNYI(
+              field->getSourceRange(),
+              "[[no_unique_address]] field that is empty for layout but holds "
+              "data for the ABI");
+        droppedFieldHoldingData = true;
+      }
       ++field;
     } else {
       // Use base subobject layout for potentially-overlapping fields,
       // as it is done in RecordLayoutBuilder.
+      //
+      // The mark comes from isEmptyFieldForABI, not the isEmptyFieldForLayout
+      // above.  Neither predicate subsumes the other.
       members.push_back(MemberInfo(
           bitsToCharUnits(getFieldBitOffset(*field)),
           MemberInfo::InfoKind::Field,
           field->isPotentiallyOverlapping()
               ? getStorageType(field->getType()->getAsCXXRecordDecl())
               : getStorageType(*field),
-          *field));
+          getFieldMemberKind(*field), *field));
       ++field;
     }
   }
@@ -679,7 +771,8 @@ void CIRRecordLowering::insertPadding() {
   // Add the padding to the Members list and sort it.
   for (const std::pair<CharUnits, CharUnits> &paddingPair : padding)
     members.push_back(makeStorageInfo(paddingPair.first,
-                                      getByteArrayType(paddingPair.second)));
+                                      getByteArrayType(paddingPair.second),
+                                      cir::RecordMemberKind::Pad));
   llvm::stable_sort(members);
 }
 
@@ -696,6 +789,18 @@ convertRecordArgPassingKind(RecordArgPassingKind kind) {
   llvm_unreachable("unknown RecordArgPassingKind");
 }
 
+/// Whether the member kinds on \p recordTy answer the record's ABI emptiness
+/// the same way the AST predicate does.  A lowering that dropped a field
+/// holding data has no member left to carry that data, so it is exempt.
+[[maybe_unused]] static bool
+marksMatchABIEmptiness(const ASTContext &astContext, const RecordDecl *rd,
+                       cir::RecordType recordTy, bool droppedFieldHoldingData) {
+  if (droppedFieldHoldingData)
+    return true;
+  return cir::allMembersNonData(recordTy) ==
+         isEmptyRecordForABI(astContext, astContext.getCanonicalTagType(rd));
+}
+
 std::unique_ptr<CIRGenRecordLayout>
 CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
   CIRRecordLowering lowering(*this, rd, /*packed=*/false);
@@ -726,8 +831,8 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
       baseLowering.lower(/*nonVirtualBaseType=*/true);
       std::string baseIdentifier = getRecordTypeName(rd, ".base");
       baseTy = builder.getCompleteNamedRecordType(
-          baseLowering.fieldTypes, baseLowering.packed, baseLowering.padde...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/215175


More information about the llvm-branch-commits mailing list