Author: Adam Smith
Date: 2026-08-17T11:24:17-05:00
New Revision: 5f81b8d924fba12b4e851352959b5c4a54260d9a

URL: 
https://github.com/llvm/llvm-project/commit/5f81b8d924fba12b4e851352959b5c4a54260d9a
DIFF: 
https://github.com/llvm/llvm-project/commit/5f81b8d924fba12b4e851352959b5c4a54260d9a.diff

LOG: [CIR] Derive record padding from the member marks (#215176)r

Now that every record member carries a kind, the record-level `padded`
bool is redundant: a record is padded exactly when some member is marked
pad. Drop the parameter and answer `getPadded()` from the marks.

That also fixes `computeStructDataSize`, which had read the bool as "the
last member is tail padding" and so dropped a real member when padding
sat between two data members. It now drops the trailing run, so an
all-pad record sizes to zero.

Assisted-by: Cursor / claude-opus-5

Added: 
    clang/test/CIR/Lowering/copy-skip-tail-padding.cir

Modified: 
    clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
    clang/include/clang/CIR/Dialect/IR/CIROps.td
    clang/include/clang/CIR/Dialect/IR/CIRTypes.h
    clang/include/clang/CIR/Dialect/IR/CIRTypes.td
    clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
    clang/lib/CIR/CodeGen/CIRGenAsm.cpp
    clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
    clang/lib/CIR/CodeGen/CIRGenBuilder.h
    clang/lib/CIR/CodeGen/CIRGenBuiltinAMDGPU.cpp
    clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
    clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
    clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
    clang/lib/CIR/CodeGen/CIRGenModule.cpp
    clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
    clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
    clang/lib/CIR/CodeGen/CIRGenTypes.cpp
    clang/lib/CIR/CodeGen/CIRGenVTables.cpp
    clang/lib/CIR/Dialect/IR/CIRTypes.cpp
    clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
    clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
    clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
    clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
    clang/test/CIR/CodeGen/atomic.c
    clang/test/CIR/CodeGen/bitfields.c
    clang/test/CIR/CodeGen/cleanup.cpp
    clang/test/CIR/CodeGen/dumb-record.cpp
    clang/test/CIR/CodeGen/empty-union.cpp
    clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp
    clang/test/CIR/CodeGen/member-functions.cpp
    clang/test/CIR/CodeGen/no-unique-address.cpp
    clang/test/CIR/CodeGen/paren-list-agg-init.cpp
    clang/test/CIR/CodeGen/pointer-to-empty-data-member.cpp
    clang/test/CIR/CodeGen/record-member-kinds.c
    clang/test/CIR/CodeGen/record-member-kinds.cpp
    clang/test/CIR/CodeGen/record-type-metadata.cpp
    clang/test/CIR/CodeGen/struct.c
    clang/test/CIR/CodeGen/template-specialization.cpp
    clang/test/CIR/CodeGen/vtt.cpp
    clang/test/CIR/CodeGenCXX/zero_init_bases.cpp
    clang/test/CIR/CodeGenCoroutines/coro-task.cpp
    clang/test/CIR/IR/array-ctor.cir
    clang/test/CIR/IR/array-dtor.cir
    clang/test/CIR/IR/construct-catch-param.cir
    clang/test/CIR/IR/func-attrs.cir
    clang/test/CIR/IR/global-init.cir
    clang/test/CIR/IR/invalid-array-structor.cir
    clang/test/CIR/IR/invalid-construct-catch-param.cir
    clang/test/CIR/IR/invalid-delete-array.cir
    clang/test/CIR/IR/invalid-loop-cleanup.cir
    clang/test/CIR/IR/loop-cleanup.cir
    clang/test/CIR/IR/struct.cir
    clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir
    clang/test/CIR/Transforms/cxx-abi-lowering-attrs.cir
    clang/test/CIR/Transforms/eh-abi-lowering-construct-catch-invalid.cir
    clang/test/CIR/Transforms/eh-abi-lowering-construct-catch.cir
    clang/test/CIR/Transforms/flatten-cleanup-scope-eh.cir
    clang/test/CIR/Transforms/flatten-loop-cleanup.cir
    clang/test/CIR/Transforms/mem2reg.cir
    clang/unittests/CIR/PointerLikeTest.cpp
    clang/unittests/CIR/RecordMemberKindTest.cpp
    clang/unittests/CIR/RecordTypeMetadataTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td 
b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 29e82b9392b82..0871d48506cf8 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1563,8 +1563,8 @@ 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>}>
+        !rec_S = !cir.struct<"S" packed {!u64i, !u16i,
+                                         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 8f8159a356b5a..ccf0a21d56e04 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3761,8 +3761,8 @@ 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>
+    !record_type = !cir.struct<"S" packed {!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>
 
@@ -3852,7 +3852,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.struct<"S" packed {!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/include/clang/CIR/Dialect/IR/CIRTypes.h 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
index c3a513dbbda66..9b5b3957f6453 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.h
@@ -144,7 +144,9 @@ class RecordType : public mlir::Type {
   std::string getKindAsStr() const;
   std::string getPrefixedName() const;
 
-  void complete(llvm::ArrayRef<mlir::Type> members, bool packed, bool padded,
+  /// \p padding is union-only.  A struct carries its padding as a member
+  /// marked pad.
+  void complete(llvm::ArrayRef<mlir::Type> members, bool packed,
                 mlir::Type padding,
                 llvm::ArrayRef<RecordMemberKind> memberKinds);
   uint64_t getElementOffset(const mlir::DataLayout &dataLayout,

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 975d2a4304c42..2d8d7abe9b404 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -698,7 +698,6 @@ def CIR_StructType : CIR_Type<"Struct", "struct", [
         !rec_incomplete = !cir.struct<"incomplete" incomplete>
         !anonymous    = !cir.struct<{data !u8i}>
         !rec_packed   = !cir.struct<"p1" packed {data !u8i, data !u8i}>
-        !rec_padded   = !cir.struct<"p2" padded {data !u8i, data !u8i}>
         !rec_pad      = !cir.struct<"p3" {data !u8i, pad !cir.array<!u8i x 3>}>
         !rec_empty    = !cir.struct<"e" {empty !u8i}>
         !recursive    = !cir.struct<"Node" {data 
!cir.ptr<!cir.struct<"Node">>}>
@@ -710,7 +709,6 @@ def CIR_StructType : CIR_Type<"Struct", "struct", [
     OptionalParameter<"mlir::StringAttr">:$name,
     "bool":$incomplete,
     "bool":$packed,
-    "bool":$padded,
     ArrayRefParameter<"cir::RecordMemberKind">:$member_kinds,
     "bool":$is_class
   );
@@ -728,11 +726,10 @@ def CIR_StructType : CIR_Type<"Struct", "struct", [
       "llvm::ArrayRef<mlir::Type>":$members,
       "mlir::StringAttr":$name,
       "bool":$packed,
-      "bool":$padded,
       "bool":$is_class,
       "llvm::ArrayRef<cir::RecordMemberKind>":$member_kinds
     ), [{
-      return $_get($_ctxt, members, name, /*incomplete=*/false, packed, padded,
+      return $_get($_ctxt, members, name, /*incomplete=*/false, packed,
                    member_kinds, is_class);
     }]>,
 
@@ -742,7 +739,7 @@ def CIR_StructType : CIR_Type<"Struct", "struct", [
       "bool":$is_class
     ), [{
       return $_get($_ctxt, /*members=*/llvm::ArrayRef<mlir::Type>{}, name,
-                   /*incomplete=*/true, /*packed=*/false, /*padded=*/false,
+                   /*incomplete=*/true, /*packed=*/false,
                    /*member_kinds=*/llvm::ArrayRef<cir::RecordMemberKind>{},
                    is_class);
     }]>,
@@ -751,12 +748,11 @@ def CIR_StructType : CIR_Type<"Struct", "struct", [
     TypeBuilder<(ins
       "llvm::ArrayRef<mlir::Type>":$members,
       "bool":$packed,
-      "bool":$padded,
       "bool":$is_class,
       "llvm::ArrayRef<cir::RecordMemberKind>":$member_kinds
     ), [{
       return $_get($_ctxt, members, mlir::StringAttr{}, /*incomplete=*/false,
-                   packed, padded, member_kinds, is_class);
+                   packed, member_kinds, is_class);
     }]>
   ];
 
@@ -780,9 +776,11 @@ def CIR_StructType : CIR_Type<"Struct", "struct", [
     }
 
     void complete(llvm::ArrayRef<mlir::Type> members, bool packed,
-                  bool isPadded,
                   llvm::ArrayRef<cir::RecordMemberKind> memberKinds);
 
+    /// Whether any member is marked pad.
+    bool getPadded() const;
+
     uint64_t getElementOffset(const mlir::DataLayout &dataLayout,
                               unsigned idx) const;
 
@@ -806,7 +804,9 @@ def CIR_StructType : CIR_Type<"Struct", "struct", [
 
     bool isSized() const { return isComplete(); }
 
-    /// Returns the data size (excluding tail padding) for this struct type.
+    /// Returns the data size for this struct type.  Tail padding is the
+    /// trailing run of pad members, so interior padding and a trailing empty
+    /// member stay inside the data size.
     unsigned computeStructDataSize(const mlir::DataLayout &dataLayout) const;
 
   private:
@@ -914,9 +914,7 @@ def CIR_UnionType : CIR_Type<"Union", "union", [
     bool isComplete() const { return !isIncomplete(); }
     bool isIncomplete() const;
 
-    /// Returns true when this union carries a tail-padding type.
-    /// Derived from getPadding(): a union is padded iff it has a non-null
-    /// padding type.
+    /// Whether this union carries a tail-padding type.
     bool getPadded() const;
 
     size_t getNumElements() const { return getMembers().size(); }

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h 
b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
index 7a451698f4b23..4176fc8900c58 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
@@ -32,68 +32,64 @@ struct StructTypeStorage : public mlir::TypeStorage {
     mlir::StringAttr name;
     bool incomplete;
     bool packed;
-    bool padded;
     llvm::ArrayRef<RecordMemberKind> member_kinds;
     bool is_class;
 
     KeyTy(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
-          bool incomplete, bool packed, bool padded,
+          bool incomplete, bool packed,
           llvm::ArrayRef<RecordMemberKind> member_kinds, bool is_class)
         : members(members), name(name), incomplete(incomplete), packed(packed),
-          padded(padded), member_kinds(member_kinds), is_class(is_class) {}
+          member_kinds(member_kinds), is_class(is_class) {}
   };
 
   llvm::ArrayRef<mlir::Type> members;
   mlir::StringAttr name;
   bool incomplete;
   bool packed;
-  bool padded;
   llvm::ArrayRef<RecordMemberKind> member_kinds;
   bool is_class;
 
   StructTypeStorage(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
-                    bool incomplete, bool packed, bool padded,
+                    bool incomplete, bool packed,
                     llvm::ArrayRef<RecordMemberKind> member_kinds,
                     bool is_class)
       : members(members), name(name), incomplete(incomplete), packed(packed),
-        padded(padded), member_kinds(member_kinds), is_class(is_class) {
+        member_kinds(member_kinds), is_class(is_class) {
     assert((name || !incomplete) && "Incomplete records must have a name");
     assert(member_kinds.size() == members.size() &&
            "every member must say what it holds");
   }
 
   KeyTy getAsKey() const {
-    return KeyTy(members, name, incomplete, packed, padded, member_kinds,
-                 is_class);
+    return KeyTy(members, name, incomplete, packed, member_kinds, is_class);
   }
 
   bool operator==(const KeyTy &key) const {
     if (name)
       return (name == key.name) && (is_class == key.is_class);
-    return std::tie(members, name, incomplete, packed, padded, member_kinds,
+    return std::tie(members, name, incomplete, packed, member_kinds,
                     is_class) == std::tie(key.members, key.name, 
key.incomplete,
-                                          key.packed, key.padded,
-                                          key.member_kinds, key.is_class);
+                                          key.packed, key.member_kinds,
+                                          key.is_class);
   }
 
   static llvm::hash_code hashKey(const KeyTy &key) {
     if (key.name)
       return llvm::hash_combine(key.name, key.is_class);
     return llvm::hash_combine(key.members, key.incomplete, key.packed,
-                              key.padded, key.member_kinds, key.is_class);
+                              key.member_kinds, key.is_class);
   }
 
   static StructTypeStorage *construct(mlir::TypeStorageAllocator &allocator,
                                       const KeyTy &key) {
     return new (allocator.allocate<StructTypeStorage>()) StructTypeStorage(
         allocator.copyInto(key.members), key.name, key.incomplete, key.packed,
-        key.padded, allocator.copyInto(key.member_kinds), key.is_class);
+        allocator.copyInto(key.member_kinds), key.is_class);
   }
 
   /// Mutates the members and attributes of an identified struct/class.
   llvm::LogicalResult mutate(mlir::TypeStorageAllocator &allocator,
                              llvm::ArrayRef<mlir::Type> members, bool packed,
-                             bool padded,
                              llvm::ArrayRef<RecordMemberKind> memberKinds) {
     if (!name)
       return llvm::failure();
@@ -102,9 +98,9 @@ struct StructTypeStorage : public mlir::TypeStorage {
     // including the kinds: otherwise it silently keeps the kinds it was given
     // the first time.
     if (!incomplete)
-      return mlir::success(
-          (this->members == members) && (this->packed == packed) &&
-          (this->padded == padded) && (this->member_kinds == memberKinds));
+      return mlir::success((this->members == members) &&
+                           (this->packed == packed) &&
+                           (this->member_kinds == memberKinds));
 
     // mutate is the one entrance verify() never sees, so check the length here
     // rather than leave it to an assert.
@@ -113,7 +109,6 @@ struct StructTypeStorage : public mlir::TypeStorage {
 
     this->members = allocator.copyInto(members);
     this->packed = packed;
-    this->padded = padded;
     this->member_kinds = allocator.copyInto(memberKinds);
     incomplete = false;
     return llvm::success();

diff  --git a/clang/lib/CIR/CodeGen/CIRGenAsm.cpp 
b/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
index 9ad09ca95f73a..cfc5cb52584ae 100644
--- a/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenAsm.cpp
@@ -539,7 +539,7 @@ mlir::LogicalResult CIRGenFunction::emitAsmStmt(const 
AsmStmt &s) {
   else if (resultRegTypes.size() > 1)
     resultType = builder.getAnonRecordTy(
         resultRegTypes, /*packed=*/false,
-        /*padded=*/false, cir::RecordType::getAllDataKinds(resultRegTypes));
+        cir::RecordType::getAllDataKinds(resultRegTypes));
 
   bool hasSideEffect = s.isVolatile() || s.getNumOutputs() == 0;
 

diff  --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
index f521fd2a6936e..e342625d2b1f3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
@@ -195,33 +195,8 @@ uint64_t 
CIRGenBuilderTy::computeOffsetFromGlobalViewIndices(
   return offset;
 }
 
-cir::RecordType clang::CIRGen::CIRGenBuilderTy::getCompleteRecordType(
-    mlir::ArrayAttr fields, bool packed, bool padded, llvm::StringRef name) {
-  assert(!cir::MissingFeatures::astRecordDeclAttr());
-  llvm::SmallVector<mlir::Type> members;
-  members.reserve(fields.size());
-  llvm::transform(fields, std::back_inserter(members),
-                  [](mlir::Attribute attr) {
-                    return mlir::cast<mlir::TypedAttr>(attr).getType();
-                  });
-
-  if (name.empty())
-    return getAnonRecordTy(members, packed, padded,
-                           cir::RecordType::getAllDataKinds(members));
-
-  return getCompleteNamedRecordType(members, packed, padded, name,
-                                    cir::RecordType::getAllDataKinds(members));
-}
-
 mlir::Attribute clang::CIRGen::CIRGenBuilderTy::getConstRecordOrZeroAttr(
-    mlir::ArrayAttr arrayAttr, bool packed, bool padded, mlir::Type type) {
-  auto recordTy = mlir::cast_or_null<cir::RecordType>(type);
-
-  // Record type not specified: create anon record type from members.
-  if (!recordTy) {
-    recordTy = getCompleteRecordType(arrayAttr, packed, padded);
-  }
-
+    mlir::ArrayAttr arrayAttr, cir::RecordType recordTy) {
   // Return zero or anonymous constant record.
   const bool isZero = llvm::all_of(
       arrayAttr, [&](mlir::Attribute a) { return isNullValue(a); });

diff  --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index f8a152c0e4c30..dc4fbf5c64f97 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -79,25 +79,17 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
   }
 
   mlir::Attribute getConstRecordOrZeroAttr(mlir::ArrayAttr arrayAttr,
-                                           bool packed = false,
-                                           bool padded = false,
-                                           mlir::Type type = {});
-
-  cir::ConstRecordAttr getAnonConstRecord(mlir::ArrayAttr arrayAttr,
-                                          bool packed = false,
-                                          bool padded = false,
-                                          mlir::Type ty = {}) {
-    llvm::SmallVector<mlir::Type, 4> members;
+                                           cir::RecordType recordTy);
+
+  cir::ConstRecordAttr getAnonConstRecord(mlir::ArrayAttr arrayAttr) {
+    llvm::SmallVector<mlir::Type> members;
     for (auto &f : arrayAttr) {
       auto ta = mlir::cast<mlir::TypedAttr>(f);
       members.push_back(ta.getType());
     }
 
-    if (!ty)
-      ty = getAnonRecordTy(members, packed, padded,
-                           cir::RecordType::getAllDataKinds(members));
-
-    auto sTy = mlir::cast<cir::RecordType>(ty);
+    auto sTy = getAnonRecordTy(members, /*packed=*/false,
+                               cir::RecordType::getAllDataKinds(members));
     return cir::ConstRecordAttr::get(sTy, arrayAttr);
   }
 
@@ -156,34 +148,27 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
   /// 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 getCompleteNamedRecordType(
-      llvm::ArrayRef<mlir::Type> members, bool packed, bool padded,
-      llvm::StringRef name, llvm::ArrayRef<cir::RecordMemberKind> memberKinds) 
{
+      llvm::ArrayRef<mlir::Type> members, bool packed, 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).
+    // Always a struct at this call site, never a class or a union.
     auto type = cir::StructType::get(getContext(), members, nameAttr, packed,
-                                     padded, /*is_class=*/false, memberKinds);
+                                     /*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));
+           (type.getMembers() == members && type.getPacked() == packed));
 
     // Complete an incomplete record or ensure the existing complete record
     // matches the requested attributes.
-    type.complete(members, packed, padded, memberKinds);
+    type.complete(members, packed, memberKinds);
 
     return type;
   }
 
-  cir::RecordType getCompleteRecordType(mlir::ArrayAttr fields,
-                                        bool packed = false,
-                                        bool padded = false,
-                                        llvm::StringRef name = "");
-
   /// Get an incomplete CIR record 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.
@@ -405,10 +390,10 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
 
   /// Get a CIR anonymous struct type.
   cir::StructType
-  getAnonRecordTy(llvm::ArrayRef<mlir::Type> members, bool packed, bool padded,
+  getAnonRecordTy(llvm::ArrayRef<mlir::Type> members, bool packed,
                   llvm::ArrayRef<cir::RecordMemberKind> memberKinds) {
     assert(!cir::MissingFeatures::astRecordDeclAttr());
-    return cir::StructType::get(getContext(), members, packed, padded,
+    return cir::StructType::get(getContext(), members, packed,
                                 /*is_class=*/false, memberKinds);
   }
 

diff  --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinAMDGPU.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinAMDGPU.cpp
index 0c025e40e07ea..ef40039eb6800 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinAMDGPU.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinAMDGPU.cpp
@@ -78,7 +78,7 @@ static mlir::Value emitLogbBuiltin(CIRGenFunction &cgf, const 
CallExpr *e,
 
   mlir::Type frExpResMembers[] = {srcTy, int32Ty};
   cir::RecordType frExpResTy = builder.getAnonRecordTy(
-      frExpResMembers, /*packed=*/false, /*padded=*/false,
+      frExpResMembers, /*packed=*/false,
       cir::RecordType::getAllDataKinds(frExpResMembers));
 
   mlir::Value frExpResult = builder.emitIntrinsicCallOp(
@@ -178,7 +178,7 @@ CIRGenFunction::emitAMDGPUBuiltinExpr(unsigned builtinId,
     auto i1Ty = builder.getUIntNTy(1);
     mlir::Type resMembers[] = {x.getType(), i1Ty};
     cir::RecordType resTy =
-        builder.getAnonRecordTy(resMembers, /*packed=*/false, /*padded=*/false,
+        builder.getAnonRecordTy(resMembers, /*packed=*/false,
                                 cir::RecordType::getAllDataKinds(resMembers));
 
     mlir::Value structResult =

diff  --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
index f0ee735e8bc94..2220639876695 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinNVPTX.cpp
@@ -1042,9 +1042,8 @@ static mlir::Value 
packArgsIntoNVPTXFormatBuffer(CIRGenFunction &cgf,
   // We can directly store the arguments into a struct, and the alignment
   // would automatically be correct. That's because vprintf does not
   // accept aggregates.
-  mlir::Type allocaTy =
-      builder.getAnonRecordTy(argTypes, /*packed=*/false, /*padded=*/false,
-                              cir::RecordType::getAllDataKinds(argTypes));
+  mlir::Type allocaTy = builder.getAnonRecordTy(
+      argTypes, /*packed=*/false, cir::RecordType::getAllDataKinds(argTypes));
   auto allocaAlign = clang::CharUnits::fromQuantity(
       dataLayout.getABITypeAlign(allocaTy).value());
   Address allocaAddr =

diff  --git a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
index 18f464b750140..09c60b4d21f7a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltinX86.cpp
@@ -208,10 +208,9 @@ emitEncodeKey(mlir::MLIRContext *context, CIRGenBuilderTy 
&builder,
   llvm::SmallVector<mlir::Type> members{builder.getUInt32Ty()};
   llvm::append_range(members,
                      llvm::SmallVector<mlir::Type>(vecOutputCount, resVector));
-  cir::StructType resRecord =
-      cir::StructType::get(context, members, /*packed=*/false,
-                           /*padded=*/false, /*is_class=*/false,
-                           cir::RecordType::getAllDataKinds(members));
+  cir::StructType resRecord = cir::StructType::get(
+      context, members, /*packed=*/false,
+      /*is_class=*/false, cir::RecordType::getAllDataKinds(members));
 
   mlir::Value outputPtr =
       builder.createBitcast(outputOperand, cir::PointerType::get(resVector));
@@ -696,7 +695,7 @@ static mlir::Value emitX86Aes(CIRGenBuilderTy &builder, 
mlir::Location loc,
       mlir::cast<cir::PointerType>(ops[0].getType()).getPointee();
   mlir::Type rstMembers[] = {retType, vecType};
   cir::RecordType rstRecTy =
-      builder.getAnonRecordTy(rstMembers, /*packed=*/false, /*padded=*/false,
+      builder.getAnonRecordTy(rstMembers, /*packed=*/false,
                               cir::RecordType::getAllDataKinds(rstMembers));
   mlir::Value rstValueRec = builder.emitIntrinsicCallOp(
       loc, intrinsicName, rstRecTy, mlir::ValueRange{ops[1], ops[2]});
@@ -754,9 +753,8 @@ static mlir::Value emitX86Aeswide(CIRGenBuilderTy &builder, 
mlir::Location loc,
         builder.createAlignedLoad(loc, vecType, nextInElePtr,
                                   /*align=*/CharUnits::fromQuantity(16));
   }
-  cir::RecordType rstRecTy =
-      builder.getAnonRecordTy(recTypes, /*packed=*/false, /*padded=*/false,
-                              cir::RecordType::getAllDataKinds(recTypes));
+  cir::RecordType rstRecTy = builder.getAnonRecordTy(
+      recTypes, /*packed=*/false, cir::RecordType::getAllDataKinds(recTypes));
   mlir::Value rstValueRec =
       builder.emitIntrinsicCallOp(loc, intrinsicName, rstRecTy, arguments);
 
@@ -934,9 +932,8 @@ cir::GetGlobalOp 
CIRGenFunction::createGetCpuModel(mlir::Location loc) {
     // unsigned int __cpu_subtype;
     // unsigned int __cpu_features[1];
     mlir::Type tys[] = {u32, u32, u32, cir::ArrayType::get(u32, 1)};
-    mlir::Type modelTy =
-        builder.getAnonRecordTy(tys, /*packed=*/false, /*padded=*/false,
-                                cir::RecordType::getAllDataKinds(tys));
+    mlir::Type modelTy = builder.getAnonRecordTy(
+        tys, /*packed=*/false, cir::RecordType::getAllDataKinds(tys));
     cpuModel =
         cgm.createGlobalOp(loc, "__cpu_model", modelTy, /*isConstant=*/false);
     cpuModel.setDsoLocal(true);
@@ -1142,9 +1139,8 @@ CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID, 
const CallExpr *expr) {
     mlir::Type i64Ty = builder.getUInt64Ty();
     mlir::Type i32Ty = builder.getUInt32Ty();
     mlir::Type members[] = {i64Ty, i32Ty};
-    mlir::Type structTy =
-        builder.getAnonRecordTy(members, /*packed=*/false, /*padded=*/false,
-                                cir::RecordType::getAllDataKinds(members));
+    mlir::Type structTy = builder.getAnonRecordTy(
+        members, /*packed=*/false, cir::RecordType::getAllDataKinds(members));
     mlir::Value result =
         builder.emitIntrinsicCallOp(loc, "x86.rdtscp", structTy);
 
@@ -2504,10 +2500,9 @@ CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID, 
const CallExpr *expr) {
     mlir::Type randTy = cast<cir::PointerType>(ops[0].getType()).getPointee();
     llvm::SmallVector<mlir::Type, 2> resultTypes = {randTy,
                                                     builder.getUInt32Ty()};
-    cir::StructType resRecord =
-        cir::StructType::get(&getMLIRContext(), resultTypes, /*packed=*/false,
-                             /*padded=*/false, /*is_class=*/false,
-                             cir::RecordType::getAllDataKinds(resultTypes));
+    cir::StructType resRecord = cir::StructType::get(
+        &getMLIRContext(), resultTypes, /*packed=*/false,
+        /*is_class=*/false, cir::RecordType::getAllDataKinds(resultTypes));
 
     mlir::Value call =
         builder.emitIntrinsicCallOp(loc, intrinsicName, resRecord);
@@ -2576,8 +2571,8 @@ CIRGenFunction::emitX86BuiltinExpr(unsigned builtinID, 
const CallExpr *expr) {
 
     mlir::Type resMembers[] = {resVector, resVector};
     cir::StructType resRecord = cir::StructType::get(
-        &getMLIRContext(), resMembers, /*packed=*/false, /*padded=*/false,
-        /*is_class=*/false, cir::RecordType::getAllDataKinds(resMembers));
+        &getMLIRContext(), resMembers, /*packed=*/false, /*is_class=*/false,
+        cir::RecordType::getAllDataKinds(resMembers));
 
     mlir::Value call = builder.emitIntrinsicCallOp(
         getLoc(expr->getExprLoc()), intrinsicName, resRecord,

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index 93d4fee235bad..9e3097e2bba27 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -400,8 +400,6 @@ mlir::Attribute buildRecordHelper(ConstantEmitter &emitter,
   }
 
   return builder.getConstRecordOrZeroAttr(builder.getArrayAttr(elements),
-                                          /*packed=*/recordTy.getPacked(),
-                                          /*padded=*/recordTy.getPadded(),
                                           recordTy);
 }
 

diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp 
b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
index 98f6ab9755501..e3e2b89cad310 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp
@@ -1121,8 +1121,7 @@ static mlir::Attribute getNewInitValue(CIRGenModule &cgm, 
cir::GlobalOp newGlob,
   if (auto oldRecord = mlir::dyn_cast<cir::ConstRecordAttr>(oldInit)) {
     mlir::ArrayAttr newMembers = getNewInitElements(oldRecord.getMembers());
     auto recordTy = mlir::cast<cir::RecordType>(oldRecord.getType());
-    return cgm.getBuilder().getConstRecordOrZeroAttr(
-        newMembers, recordTy.getPacked(), recordTy.getPadded(), recordTy);
+    return cgm.getBuilder().getConstRecordOrZeroAttr(newMembers, recordTy);
   }
 
   // This may be unreachable in practice, but keep it as errorNYI while CIR

diff  --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h 
b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
index 9733d18f52ce8..992dfa95e40cf 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
@@ -45,8 +45,8 @@ namespace clang::CIRGen {
 /// represented by one !u16i value, and the array provides padding to align the
 /// struct to a 4-byte alignment.
 ///
-///   !rec_S = !cir.record<struct "S" padded {!s8i, !s8i, !s8i, !u16i,
-///   !cir.array<!u8i x 3>}>
+///   !rec_S = !cir.struct<"S" packed {!s8i, !s8i, !s8i, !u16i,
+///                                   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 8e3d7b5f254c4..1ce70091d9f4b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -222,7 +222,6 @@ struct CIRRecordLowering final {
     if (size.isZero())
       return;
     mlir::Type padTy = getByteArrayType(size);
-    padded = true;
     if (recordDecl->isUnion()) {
       assert(!unionPadding && "at most one union tail-padding type");
       unionPadding = padTy;
@@ -267,8 +266,6 @@ struct CIRRecordLowering final {
   unsigned zeroInitializableAsBase : 1;
   LLVM_PREFERRED_TYPE(bool)
   unsigned packed : 1;
-  LLVM_PREFERRED_TYPE(bool)
-  unsigned padded : 1;
 
 private:
   // Output fields, consumed by CIRGenTypes::computeRecordLayout.  Private so
@@ -290,8 +287,7 @@ CIRRecordLowering::CIRRecordLowering(CIRGenTypes 
&cirGenTypes,
       astRecordLayout{
           cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)},
       dataLayout{cirGenTypes.getCGModule().getModule()},
-      zeroInitializable{true}, zeroInitializableAsBase{true}, packed{packed},
-      padded{false} {}
+      zeroInitializable{true}, zeroInitializableAsBase{true}, packed{packed} {}
 
 void CIRRecordLowering::setBitFieldInfo(const FieldDecl *fd,
                                         CharUnits startOffset,
@@ -753,7 +749,6 @@ void CIRRecordLowering::insertPadding() {
   }
   if (padding.empty())
     return;
-  padded = true;
   // Add the padding to the Members list and sort it.
   for (const std::pair<CharUnits, CharUnits> &paddingPair : padding)
     members.push_back(makeStorageInfo(paddingPair.first,
@@ -814,8 +809,8 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, 
cir::RecordType *ty) {
       baseLowering.lower(/*nonVirtualBaseType=*/true);
       std::string baseIdentifier = getRecordTypeName(rd, ".base");
       baseTy = builder.getCompleteNamedRecordType(
-          baseLowering.getFieldTypes(), baseLowering.packed,
-          baseLowering.padded, baseIdentifier, baseLowering.getFieldKinds());
+          baseLowering.getFieldTypes(), baseLowering.packed, baseIdentifier,
+          baseLowering.getFieldKinds());
       // TODO(cir): add something like addRecordTypeName
 
       // BaseTy and Ty must agree on their packedness for getCIRFieldNo to work
@@ -839,8 +834,8 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, 
cir::RecordType *ty) {
   // signifies that the type is no longer opaque and record layout is complete,
   // but we may need to recursively layout rd while laying D out as a base 
type.
   assert(!cir::MissingFeatures::astRecordDeclAttr());
-  ty->complete(lowering.getFieldTypes(), lowering.packed, lowering.padded,
-               lowering.unionPadding, lowering.getFieldKinds());
+  ty->complete(lowering.getFieldTypes(), lowering.packed, 
lowering.unionPadding,
+               lowering.getFieldKinds());
 
   // The marks exist so that emptiness can be read off the type, so check that
   // answer against the AST predicate on every record CIRGen lays out.  This
@@ -1008,10 +1003,8 @@ void CIRRecordLowering::lowerUnion(bool 
nonVirtualBaseType) {
     clearFields();
     addField(storageType, storageKind);
     CharUnits padding = layoutSize - getSize(storageType);
-    if (!padding.isZero()) {
+    if (!padding.isZero())
       addField(getByteArrayType(padding), cir::RecordMemberKind::Pad);
-      padded = true;
-    }
   } else {
     // Else we just add padding normally.
     appendPaddingBytes(layoutSize - getSize(storageType));

diff  --git a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp 
b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
index 346542134470a..f1834d8fc7f37 100644
--- a/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenTypes.cpp
@@ -662,9 +662,9 @@ mlir::Type CIRGenTypes::convertType(QualType type) {
       mlir::Type elements[] = {resultType, paddingArray};
       cir::RecordMemberKind kinds[] = {cir::RecordMemberKind::Data,
                                        cir::RecordMemberKind::Pad};
-      resultType = cir::StructType::get(&getMLIRContext(), 
/*members=*/elements,
-                                        /*packed=*/false, /*padded=*/true,
-                                        /*is_class=*/false, kinds);
+      resultType =
+          cir::StructType::get(&getMLIRContext(), /*members=*/elements,
+                               /*packed=*/false, /*is_class=*/false, kinds);
     }
 
     break;

diff  --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp 
b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
index 1926c2657454b..97f4b5b2c2c48 100644
--- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp
@@ -70,8 +70,7 @@ cir::RecordType CIRGenVTables::getVTableType(const 
VTableLayout &layout) {
   // FIXME(cir): should VTableLayout be encoded like we do for some
   // AST nodes?
   return cgm.getBuilder().getAnonRecordTy(
-      tys, /*packed=*/false,
-      /*padded=*/false, cir::RecordType::getAllDataKinds(tys));
+      tys, /*packed=*/false, cir::RecordType::getAllDataKinds(tys));
 }
 
 /// At this point in the translation unit, does it appear that can we

diff  --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 997bc29b48ab4..4a26f970b5bc5 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -209,16 +209,15 @@ parseRecordBody(mlir::AsmParser &parser, bool &incomplete,
 }
 
 /// Print a complete CIR record body:
-///   '<' ['class '] [name] ['packed '] ['padded '] body '>'
-/// where body is "incomplete" or "{[mark] members[, padding = {type}]}".
+///   '<' ['class '] [name] ['packed '] body '>'
+/// where body is "incomplete" or "{[mark] members}[, padding = {type}]".
 /// RecordTy must be a mutable MLIR type (StructType or UnionType).
 template <typename RecordTy>
-static void printRecordBody(mlir::AsmPrinter &printer, RecordTy self,
-                            mlir::StringAttr name, bool hasClassPrefix,
-                            bool isPacked, bool isPadded, bool isIncomplete,
-                            llvm::ArrayRef<mlir::Type> members,
-                            mlir::Type padding,
-                            llvm::ArrayRef<RecordMemberKind> memberKinds) {
+static void
+printRecordBody(mlir::AsmPrinter &printer, RecordTy self, mlir::StringAttr 
name,
+                bool hasClassPrefix, bool isPacked, bool isIncomplete,
+                llvm::ArrayRef<mlir::Type> members, mlir::Type padding,
+                llvm::ArrayRef<RecordMemberKind> memberKinds) {
   printer << '<';
   if (hasClassPrefix)
     printer << "class ";
@@ -236,8 +235,6 @@ static void printRecordBody(mlir::AsmPrinter &printer, 
RecordTy self,
     printer << ' ';
   if (isPacked)
     printer << "packed ";
-  if (isPadded)
-    printer << "padded ";
   if (isIncomplete) {
     printer << "incomplete";
   } else {
@@ -264,7 +261,6 @@ Type StructType::parse(mlir::AsmParser &parser) {
   const llvm::SMLoc loc = parser.getCurrentLocation();
   const mlir::Location eLoc = parser.getEncodedSourceLoc(loc);
   bool packed = false;
-  bool padded = false;
   mlir::MLIRContext *context = parser.getContext();
 
   if (parser.parseLess())
@@ -299,9 +295,6 @@ Type StructType::parse(mlir::AsmParser &parser) {
   if (parser.parseOptionalKeyword("packed").succeeded())
     packed = true;
 
-  if (parser.parseOptionalKeyword("padded").succeeded())
-    padded = true;
-
   bool incomplete = true;
   llvm::SmallVector<mlir::Type> members;
   llvm::SmallVector<RecordMemberKind> memberKinds;
@@ -317,18 +310,18 @@ Type StructType::parse(mlir::AsmParser &parser) {
   if (name && incomplete) {
     type = StructType::getChecked(eLoc, context, name, is_class);
   } else if (!name && !incomplete) {
-    type = StructType::getChecked(eLoc, context, membersRef, packed, padded,
-                                  is_class, kindsRef);
+    type = StructType::getChecked(eLoc, context, membersRef, packed, is_class,
+                                  kindsRef);
     if (!type)
       return {};
   } else if (!incomplete) {
     type = StructType::getChecked(eLoc, context, membersRef, name, packed,
-                                  padded, is_class, kindsRef);
+                                  is_class, kindsRef);
     if (!type)
       return {};
     if (auto structTy = mlir::dyn_cast<StructType>(type))
       if (structTy.isIncomplete())
-        structTy.complete(membersRef, packed, padded, kindsRef);
+        structTy.complete(membersRef, packed, kindsRef);
     assert(!cir::MissingFeatures::astRecordDeclAttr());
   } else {
     parser.emitError(loc, "anonymous records must be complete");
@@ -340,16 +333,14 @@ Type StructType::parse(mlir::AsmParser &parser) {
 
 void StructType::print(mlir::AsmPrinter &printer) const {
   printRecordBody(printer, *this, getName(), isClass(), getPacked(),
-                  getPadded(), isIncomplete(), getMembers(), /*padding=*/{},
+                  isIncomplete(), getMembers(), /*padding=*/{},
                   getMemberKinds());
 }
 
-mlir::LogicalResult
-StructType::verify(function_ref<mlir::InFlightDiagnostic()> emitError,
-                   llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
-                   bool incomplete, bool packed, bool padded,
-                   llvm::ArrayRef<RecordMemberKind> member_kinds,
-                   bool is_class) {
+mlir::LogicalResult StructType::verify(
+    function_ref<mlir::InFlightDiagnostic()> emitError,
+    llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name, bool incomplete,
+    bool packed, llvm::ArrayRef<RecordMemberKind> member_kinds, bool is_class) 
{
   if (name && name.getValue().empty())
     return emitError() << "identified records cannot have an empty name";
   return verifyRecordMemberKinds(emitError, members.size(), member_kinds);
@@ -364,12 +355,15 @@ mlir::StringAttr StructType::getName() const { return 
getImpl()->name; }
 bool StructType::isIncomplete() const { return getImpl()->incomplete; }
 bool StructType::getIncomplete() const { return getImpl()->incomplete; }
 bool StructType::getPacked() const { return getImpl()->packed; }
-bool StructType::getPadded() const { return getImpl()->padded; }
 llvm::ArrayRef<RecordMemberKind> StructType::getMemberKinds() const {
   return getImpl()->member_kinds;
 }
 bool StructType::getIsClass() const { return getImpl()->is_class; }
 
+bool StructType::getPadded() const {
+  return llvm::is_contained(getMemberKinds(), RecordMemberKind::Pad);
+}
+
 bool StructType::isABIConvertedRecord() const {
   return getName() && getName().getValue().starts_with(abi_conversion_prefix);
 }
@@ -388,10 +382,10 @@ void StructType::removeABIConversionNamePrefix() {
         recordName.getType());
 }
 
-void StructType::complete(ArrayRef<Type> members, bool packed, bool padded,
+void StructType::complete(ArrayRef<Type> members, bool packed,
                           ArrayRef<RecordMemberKind> memberKinds) {
   assert(!cir::MissingFeatures::astRecordDeclAttr());
-  if (mutate(members, packed, padded, memberKinds).failed())
+  if (mutate(members, packed, memberKinds).failed())
     llvm_unreachable("failed to complete struct");
 }
 
@@ -504,8 +498,8 @@ Type UnionType::parse(mlir::AsmParser &parser) {
 
 void UnionType::print(mlir::AsmPrinter &printer) const {
   printRecordBody(printer, *this, getName(), /*hasClassPrefix=*/false,
-                  getPacked(), /*isPadded=*/false, isIncomplete(), 
getMembers(),
-                  getPadding(), getMemberKinds());
+                  getPacked(), isIncomplete(), getMembers(), getPadding(),
+                  getMemberKinds());
 }
 
 mlir::LogicalResult
@@ -641,14 +635,13 @@ std::string RecordType::getKindAsStr() const {
 std::string RecordType::getPrefixedName() const {
   return getKindAsStr() + "." + getName().getValue().str();
 }
-void RecordType::complete(ArrayRef<Type> members, bool packed, bool padded,
+void RecordType::complete(ArrayRef<Type> members, bool packed,
                           mlir::Type padding,
                           ArrayRef<RecordMemberKind> memberKinds) {
-  if (auto s = mlir::dyn_cast<StructType>(*this))
-    return s.complete(members, packed, padded, memberKinds);
-  // Unions derive padded from padding; assert the caller is consistent.
-  assert((!padded || padding) &&
-         "padded=true requires a non-null padding type");
+  if (auto s = mlir::dyn_cast<StructType>(*this)) {
+    assert(!padding && "only a union takes a separate padding slot");
+    return s.complete(members, packed, memberKinds);
+  }
   return mlir::cast<UnionType>(*this).complete(members, packed, padding,
                                                memberKinds);
 }
@@ -875,17 +868,20 @@ unsigned
 StructType::computeStructDataSize(const mlir::DataLayout &dataLayout) const {
   assert(isComplete() && "Cannot get layout of incomplete records");
 
-  // Compute the data size (excluding tail padding) for this record type. For
-  // padded records, the last member is the tail padding array added by
-  // CIRGenRecordLayoutBuilder::appendPaddingBytes, so we exclude it. For
-  // non-padded records, data size equals the full struct size without
-  // alignment.
-  auto members = getMembers();
-  unsigned numMembers =
-      getPadded() && members.size() > 1 ? members.size() - 1 : members.size();
+  // Tail padding is the trailing run of pad members.  An empty member stays
+  // inside the data size: it is storage the source declared, which a derived
+  // class may not reuse.
+  llvm::ArrayRef<mlir::Type> members = getMembers();
+  llvm::ArrayRef<RecordMemberKind> kinds = getMemberKinds();
+  assert(kinds.size() == members.size() &&
+         "the two drop_back calls below must stay in step");
+  while (!kinds.empty() && kinds.back() == RecordMemberKind::Pad) {
+    kinds = kinds.drop_back();
+    members = members.drop_back();
+  }
+
   unsigned recordSize = 0;
-  for (unsigned i = 0; i < numMembers; ++i) {
-    mlir::Type ty = members[i];
+  for (mlir::Type ty : members) {
     const uint64_t tyAlign =
         (getPacked() ? 1 : dataLayout.getTypeABIAlignment(ty));
     recordSize = llvm::alignTo(recordSize, tyAlign);
@@ -1262,7 +1258,7 @@ static mlir::Type getMethodLayoutType(mlir::MLIRContext 
*ctx) {
   auto voidPtrTy = cir::PointerType::get(cir::VoidType::get(ctx));
   mlir::Type fields[2]{voidPtrTy, voidPtrTy};
   return cir::StructType::get(ctx, fields, /*packed=*/false,
-                              /*padded=*/false, /*is_class=*/false,
+                              /*is_class=*/false,
                               cir::RecordType::getAllDataKinds(fields));
 }
 

diff  --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp 
b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
index e8e7f4d87346c..32ff223a671a4 100644
--- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
@@ -866,8 +866,8 @@ class CIRABITypeConverter : public mlir::TypeConverter {
       }
       auto s = mlir::cast<cir::StructType>(type);
       return cir::StructType::get(type.getContext(), converted,
-                                  type.getPacked(), type.getPadded(),
-                                  s.getIsClass(), s.getMemberKinds());
+                                  type.getPacked(), s.getIsClass(),
+                                  s.getMemberKinds());
     }
 
     assert(!type.isIncomplete() || type.getMembers().empty());
@@ -914,8 +914,8 @@ class CIRABITypeConverter : public mlir::TypeConverter {
     if (auto u = mlir::dyn_cast<cir::UnionType>(type))
       if (mlir::Type pad = u.getPadding())
         loweredPadding = convertType(pad);
-    convertedType.complete(convertedMembers, type.getPacked(), 
type.getPadded(),
-                           loweredPadding, type.getMemberKinds());
+    convertedType.complete(convertedMembers, type.getPacked(), loweredPadding,
+                           type.getMemberKinds());
     addConvertedRecordType(convertedType);
     return convertedType;
   }

diff  --git a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp 
b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
index 52932f6a20dd0..6d3249ef9e5b6 100644
--- a/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CallConvLoweringPass.cpp
@@ -246,8 +246,7 @@ static mlir::Type abiTypeToCIR(const llvm::abi::Type *ty, 
MLIRContext *ctx) {
         // Coercion types are plain register tuples, not the source record.
         return cir::StructType::get(
             ctx, fieldTypes, /*packed=*/false,
-            /*padded=*/false, /*is_class=*/false,
-            cir::RecordType::getAllDataKinds(fieldTypes));
+            /*is_class=*/false, cir::RecordType::getAllDataKinds(fieldTypes));
       })
       .Default([](const llvm::abi::Type *) -> mlir::Type { return nullptr; });
 }

diff  --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 28ba14962cc7b..42ab14aba338e 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -2444,8 +2444,7 @@ void LoweringPreparePass::buildCUDAModuleCtor() {
   //    struct { int magic; int version; void *fatbin; void *unused; };
   mlir::Type fatbinWrapperMembers[] = {intTy, intTy, voidPtrTy, voidPtrTy};
   auto fatbinWrapperType = cir::StructType::get(
-      &getContext(), fatbinWrapperMembers, /*packed=*/false, /*padded=*/false,
-      /*is_class=*/false,
+      &getContext(), fatbinWrapperMembers, /*packed=*/false, 
/*is_class=*/false,
       cir::RecordType::getAllDataKinds(fatbinWrapperMembers));
   std::string fatbinWrapperName =
       addUnderscoredPrefix(cudaPrefix, "_fatbin_wrapper");

diff  --git 
a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp 
b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
index 458f87f2b8c07..fdfbea028926a 100644
--- a/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/TargetLowering/LowerItaniumCXXABI.cpp
@@ -192,7 +192,7 @@ mlir::Type LowerItaniumCXXABI::lowerMethodType(
   // function pointers. Let's follow this approach.
   mlir::Type members[] = {ptr
diff CIRTy, ptr
diff CIRTy};
   return cir::StructType::get(type.getContext(), members, /*packed=*/false,
-                              /*padded=*/false, /*is_class=*/false,
+                              /*is_class=*/false,
                               cir::RecordType::getAllDataKinds(members));
 }
 

diff  --git a/clang/test/CIR/CodeGen/atomic.c b/clang/test/CIR/CodeGen/atomic.c
index 34669bf492e58..96a84eeac4929 100644
--- a/clang/test/CIR/CodeGen/atomic.c
+++ b/clang/test/CIR/CodeGen/atomic.c
@@ -9,7 +9,7 @@ struct S1 {
   short x, y, z;
 };
 
-// CIR: !rec_anon_struct = !cir.struct<padded {data !rec_S1, pad 
!cir.array<!s8i x 2>}>
+// CIR: !rec_anon_struct = !cir.struct<{data !rec_S1, pad !cir.array<!s8i x 
2>}>
 
 _Atomic int g1;
 _Atomic int g2 = 42;

diff  --git a/clang/test/CIR/CodeGen/bitfields.c 
b/clang/test/CIR/CodeGen/bitfields.c
index 330c31989ff70..2ed9c6147212b 100644
--- a/clang/test/CIR/CodeGen/bitfields.c
+++ b/clang/test/CIR/CodeGen/bitfields.c
@@ -12,7 +12,7 @@ typedef struct {
   unsigned still_more_bits : 7;
 } A;
 
-// CIR-DAG:  !rec_A = !cir.struct<"A" packed padded {data !s8i, data !s8i, 
data !s8i, data !u16i, pad !cir.array<!u8i x 3>}>
+// CIR-DAG:  !rec_A = !cir.struct<"A" packed {data !s8i, data !s8i, data !s8i, 
data !u16i, pad !cir.array<!u8i x 3>}>
 // CIR-DAG:  #bfi_more_bits = #cir.bitfield_info<name = "more_bits", 
storage_type = !u16i, size = 4, offset = 3, is_signed = false>
 // LLVM-DAG: %struct.A = type <{ i8, i8, i8, i16, [3 x i8] }>
 // OGCG-DAG: %struct.A = type <{ i8, i8, i8, i16, [3 x i8] }>

diff  --git a/clang/test/CIR/CodeGen/cleanup.cpp 
b/clang/test/CIR/CodeGen/cleanup.cpp
index 4644c50a15a35..a2e4104fcc61e 100644
--- a/clang/test/CIR/CodeGen/cleanup.cpp
+++ b/clang/test/CIR/CodeGen/cleanup.cpp
@@ -7,7 +7,7 @@ struct Struk {
   ~Struk();
 };
 
-// CHECK: !rec_Struk = !cir.struct<"Struk" padded {pad !u8i}>
+// CHECK: !rec_Struk = !cir.struct<"Struk" {pad !u8i}>
 
 // CHECK: cir.func{{.*}} @_ZN5StrukD1Ev(!cir.ptr<!rec_Struk> {{.*}})
 

diff  --git a/clang/test/CIR/CodeGen/dumb-record.cpp 
b/clang/test/CIR/CodeGen/dumb-record.cpp
index 75d442f28aee2..10870124bd019 100644
--- a/clang/test/CIR/CodeGen/dumb-record.cpp
+++ b/clang/test/CIR/CodeGen/dumb-record.cpp
@@ -15,8 +15,8 @@ struct Empty {
 } empty;
 
 // CHECK: Layout: <CIRecordLayout
-// CHECK:  CIR Type:!cir.struct<"Empty" padded {pad !cir.int<u, 8>}>
-// CHECK:  NonVirtualBaseCIRType:!cir.struct<"Empty" padded {pad !cir.int<u, 
8>}>
+// CHECK:  CIR Type:!cir.struct<"Empty" {pad !cir.int<u, 8>}>
+// CHECK:  NonVirtualBaseCIRType:!cir.struct<"Empty" {pad !cir.int<u, 8>}>
 // CHECK:  IsZeroInitializable:1
 // CHECK:  BitFields:[
 // CHECK:  ]>

diff  --git a/clang/test/CIR/CodeGen/empty-union.cpp 
b/clang/test/CIR/CodeGen/empty-union.cpp
index c543b25253625..a608eefbc77aa 100644
--- a/clang/test/CIR/CodeGen/empty-union.cpp
+++ b/clang/test/CIR/CodeGen/empty-union.cpp
@@ -86,7 +86,7 @@ Leading leadArr[2];
 // CIR-DAG: !rec_Leading = !cir.struct<"Leading" {data !rec_Empty, data !s32i}>
 // CIR-DAG: !rec_Trailing = !cir.struct<"Trailing" {data !s32i, data 
!rec_Empty}>
 // CIR-DAG: !rec_Middle = !cir.struct<"Middle" {data !s32i, data !rec_Empty, 
data !s32i}>
-// CIR-DAG: !rec_LeadingOver = !cir.struct<"LeadingOver" padded {data 
!rec_EmptyAligned, data !s32i, pad !cir.array<!u8i x 12>}>
+// CIR-DAG: !rec_LeadingOver = !cir.struct<"LeadingOver" {data 
!rec_EmptyAligned, data !s32i, pad !cir.array<!u8i x 12>}>
 // CIR-DAG: !rec_LeadingZeroBitfield = !cir.struct<"LeadingZeroBitfield" {data 
!rec_OnlyZeroBitfield, data !s32i}>
 
 // CIR keeps the union's own named type as the record's field and leaves the

diff  --git a/clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp 
b/clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp
index 01512d910b12b..db97c97342ec8 100644
--- a/clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp
+++ b/clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp
@@ -23,7 +23,7 @@ struct S2 {
   unsigned long f3:6;
 };
 
-// CIR-DAG: !rec_S2 = !cir.struct<"S2" padded {data !u16i, data !u16i, data 
!u8i, pad !cir.array<!u8i x 3>}>
+// CIR-DAG: !rec_S2 = !cir.struct<"S2" {data !u16i, data !u16i, data !u8i, pad 
!cir.array<!u8i x 3>}>
 // LLVM-DAG: %struct.S2 = type { i16, i16, i8, [3 x i8] }
 // OGCG-DAG: %struct.S2 = type { i16, i16, i8, [3 x i8] }
 

diff  --git a/clang/test/CIR/CodeGen/member-functions.cpp 
b/clang/test/CIR/CodeGen/member-functions.cpp
index e365f5312e479..4fc39390b6eb9 100644
--- a/clang/test/CIR/CodeGen/member-functions.cpp
+++ b/clang/test/CIR/CodeGen/member-functions.cpp
@@ -6,7 +6,7 @@ struct C {
   void f2(int a, int b);
 };
 
-// CIR: !rec_C = !cir.struct<"C" padded {pad !u8i}>
+// CIR: !rec_C = !cir.struct<"C" {pad !u8i}>
 
 void C::f() {}
 

diff  --git a/clang/test/CIR/CodeGen/no-unique-address.cpp 
b/clang/test/CIR/CodeGen/no-unique-address.cpp
index da507405c3321..54514d5f18b20 100644
--- a/clang/test/CIR/CodeGen/no-unique-address.cpp
+++ b/clang/test/CIR/CodeGen/no-unique-address.cpp
@@ -30,7 +30,7 @@ struct Outer {
 // Middle's tail padding.
 
 // CIR: !rec_Middle2Ebase = !cir.struct<"Middle.base" packed {data !rec_Base, 
data !s8i}>
-// CIR: !rec_Outer = !cir.struct<"Outer" padded {data !rec_Middle2Ebase, data 
!s8i, pad !cir.array<!u8i x 2>}>
+// CIR: !rec_Outer = !cir.struct<"Outer" {data !rec_Middle2Ebase, data !s8i, 
pad !cir.array<!u8i x 2>}>
 
 // CIR-LABEL: cir.func {{.*}} @_ZN5OuterC2ERK6Middlec(
 // CIR:         %[[THIS:.*]] = cir.load %{{.+}} : 
!cir.ptr<!cir.ptr<!rec_Outer>>, !cir.ptr<!rec_Outer>
@@ -192,8 +192,8 @@ OuterUnionPadAfterStorage oupas;
 // CIR-NUA-DAG: !rec_OuterUnionPad = !cir.struct<"OuterUnionPad" {data 
!rec_UnionWithPadding2Ebase, data !cir.bool}>
 // CIR-NUA-DAG: !rec_FinalUnionWithPadding2Ebase = 
!cir.struct<"FinalUnionWithPadding.base" {data !u8i}>
 // CIR-NUA-DAG: !rec_OuterFinalUnionPad = !cir.struct<"OuterFinalUnionPad" 
{data !rec_FinalUnionWithPadding2Ebase, data !cir.bool}>
-// CIR-NUA-DAG: !rec_TailBig = !cir.struct<"TailBig" packed padded {data 
!s16i, data !cir.array<!s8i x 5>, pad !u8i}>
-// CIR-NUA-DAG: !rec_UnionPadAfterStorage2Ebase = 
!cir.struct<"UnionPadAfterStorage.base" packed padded {data !s32i, pad 
!cir.array<!u8i x 3>}>
+// CIR-NUA-DAG: !rec_TailBig = !cir.struct<"TailBig" packed {data !s16i, data 
!cir.array<!s8i x 5>, pad !u8i}>
+// CIR-NUA-DAG: !rec_UnionPadAfterStorage2Ebase = 
!cir.struct<"UnionPadAfterStorage.base" packed {data !s32i, pad !cir.array<!u8i 
x 3>}>
 // CIR-NUA-DAG: !rec_OuterUnionPadAfterStorage = 
!cir.struct<"OuterUnionPadAfterStorage" {data !rec_UnionPadAfterStorage2Ebase, 
data !cir.bool}>
 // CIR-NUA-DAG: cir.global external @ou = #cir.zero : !rec_OuterUnion
 // CIR-NUA-DAG: cir.global external @of = #cir.zero : !rec_OuterFinal

diff  --git a/clang/test/CIR/CodeGen/paren-list-agg-init.cpp 
b/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
index f960254c288b4..74541056ea8c8 100644
--- a/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
+++ b/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
@@ -38,7 +38,7 @@ struct B {
 };
 
 // LLVM-DAG: [[STRUCT_C:%.*]] = type <{ [[STRUCT_B]], [[STRUCT_A]], i32, [4 x 
i8] }>
-// CIR-DAG: ![[STRUCT_C:.*]] = !cir.struct<"C" packed padded {data 
![[STRUCT_B]], data ![[STRUCT_A]], data !s32i, pad !cir.array<!u8i x 4>}>
+// CIR-DAG: ![[STRUCT_C:.*]] = !cir.struct<"C" packed {data ![[STRUCT_B]], 
data ![[STRUCT_A]], data !s32i, pad !cir.array<!u8i x 4>}>
 struct C : public B, public A {
   int c;
 };
@@ -60,7 +60,7 @@ struct E {
   ~E() {};
 };
 
-// CIR-DAG: ![[STRUCT_F:.*]] = !cir.struct<"F" padded {pad !u8i}>
+// CIR-DAG: ![[STRUCT_F:.*]] = !cir.struct<"F" {pad !u8i}>
 struct F {
   F (int i = 1);
   F (const F &f) = delete;
@@ -69,7 +69,7 @@ struct F {
 
 // LLVMCIR-DAG: [[STRUCT_G:%.*]] = type <{ i32, %struct.F, [3 x i8] }>
 // OGCG-DAG:    [[STRUCT_G:%.*]] = type <{ i32, [4 x i8] }>
-// CIR-DAG: ![[STRUCT_G:.*]] = !cir.struct<"G" packed padded {data !s32i, data 
!rec_F, pad !cir.array<!u8i x 3>}>
+// CIR-DAG: ![[STRUCT_G:.*]] = !cir.struct<"G" packed {data !s32i, data 
!rec_F, pad !cir.array<!u8i x 3>}>
 struct G {
   int a;
   F f;
@@ -87,7 +87,7 @@ union U {
 
 namespace gh61145 {
   // LLVM-DAG: [[STRUCT_VEC:%.*Vec.*]] = type { i8 }
-  // CIR-DAG: ![[STRUCT_VEC:.*]] = !cir.struct<"gh61145::Vec" padded {pad 
!u8i}>
+  // CIR-DAG: ![[STRUCT_VEC:.*]] = !cir.struct<"gh61145::Vec" {pad !u8i}>
   struct Vec {
     Vec();
     Vec(Vec&&);

diff  --git a/clang/test/CIR/CodeGen/pointer-to-empty-data-member.cpp 
b/clang/test/CIR/CodeGen/pointer-to-empty-data-member.cpp
index cd1cdd358e9d0..3c1d59223c9ad 100644
--- a/clang/test/CIR/CodeGen/pointer-to-empty-data-member.cpp
+++ b/clang/test/CIR/CodeGen/pointer-to-empty-data-member.cpp
@@ -7,7 +7,7 @@
 // RUN: FileCheck --check-prefix=LLVM,OGCG --input-file=%t.ll %s
 
 struct Empty {};
-// CIR-DAG: !rec_Empty = !cir.struct<"Empty" padded {pad !u8i}>
+// CIR-DAG: !rec_Empty = !cir.struct<"Empty" {pad !u8i}>
 // LLVMCIR-DAG: %struct.Empty = type { i8 }
 
 struct HasEmpty {
@@ -38,7 +38,7 @@ const HasEmpty2 globalHE2 = {{}, 1};
 
 // Not referenced enough to be emitted in 'after'.
 struct EmptyBase{};
-// CIR-BEFORE-DAG: !rec_EmptyBase = !cir.struct<"EmptyBase" padded {pad !u8i}>
+// CIR-BEFORE-DAG: !rec_EmptyBase = !cir.struct<"EmptyBase" {pad !u8i}>
 
 struct Base { int i; };
 // CIR-DAG: !rec_Base = !cir.struct<"Base" {data !s32i}>
@@ -87,7 +87,7 @@ struct hasNUA {
   [[no_unique_address]] EmptyBase eb6;
   int i;
 };
-// CIR-DAG: !rec_hasNUA = !cir.struct<"hasNUA" padded {data !s32i, pad 
!cir.array<!u8i x 4>}>
+// CIR-DAG: !rec_hasNUA = !cir.struct<"hasNUA" {data !s32i, pad 
!cir.array<!u8i x 4>}>
 // LLVM-DAG: %struct.hasNUA = type { i32, [4 x i8] }
 
 const hasNUA nua = {{},{},{},{},{},{}, 1};

diff  --git a/clang/test/CIR/CodeGen/record-member-kinds.c 
b/clang/test/CIR/CodeGen/record-member-kinds.c
index 3c6140d60a7fa..1e46153862a49 100644
--- a/clang/test/CIR/CodeGen/record-member-kinds.c
+++ b/clang/test/CIR/CodeGen/record-member-kinds.c
@@ -79,7 +79,7 @@ union ContainsEmptyUnion { struct E e; };
 // CIR-DAG: !rec_ContainsEmptyUnion = !cir.union<"ContainsEmptyUnion" {empty 
!rec_E}>
 
 struct AlignedTail { char c; int i __attribute__((aligned(8))); };
-// CIR-DAG: !rec_AlignedTail = !cir.struct<"AlignedTail" padded {data !s8i, 
pad !cir.array<!u8i x 7>, data !s32i, pad !cir.array<!u8i x 4>}>
+// CIR-DAG: !rec_AlignedTail = !cir.struct<"AlignedTail" {data !s8i, pad 
!cir.array<!u8i x 7>, data !s32i, pad !cir.array<!u8i x 4>}>
 // LLVM-DAG: %struct.AlignedTail = type { i8, [7 x i8], i32, [4 x i8] }
 
 // Name every record so that its CIR type reaches the output.

diff  --git a/clang/test/CIR/CodeGen/record-member-kinds.cpp 
b/clang/test/CIR/CodeGen/record-member-kinds.cpp
index 5da943fb32d5b..98be5496e7040 100644
--- a/clang/test/CIR/CodeGen/record-member-kinds.cpp
+++ b/clang/test/CIR/CodeGen/record-member-kinds.cpp
@@ -3,7 +3,7 @@
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s 
-o - | FileCheck %s --check-prefix=LLVM
 
 struct Empty {};
-// CIR-DAG: !rec_Empty = !cir.struct<"Empty" padded {pad !u8i}>
+// CIR-DAG: !rec_Empty = !cir.struct<"Empty" {pad !u8i}>
 // LLVM-DAG: %struct.Empty = type { i8 }
 
 // A C++ empty member is not empty for the ABI without [[no_unique_address]].
@@ -44,7 +44,7 @@ struct DerivesBitFieldBase : BitFieldBase { int i; };
 
 // A virtual base is marked the same way a non-virtual one is.
 struct HasBitFieldVBase : virtual BitFieldBase { int i; };
-// CIR-DAG: !rec_HasBitFieldVBase = !cir.struct<"HasBitFieldVBase" packed 
padded {data !cir.vptr, data !s32i, empty !rec_BitFieldBase, pad 
!cir.array<!u8i x 3>}>
+// CIR-DAG: !rec_HasBitFieldVBase = !cir.struct<"HasBitFieldVBase" packed 
{data !cir.vptr, data !s32i, empty !rec_BitFieldBase, pad !cir.array<!u8i x 3>}>
 
 struct ZeroLenEmptyArr { Empty e[0]; };
 // CIR-DAG: !rec_ZeroLenEmptyArr = !cir.struct<"ZeroLenEmptyArr" {empty 
!cir.array<!rec_Empty x 0>}>
@@ -59,7 +59,7 @@ struct NuaEmptyArr { [[no_unique_address]] Empty e[2]; int i; 
};
 // CIR-DAG: !rec_NuaEmptyArr = !cir.struct<"NuaEmptyArr" {data 
!cir.array<!rec_Empty x 2>, data !s32i}>
 
 struct AlignasTail { char c; alignas(8) int i; };
-// CIR-DAG: !rec_AlignasTail = !cir.struct<"AlignasTail" padded {data !s8i, 
pad !cir.array<!u8i x 7>, data !s32i, pad !cir.array<!u8i x 4>}>
+// CIR-DAG: !rec_AlignasTail = !cir.struct<"AlignasTail" {data !s8i, pad 
!cir.array<!u8i x 7>, data !s32i, pad !cir.array<!u8i x 4>}>
 // LLVM-DAG: %struct.AlignasTail = type { i8, [7 x i8], i32, [4 x i8] }
 
 // An unnamed bit-field unit is declared storage that holds no ABI data.
@@ -80,10 +80,10 @@ struct UnnamedFirst { int : 16; int a : 8; };
 // A zero-length bit-field separates one span into two units, and a record can
 // carry a data unit and an empty unit at once, in either order.
 struct SpanMixed { int a : 3; int : 0; int : 3; };
-// CIR-DAG: !rec_SpanMixed = !cir.struct<"SpanMixed" padded {data !u8i, pad 
!cir.array<!u8i x 3>, empty !u8i, pad !cir.array<!u8i x 3>}>
+// CIR-DAG: !rec_SpanMixed = !cir.struct<"SpanMixed" {data !u8i, pad 
!cir.array<!u8i x 3>, empty !u8i, pad !cir.array<!u8i x 3>}>
 
 struct SpanEmptyFirst { int : 3; int : 0; int b : 3; };
-// CIR-DAG: !rec_SpanEmptyFirst = !cir.struct<"SpanEmptyFirst" padded {empty 
!u8i, pad !cir.array<!u8i x 3>, data !u8i, pad !cir.array<!u8i x 3>}>
+// CIR-DAG: !rec_SpanEmptyFirst = !cir.struct<"SpanEmptyFirst" {empty !u8i, 
pad !cir.array<!u8i x 3>, data !u8i, pad !cir.array<!u8i x 3>}>
 
 // One unit covering both of these would need more than one register, so they
 // split into two units that are marked independently.
@@ -110,13 +110,13 @@ struct NearlyEmptyVBase { virtual ~NearlyEmptyVBase(); };
 // CIR-DAG: !rec_NearlyEmptyVBase = !cir.struct<"NearlyEmptyVBase" {data 
!cir.vptr}>
 
 struct HasNearlyEmptyVBase : virtual NearlyEmptyVBase { int i; };
-// CIR-DAG: !rec_HasNearlyEmptyVBase = !cir.struct<"HasNearlyEmptyVBase" 
packed padded {data !rec_NearlyEmptyVBase, data !s32i, pad !cir.array<!u8i x 
4>}>
+// CIR-DAG: !rec_HasNearlyEmptyVBase = !cir.struct<"HasNearlyEmptyVBase" 
packed {data !rec_NearlyEmptyVBase, data !s32i, pad !cir.array<!u8i x 4>}>
 
 // Both marks appear on one record: the byte array is storage the source
 // declared for its unnamed bit-field, while the byte after it is inserted by
 // the compiler.  The storage keeps its mark in the base subobject type.
 struct Clipped { Clipped(const Clipped &); int i; int : 24; };
-// CIR-DAG: !rec_Clipped = !cir.struct<"Clipped" packed padded {data !s32i, 
empty !cir.array<!u8i x 3>, pad !u8i}>
+// CIR-DAG: !rec_Clipped = !cir.struct<"Clipped" packed {data !s32i, empty 
!cir.array<!u8i x 3>, pad !u8i}>
 // CIR-DAG: !rec_Clipped2Ebase = !cir.struct<"Clipped.base" packed {data 
!s32i, empty !cir.array<!u8i x 3>}>
 
 struct DerivedClipped : Clipped { char c; };

diff  --git a/clang/test/CIR/CodeGen/record-type-metadata.cpp 
b/clang/test/CIR/CodeGen/record-type-metadata.cpp
index 156e69c55713e..8bc0ce148080c 100644
--- a/clang/test/CIR/CodeGen/record-type-metadata.cpp
+++ b/clang/test/CIR/CodeGen/record-type-metadata.cpp
@@ -21,8 +21,8 @@ void takesNTD(NonTrivialDtor n) {}
 
 // Record types should NOT contain ABI metadata keywords.
 // CIR-DAG: !rec_Trivial = !cir.struct<"Trivial" {data !s32i, data !s32i}>
-// CIR-DAG: !rec_Empty = !cir.struct<"Empty" padded {pad !u8i}>
-// CIR-DAG: !rec_Aligned = !cir.struct<"Aligned" padded {data !s32i, data 
!s32i, pad !cir.array<!u8i x 8>}>
+// CIR-DAG: !rec_Empty = !cir.struct<"Empty" {pad !u8i}>
+// CIR-DAG: !rec_Aligned = !cir.struct<"Aligned" {data !s32i, data !s32i, pad 
!cir.array<!u8i x 8>}>
 // CIR-DAG: !rec_NonTrivialDtor = !cir.struct<class "NonTrivialDtor" {data 
!s32i}>
 
 // ABI metadata lives in module-level cir.record_layouts attribute.

diff  --git a/clang/test/CIR/CodeGen/struct.c b/clang/test/CIR/CodeGen/struct.c
index e43f20bef56f1..09f4fc4e26266 100644
--- a/clang/test/CIR/CodeGen/struct.c
+++ b/clang/test/CIR/CodeGen/struct.c
@@ -12,7 +12,7 @@
 // CIR-DAG: !rec_OuterS = !cir.struct<"OuterS" {data !rec_InnerS, data !s32i}> 
 
 // CIR-DAG: !rec_InnerS = !cir.struct<"InnerS" {data !s32i, data !s8i}>
 // CIR-DAG: !rec_PackedS = !cir.struct<"PackedS" packed {data !s32i, data 
!s8i}>
-// CIR-DAG: !rec_PackedAndPaddedS = !cir.struct<"PackedAndPaddedS" packed 
padded {data !s32i, data !s8i, pad !u8i}>
+// CIR-DAG: !rec_PackedAndPaddedS = !cir.struct<"PackedAndPaddedS" packed 
{data !s32i, data !s8i, pad !u8i}>
 // CIR-DAG: !rec_NodeS = !cir.struct<"NodeS" {data 
!cir.ptr<!cir.struct<"NodeS">>}>
 // CIR-DAG: !rec_RightS = !cir.struct<"RightS" {data 
!cir.ptr<!cir.struct<"LeftS" {data !cir.ptr<!cir.struct<"RightS">>}>>}>
 // CIR-DAG: !rec_LeftS = !cir.struct<"LeftS" {data !cir.ptr<!rec_RightS>}>

diff  --git a/clang/test/CIR/CodeGen/template-specialization.cpp 
b/clang/test/CIR/CodeGen/template-specialization.cpp
index 66f81baf51f60..06fc834a4dcdf 100644
--- a/clang/test/CIR/CodeGen/template-specialization.cpp
+++ b/clang/test/CIR/CodeGen/template-specialization.cpp
@@ -13,7 +13,7 @@ class Templ<T, int>{};
 
 Templ<int, int> t;
 
-// CIR: !rec_Templ3Cint2C_int3E = !cir.struct<class "Templ<int, int>" padded 
{pad !u8i}>
+// CIR: !rec_Templ3Cint2C_int3E = !cir.struct<class "Templ<int, int>" {pad 
!u8i}>
 // CIR: cir.global external @t = #cir.zero : !rec_Templ3Cint2C_int3E
 
 // LLVM: %"class.Templ<int, int>" = type { i8 }

diff  --git a/clang/test/CIR/CodeGen/vtt.cpp b/clang/test/CIR/CodeGen/vtt.cpp
index 62fc27d4949b6..c4d22f2091b28 100644
--- a/clang/test/CIR/CodeGen/vtt.cpp
+++ b/clang/test/CIR/CodeGen/vtt.cpp
@@ -49,8 +49,8 @@ void D::y() {}
 // CIR-COMMON: !rec_A2Ebase = !cir.struct<"A.base" packed {data !cir.vptr, 
data !s32i}>
 // CIR-COMMON: !rec_B2Ebase = !cir.struct<"B.base" packed {data !cir.vptr, 
data !s32i}>
 // CIR-COMMON: !rec_C2Ebase = !cir.struct<"C.base" {data !cir.vptr, data 
!s64i}>
-// CIR-COMMON: !rec_A = !cir.struct<class "A" packed padded {data !cir.vptr, 
data !s32i, pad !cir.array<!u8i x 4>}>
-// CIR-COMMON: !rec_B = !cir.struct<class "B" packed padded {data !cir.vptr, 
data !s32i, pad !cir.array<!u8i x 4>, data !rec_A2Ebase, pad !cir.array<!u8i x 
4>}>
+// CIR-COMMON: !rec_A = !cir.struct<class "A" packed {data !cir.vptr, data 
!s32i, pad !cir.array<!u8i x 4>}>
+// CIR-COMMON: !rec_B = !cir.struct<class "B" packed {data !cir.vptr, data 
!s32i, pad !cir.array<!u8i x 4>, data !rec_A2Ebase, pad !cir.array<!u8i x 4>}>
 // CIR-COMMON: !rec_C = !cir.struct<class "C" {data !cir.vptr, data !s64i, 
data !rec_A2Ebase}>
 // CIR-COMMON: !rec_D = !cir.struct<class "D" {data !rec_B2Ebase, data 
!rec_C2Ebase, data !s64i, data !rec_A2Ebase}>
 

diff  --git a/clang/test/CIR/CodeGenCXX/zero_init_bases.cpp 
b/clang/test/CIR/CodeGenCXX/zero_init_bases.cpp
index 98078212c53e3..2827c655444e5 100644
--- a/clang/test/CIR/CodeGenCXX/zero_init_bases.cpp
+++ b/clang/test/CIR/CodeGenCXX/zero_init_bases.cpp
@@ -25,7 +25,7 @@ struct VirtualInherits : virtual Base1, virtual Base2 {
 // CIR: !rec_Base2 = !cir.struct<"Base2" {data !cir.float, data !cir.float, 
data !cir.float}>
 // CIR: !rec_Base1 = !cir.struct<"Base1" {data !s32i, data !s32i, data !s32i}>
 // CIR: !rec_Inherits = !cir.struct<"Inherits" {data !rec_Base1, data 
!rec_Base2, data !s32i, data !s32i, data !s32i}>
-// CIR: !rec_VirtualInherits = !cir.struct<"VirtualInherits" packed padded 
{data !cir.vptr, data !s32i, data !s32i, data !s32i, data !rec_Base1, data 
!rec_Base2, pad !cir.array<!u8i x 4>}>
+// CIR: !rec_VirtualInherits = !cir.struct<"VirtualInherits" packed {data 
!cir.vptr, data !s32i, data !s32i, data !s32i, data !rec_Base1, data 
!rec_Base2, pad !cir.array<!u8i x 4>}>
 //
 // LLVM: %struct.Inherits = type { %struct.Base1, %struct.Base2, i32, i32, i32 
}
 // LLVM: %struct.Base1 = type { i32, i32, i32 }

diff  --git a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp 
b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
index bfa205e97e3de..a337cd8569a88 100644
--- a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
+++ b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
@@ -7,15 +7,15 @@
 
 #include "Inputs/coroutine.h"
 
-// CIR-DAG: ![[VoidTask:.*]] = !cir.struct<"folly::coro::Task<void>" padded 
{pad !u8i}>
-// CIR-DAG: ![[IntTask:.*]] = !cir.struct<"folly::coro::Task<int>" padded {pad 
!u8i}>
-// CIR-DAG: ![[VoidPromisse:.*]] = 
!cir.struct<"folly::coro::Task<void>::promise_type" padded {pad !u8i}>
-// CIR-DAG: ![[IntPromisse:.*]] = 
!cir.struct<"folly::coro::Task<int>::promise_type" padded {pad !u8i}>
-// CIR-DAG: ![[StdString:.*]] = !cir.struct<"std::string" padded {pad !u8i}>
-// CIR-DAG: ![[CoroHandleVoid:.*]] = !cir.struct<"std::coroutine_handle<void>" 
padded {pad !u8i}>
-// CIR-DAG: ![[CoroHandlePromiseVoid:rec_.*]]  = 
!cir.struct<"std::coroutine_handle<folly::coro::Task<void>::promise_type>" 
padded {pad !u8i}>
-// CIR-DAG: ![[CoroHandlePromiseInt:rec_.*]] = 
!cir.struct<"std::coroutine_handle<folly::coro::Task<int>::promise_type>" 
padded {pad !u8i}>
-// CIR-DAG: ![[SuspendAlways:.*]] = !cir.struct<"std::suspend_always" padded 
{pad !u8i}>
+// CIR-DAG: ![[VoidTask:.*]] = !cir.struct<"folly::coro::Task<void>" {pad 
!u8i}>
+// CIR-DAG: ![[IntTask:.*]] = !cir.struct<"folly::coro::Task<int>" {pad !u8i}>
+// CIR-DAG: ![[VoidPromisse:.*]] = 
!cir.struct<"folly::coro::Task<void>::promise_type" {pad !u8i}>
+// CIR-DAG: ![[IntPromisse:.*]] = 
!cir.struct<"folly::coro::Task<int>::promise_type" {pad !u8i}>
+// CIR-DAG: ![[StdString:.*]] = !cir.struct<"std::string" {pad !u8i}>
+// CIR-DAG: ![[CoroHandleVoid:.*]] = !cir.struct<"std::coroutine_handle<void>" 
{pad !u8i}>
+// CIR-DAG: ![[CoroHandlePromiseVoid:rec_.*]]  = 
!cir.struct<"std::coroutine_handle<folly::coro::Task<void>::promise_type>" {pad 
!u8i}>
+// CIR-DAG: ![[CoroHandlePromiseInt:rec_.*]] = 
!cir.struct<"std::coroutine_handle<folly::coro::Task<int>::promise_type>" {pad 
!u8i}>
+// CIR-DAG: ![[SuspendAlways:.*]] = !cir.struct<"std::suspend_always" {pad 
!u8i}>
 
 // OGCG-DAG: %[[VoidPromisse:"struct.folly::coro::Task<void>::promise_type"]] 
= type { i8 }
 // OGCG-DAG: %[[VoidTask:"struct.folly::coro::Task"]] = type { i8 }

diff  --git a/clang/test/CIR/IR/array-ctor.cir 
b/clang/test/CIR/IR/array-ctor.cir
index 7e122adb3dda1..45d4d8fdb4a21 100644
--- a/clang/test/CIR/IR/array-ctor.cir
+++ b/clang/test/CIR/IR/array-ctor.cir
@@ -3,7 +3,7 @@
 
 !u8i = !cir.int<u, 8>
 !u64i = !cir.int<u, 64>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 module {
   cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)

diff  --git a/clang/test/CIR/IR/array-dtor.cir 
b/clang/test/CIR/IR/array-dtor.cir
index f7f835881ec38..ac884f5899d2e 100644
--- a/clang/test/CIR/IR/array-dtor.cir
+++ b/clang/test/CIR/IR/array-dtor.cir
@@ -1,7 +1,7 @@
 // RUN: cir-opt %s --verify-roundtrip | FileCheck %s
 
 !u8i = !cir.int<u, 8>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 module {
   cir.func private @_ZN1SD1Ev(!cir.ptr<!rec_S>)

diff  --git a/clang/test/CIR/IR/construct-catch-param.cir 
b/clang/test/CIR/IR/construct-catch-param.cir
index 7a77b81308126..b72eee2e8d028 100644
--- a/clang/test/CIR/IR/construct-catch-param.cir
+++ b/clang/test/CIR/IR/construct-catch-param.cir
@@ -3,7 +3,7 @@
 !s8i = !cir.int<s, 8>
 !u8i = !cir.int<u, 8>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 
 module {
 

diff  --git a/clang/test/CIR/IR/func-attrs.cir 
b/clang/test/CIR/IR/func-attrs.cir
index a96d88bf53cee..17a5ae905e5fe 100644
--- a/clang/test/CIR/IR/func-attrs.cir
+++ b/clang/test/CIR/IR/func-attrs.cir
@@ -5,7 +5,7 @@
 !s8i = !cir.int<s, 8>
 !u8i = !cir.int<u, 8>
 !void = !cir.void
-!rec_Struct = !cir.struct<"Struct" padded {data !u8i}>
+!rec_Struct = !cir.struct<"Struct" {pad !u8i}>
 !rec_anon_struct = !cir.struct<{data !s64i, data !s64i}>
 
 cir.func no_inline dso_local @Func1(%arg0: !s32i {llvm.noundef}, %arg1: 
!cir.float {llvm.noundef}) -> (!cir.float {llvm.noundef}) {

diff  --git a/clang/test/CIR/IR/global-init.cir 
b/clang/test/CIR/IR/global-init.cir
index a393005bcadb0..3e0387ed52594 100644
--- a/clang/test/CIR/IR/global-init.cir
+++ b/clang/test/CIR/IR/global-init.cir
@@ -2,9 +2,9 @@
 
 !u8i = !cir.int<u, 8>
 
-!rec_NeedsCtor = !cir.struct<"NeedsCtor" padded {data !u8i}>
-!rec_NeedsDtor = !cir.struct<"NeedsDtor" padded {data !u8i}>
-!rec_NeedsCtorDtor = !cir.struct<"NeedsCtorDtor" padded {data !u8i}>
+!rec_NeedsCtor = !cir.struct<"NeedsCtor" {pad !u8i}>
+!rec_NeedsDtor = !cir.struct<"NeedsDtor" {pad !u8i}>
+!rec_NeedsCtorDtor = !cir.struct<"NeedsCtorDtor" {pad !u8i}>
 
 module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
   cir.func private @_ZN9NeedsCtorC1Ev(!cir.ptr<!rec_NeedsCtor>)

diff  --git a/clang/test/CIR/IR/invalid-array-structor.cir 
b/clang/test/CIR/IR/invalid-array-structor.cir
index 5527670af2ab2..aad53dc5e5ed3 100644
--- a/clang/test/CIR/IR/invalid-array-structor.cir
+++ b/clang/test/CIR/IR/invalid-array-structor.cir
@@ -3,7 +3,7 @@
 
 !u8i = !cir.int<u, 8>
 !u64i = !cir.int<u, 64>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 module {
   cir.func dso_local @bad_static_array_ctor_with_num_elements(%p: 
!cir.ptr<!cir.array<!rec_S x 10>>, %n: !u64i) {
@@ -19,7 +19,7 @@ module {
 
 !u8i = !cir.int<u, 8>
 !u64i = !cir.int<u, 64>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 module {
   cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
@@ -38,7 +38,7 @@ module {
 // -----
 
 !u8i = !cir.int<u, 8>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 module {
   cir.func private @construct_int(!cir.ptr<!u8i>)
@@ -58,8 +58,8 @@ module {
 
 !u8i = !cir.int<u, 8>
 !u64i = !cir.int<u, 64>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
-!rec_T = !cir.struct<"T" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
+!rec_T = !cir.struct<"T" {pad !u8i}>
 
 module {
   cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
@@ -78,8 +78,8 @@ module {
 
 !u8i = !cir.int<u, 8>
 !u64i = !cir.int<u, 64>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
-!rec_T = !cir.struct<"T" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
+!rec_T = !cir.struct<"T" {pad !u8i}>
 
 module {
   cir.func private @_ZN1SC1Ev(!cir.ptr<!rec_S>)
@@ -99,7 +99,7 @@ module {
 
 !u8i = !cir.int<u, 8>
 !u64i = !cir.int<u, 64>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 module {
   cir.func dso_local @bad_static_array_dtor_with_num_elements(%p: 
!cir.ptr<!cir.array<!rec_S x 10>>, %n: !u64i) {
@@ -114,7 +114,7 @@ module {
 // -----
 
 !u8i = !cir.int<u, 8>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 module {
   cir.func private @_ZN1SD1Ev(!cir.ptr<!rec_S>)
@@ -131,7 +131,7 @@ module {
 // -----
 
 !u8i = !cir.int<u, 8>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 module {
   cir.func private @destroy_int(!cir.ptr<!u8i>)
@@ -150,8 +150,8 @@ module {
 // -----
 
 !u8i = !cir.int<u, 8>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
-!rec_T = !cir.struct<"T" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
+!rec_T = !cir.struct<"T" {pad !u8i}>
 
 module {
   cir.func private @_ZN1SD1Ev(!cir.ptr<!rec_S>)
@@ -169,8 +169,8 @@ module {
 
 !u8i = !cir.int<u, 8>
 !u64i = !cir.int<u, 64>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
-!rec_T = !cir.struct<"T" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
+!rec_T = !cir.struct<"T" {pad !u8i}>
 
 module {
   cir.func private @_ZN1SD1Ev(!cir.ptr<!rec_S>)
@@ -189,8 +189,8 @@ module {
 
 !u8i = !cir.int<u, 8>
 !u64i = !cir.int<u, 64>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
-!rec_T = !cir.struct<"T" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
+!rec_T = !cir.struct<"T" {pad !u8i}>
 
 module {
   cir.func private @_ZN1SD1Ev(!cir.ptr<!rec_S>)

diff  --git a/clang/test/CIR/IR/invalid-construct-catch-param.cir 
b/clang/test/CIR/IR/invalid-construct-catch-param.cir
index 375801a131a10..86c4c6d6612aa 100644
--- a/clang/test/CIR/IR/invalid-construct-catch-param.cir
+++ b/clang/test/CIR/IR/invalid-construct-catch-param.cir
@@ -3,7 +3,7 @@
 !u8i = !cir.int<u, 8>
 !s32i = !cir.int<s, 32>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 
 module {
 
@@ -34,7 +34,7 @@ cir.func @copy_fn_missing() {
 
 !u8i = !cir.int<u, 8>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 
 module {
 
@@ -68,7 +68,7 @@ cir.func @copy_fn_missing_thunk_attr() {
 
 !u8i = !cir.int<u, 8>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 
 module {
 
@@ -104,7 +104,7 @@ cir.func @copy_fn_wrong_arity() {
 !u8i = !cir.int<u, 8>
 !s32i = !cir.int<s, 32>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 
 module {
 
@@ -141,7 +141,7 @@ cir.func @copy_fn_non_void_return() {
 !u8i = !cir.int<u, 8>
 !s32i = !cir.int<s, 32>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 
 module {
 
@@ -179,7 +179,7 @@ cir.func @copy_fn_first_arg_mismatch() {
 !u8i = !cir.int<u, 8>
 !s32i = !cir.int<s, 32>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 
 module {
 
@@ -216,7 +216,7 @@ cir.func @copy_fn_second_arg_mismatch() {
 
 !u8i = !cir.int<u, 8>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 
 module {
 

diff  --git a/clang/test/CIR/IR/invalid-delete-array.cir 
b/clang/test/CIR/IR/invalid-delete-array.cir
index eaffbdfb2923a..7d6ce8be0951f 100644
--- a/clang/test/CIR/IR/invalid-delete-array.cir
+++ b/clang/test/CIR/IR/invalid-delete-array.cir
@@ -2,7 +2,7 @@
 
 !void = !cir.void
 !u8i = !cir.int<u, 8>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 module {
   cir.func private @_ZdaPvm(!cir.ptr<!void>)

diff  --git a/clang/test/CIR/IR/invalid-loop-cleanup.cir 
b/clang/test/CIR/IR/invalid-loop-cleanup.cir
index 02519dfdaf33e..ea07513bde788 100644
--- a/clang/test/CIR/IR/invalid-loop-cleanup.cir
+++ b/clang/test/CIR/IR/invalid-loop-cleanup.cir
@@ -1,7 +1,7 @@
 // RUN: cir-opt %s -verify-diagnostics -split-input-file
 
 !u8i = !cir.int<u, 8>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 cir.func private @ctor(!cir.ptr<!rec_S>)
 cir.func private @operatorBool(!cir.ptr<!rec_S>) -> !cir.bool
@@ -28,7 +28,7 @@ cir.func @while_cleanup_missing_kind() {
 // -----
 
 !u8i = !cir.int<u, 8>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 cir.func private @ctor(!cir.ptr<!rec_S>)
 cir.func private @operatorBool(!cir.ptr<!rec_S>) -> !cir.bool
@@ -57,7 +57,7 @@ cir.func @for_cleanup_missing_kind() {
 // -----
 
 !u8i = !cir.int<u, 8>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 cir.func private @ctor(!cir.ptr<!rec_S>)
 cir.func private @operatorBool(!cir.ptr<!rec_S>) -> !cir.bool
@@ -83,7 +83,7 @@ cir.func @while_cleanup_eh() {
 // -----
 
 !u8i = !cir.int<u, 8>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 cir.func private @ctor(!cir.ptr<!rec_S>)
 cir.func private @operatorBool(!cir.ptr<!rec_S>) -> !cir.bool

diff  --git a/clang/test/CIR/IR/loop-cleanup.cir 
b/clang/test/CIR/IR/loop-cleanup.cir
index 666709399d100..84267a33105f7 100644
--- a/clang/test/CIR/IR/loop-cleanup.cir
+++ b/clang/test/CIR/IR/loop-cleanup.cir
@@ -2,7 +2,7 @@
 
 !u8i = !cir.int<u, 8>
 #true = #cir.bool<true> : !cir.bool
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 module {
   cir.func private @ctor(!cir.ptr<!rec_S>)

diff  --git a/clang/test/CIR/IR/struct.cir b/clang/test/CIR/IR/struct.cir
index 30e278efcec8c..cf22833e211b4 100644
--- a/clang/test/CIR/IR/struct.cir
+++ b/clang/test/CIR/IR/struct.cir
@@ -25,24 +25,26 @@
 // CHECK-DAG: !rec_S1 = !cir.struct<"S1" {data !s32i, data !s32i}>
 // CHECK-DAG: !rec_Sc = !cir.struct<"Sc" {data !u8i, data !u16i, data !u32i}>
 
-// Packed and padded structs
+// A packed struct, and a packed and unpacked pair with the same members
 !rec_P1 = !cir.struct<"P1" packed {data !s32i, data !s32i}>
-!rec_P2 = !cir.struct<"P2" padded {data !u8i, data !u16i, data !u32i}>
-!rec_P3 = !cir.struct<"P3" packed padded {data !u8i, data !u16i, data !u32i}>
+!rec_P2 = !cir.struct<"P2" {data !u8i, pad !u8i, data !u16i, data !u32i}>
+!rec_P3 = !cir.struct<"P3" packed {data !u8i, pad !u8i, data !u16i, data 
!u32i}>
 
 // Members marked pad or empty
 !rec_P4 = !cir.struct<"P4" {pad !u8i}>
 !rec_P5 = !cir.struct<"P5" {empty !u8i}>
 !rec_P6 = !cir.struct<"P6" {data !u32i, empty !cir.array<!u8i x 3>, pad !u8i}>
-!rec_P7 = !cir.struct<"P7" packed padded {data !u8i, pad !u8i}>
+!rec_P7 = !cir.struct<"P7" packed {data !u8i, pad !u8i}>
+!rec_P8 = !cir.struct<"P8" {data !u16i, pad !u8i, pad !u8i}>
 
 // CHECK-DAG: !rec_P1 = !cir.struct<"P1" packed {data !s32i, data !s32i}>
-// CHECK-DAG: !rec_P2 = !cir.struct<"P2" padded {data !u8i, data !u16i, data 
!u32i}>
-// CHECK-DAG: !rec_P3 = !cir.struct<"P3" packed padded {data !u8i, data !u16i, 
data !u32i}>
+// CHECK-DAG: !rec_P2 = !cir.struct<"P2" {data !u8i, pad !u8i, data !u16i, 
data !u32i}>
+// CHECK-DAG: !rec_P3 = !cir.struct<"P3" packed {data !u8i, pad !u8i, data 
!u16i, data !u32i}>
 // CHECK-DAG: !rec_P4 = !cir.struct<"P4" {pad !u8i}>
 // CHECK-DAG: !rec_P5 = !cir.struct<"P5" {empty !u8i}>
 // CHECK-DAG: !rec_P6 = !cir.struct<"P6" {data !u32i, empty !cir.array<!u8i x 
3>, pad !u8i}>
-// CHECK-DAG: !rec_P7 = !cir.struct<"P7" packed padded {data !u8i, pad !u8i}>
+// CHECK-DAG: !rec_P7 = !cir.struct<"P7" packed {data !u8i, pad !u8i}>
+// CHECK-DAG: !rec_P8 = !cir.struct<"P8" {data !u16i, pad !u8i, pad !u8i}>
 
 // Records with identical member types, spelled apart by their kinds.
 !rec_M1 = !cir.struct<"M1" {data !u8i, pad !u8i}>
@@ -124,7 +126,8 @@ module  {
                      %arg20: !rec_PadNode,
                      %arg21: !rec_P7,
                      %arg22: !rec_anon_u_empty,
-                     %arg23: !rec_anon_u_plain) {
+                     %arg23: !rec_anon_u_plain,
+                     %arg24: !rec_P8) {
     cir.return
   }
 

diff  --git a/clang/test/CIR/Lowering/copy-skip-tail-padding.cir 
b/clang/test/CIR/Lowering/copy-skip-tail-padding.cir
new file mode 100644
index 0000000000000..e8146a522cab1
--- /dev/null
+++ b/clang/test/CIR/Lowering/copy-skip-tail-padding.cir
@@ -0,0 +1,140 @@
+// RUN: cir-opt %s --cir-to-llvm -o %t.mlir
+// RUN: FileCheck --input-file=%t.mlir %s
+
+!s8i = !cir.int<s, 8>
+!u8i = !cir.int<u, 8>
+!u16i = !cir.int<u, 16>
+!s32i = !cir.int<s, 32>
+
+!rec_Plain = !cir.struct<"Plain" {data !s32i, data !s32i}>
+!rec_Trail =
+    !cir.struct<"Trail" {data !s32i, data !u8i, pad !cir.array<!u8i x 3>}>
+!rec_MultiPad = !cir.struct<"MultiPad" {data !u16i, pad !u8i, pad !u8i}>
+!rec_Interior =
+    !cir.struct<"Interior" {data !u8i, pad !cir.array<!u8i x 3>, data !s32i}>
+!rec_TailEmpty = !cir.struct<"TailEmpty" {data !u8i, empty !u8i}>
+!rec_Packed = !cir.struct<"Packed" packed {data !u8i, data !s32i, pad !u8i}>
+!rec_AllPad = !cir.struct<"AllPad" {pad !u8i}>
+!rec_InteriorPacked =
+    !cir.struct<"InteriorPacked" packed {data !u8i, pad !cir.array<!u8i x 3>,
+                                        data !s32i}>
+!rec_Nest =
+    !cir.struct<"Nest" {data !rec_Trail, data !u8i, pad !cir.array<!u8i x 3>}>
+!rec_AlignasTail =
+    !cir.struct<"AlignasTail" {data !s8i, pad !cir.array<!u8i x 7>, data !s32i,
+                               pad !cir.array<!u8i x 4>}>
+!rec_SpanMixed =
+    !cir.struct<"SpanMixed" {data !u8i, pad !cir.array<!u8i x 3>, empty !u8i,
+                             pad !cir.array<!u8i x 3>}>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu",
+                   dlti.dl_spec = #dlti.dl_spec<>} {
+  // A record with no pad member has no tail padding to skip.
+  // CHECK-LABEL: llvm.func @plain
+  // CHECK:         %[[N:.*]] = llvm.mlir.constant(8 : i64) : i64
+  // CHECK:         "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %[[N]])
+  cir.func @plain(%arg0: !cir.ptr<!rec_Plain>, %arg1: !cir.ptr<!rec_Plain>) {
+    cir.copy %arg0 to %arg1 skip_tail_padding : !cir.ptr<!rec_Plain>
+    cir.return
+  }
+
+  // CHECK-LABEL: llvm.func @trail
+  // CHECK:         %[[N:.*]] = llvm.mlir.constant(5 : i64) : i64
+  // CHECK:         "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %[[N]])
+  cir.func @trail(%arg0: !cir.ptr<!rec_Trail>, %arg1: !cir.ptr<!rec_Trail>) {
+    cir.copy %arg0 to %arg1 skip_tail_padding : !cir.ptr<!rec_Trail>
+    cir.return
+  }
+
+  // Every member of the trailing run drops, not only the last one.
+  // CHECK-LABEL: llvm.func @multi_pad
+  // CHECK:         %[[N:.*]] = llvm.mlir.constant(2 : i64) : i64
+  // CHECK:         "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %[[N]])
+  cir.func @multi_pad(%arg0: !cir.ptr<!rec_MultiPad>,
+                      %arg1: !cir.ptr<!rec_MultiPad>) {
+    cir.copy %arg0 to %arg1 skip_tail_padding : !cir.ptr<!rec_MultiPad>
+    cir.return
+  }
+
+  // Padding before a data member is not tail padding, so nothing drops.
+  // CHECK-LABEL: llvm.func @interior_pad
+  // CHECK:         %[[N:.*]] = llvm.mlir.constant(8 : i64) : i64
+  // CHECK:         "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %[[N]])
+  cir.func @interior_pad(%arg0: !cir.ptr<!rec_Interior>,
+                         %arg1: !cir.ptr<!rec_Interior>) {
+    cir.copy %arg0 to %arg1 skip_tail_padding : !cir.ptr<!rec_Interior>
+    cir.return
+  }
+
+  // An empty member is declared storage a derived class may not reuse, so it
+  // stays inside the data size.
+  // CHECK-LABEL: llvm.func @tail_empty
+  // CHECK:         %[[N:.*]] = llvm.mlir.constant(2 : i64) : i64
+  // CHECK:         "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %[[N]])
+  cir.func @tail_empty(%arg0: !cir.ptr<!rec_TailEmpty>,
+                       %arg1: !cir.ptr<!rec_TailEmpty>) {
+    cir.copy %arg0 to %arg1 skip_tail_padding : !cir.ptr<!rec_TailEmpty>
+    cir.return
+  }
+
+  // A packed record sums its members at alignment one.
+  // CHECK-LABEL: llvm.func @packed
+  // CHECK:         %[[N:.*]] = llvm.mlir.constant(5 : i64) : i64
+  // CHECK:         "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %[[N]])
+  cir.func @packed(%arg0: !cir.ptr<!rec_Packed>, %arg1: !cir.ptr<!rec_Packed>) 
{
+    cir.copy %arg0 to %arg1 skip_tail_padding : !cir.ptr<!rec_Packed>
+    cir.return
+  }
+
+  // An empty C++ class lays out as one padding byte and has no data at all.
+  // CHECK-LABEL: llvm.func @all_pad
+  // CHECK:         %[[N:.*]] = llvm.mlir.constant(0 : i64) : i64
+  // CHECK:         "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %[[N]])
+  cir.func @all_pad(%arg0: !cir.ptr<!rec_AllPad>,
+                    %arg1: !cir.ptr<!rec_AllPad>) {
+    cir.copy %arg0 to %arg1 skip_tail_padding : !cir.ptr<!rec_AllPad>
+    cir.return
+  }
+
+  // Interior padding again, packed so that no realignment refills the hole.
+  // Dropping the pad here would change the answer.
+  // CHECK-LABEL: llvm.func @interior_pad_packed
+  // CHECK:         %[[N:.*]] = llvm.mlir.constant(8 : i64) : i64
+  // CHECK:         "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %[[N]])
+  cir.func @interior_pad_packed(%arg0: !cir.ptr<!rec_InteriorPacked>,
+                                %arg1: !cir.ptr<!rec_InteriorPacked>) {
+    cir.copy %arg0 to %arg1 skip_tail_padding : !cir.ptr<!rec_InteriorPacked>
+    cir.return
+  }
+
+  // Only the outermost object's tail padding is skippable, so a record member
+  // contributes its full size.
+  // CHECK-LABEL: llvm.func @nested
+  // CHECK:         %[[N:.*]] = llvm.mlir.constant(9 : i64) : i64
+  // CHECK:         "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %[[N]])
+  cir.func @nested(%arg0: !cir.ptr<!rec_Nest>, %arg1: !cir.ptr<!rec_Nest>) {
+    cir.copy %arg0 to %arg1 skip_tail_padding : !cir.ptr<!rec_Nest>
+    cir.return
+  }
+
+  // Interior and trailing padding together: the run stops at the data member.
+  // CHECK-LABEL: llvm.func @alignas_tail
+  // CHECK:         %[[N:.*]] = llvm.mlir.constant(12 : i64) : i64
+  // CHECK:         "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %[[N]])
+  cir.func @alignas_tail(%arg0: !cir.ptr<!rec_AlignasTail>,
+                         %arg1: !cir.ptr<!rec_AlignasTail>) {
+    cir.copy %arg0 to %arg1 skip_tail_padding : !cir.ptr<!rec_AlignasTail>
+    cir.return
+  }
+
+  // The run stops at the empty member: the pad after it drops, the pad
+  // before it does not.
+  // CHECK-LABEL: llvm.func @span_mixed
+  // CHECK:         %[[N:.*]] = llvm.mlir.constant(5 : i64) : i64
+  // CHECK:         "llvm.intr.memcpy"(%{{.+}}, %{{.+}}, %[[N]])
+  cir.func @span_mixed(%arg0: !cir.ptr<!rec_SpanMixed>,
+                       %arg1: !cir.ptr<!rec_SpanMixed>) {
+    cir.copy %arg0 to %arg1 skip_tail_padding : !cir.ptr<!rec_SpanMixed>
+    cir.return
+  }
+}

diff  --git a/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir 
b/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir
index ff7eacbe3f608..73153a9a596c3 100644
--- a/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir
+++ b/clang/test/CIR/Transforms/abi-lowering/x86_64-aggregate-nyi.cir
@@ -11,10 +11,10 @@
 !rec_SWrapsOverAligned = !cir.struct<"SWrapsOverAligned" {data !cir.double, 
data !rec_UOverAligned}>
 !rec_UEmptyLarge = !cir.union<"UEmptyLarge" {}, padding = {!cir.array<!u8i x 
32>}>
 !rec_P = !cir.struct<"P" packed {data !s8i, data !s32i}>
-!rec_Ov = !cir.struct<"Ov" padded {data !s32i, data !cir.array<!u8i x 12>}>
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_Ov = !cir.struct<"Ov" {data !s32i, pad !cir.array<!u8i x 12>}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 !rec_S1 = !cir.struct<"S1" {data !s16i, data !s16i, data !s16i}>
-!rec_AtomicWrapper = !cir.struct<padded {data !rec_S1, pad !cir.array<!s8i x 
2>}>
+!rec_AtomicWrapper = !cir.struct<{data !rec_S1, pad !cir.array<!s8i x 2>}>
 
 module attributes {
   dlti.dl_spec = #dlti.dl_spec<
@@ -93,7 +93,7 @@ module attributes {
     cir.return
   }
 
-  // CHECK: not yet implemented for type '!cir.struct<"Ov" padded
+  // CHECK: not yet implemented for type '!cir.struct<"Ov" {data !cir.int<s, 
32>, pad
 
   // An empty C++ class is laid out as a single padded byte, so it is rejected
   // by the padded check; its Ignore classification is deferred.
@@ -101,7 +101,7 @@ module attributes {
     cir.return
   }
 
-  // CHECK: not yet implemented for type '!cir.struct<"E" padded
+  // CHECK: not yet implemented for type '!cir.struct<"E" {pad
 
   // CIRGen wraps an atomic whose value is narrower than its lock-free size in
   // an anonymous record with a trailing pad member, which the padded check
@@ -110,7 +110,7 @@ module attributes {
     cir.return
   }
 
-  // CHECK: not yet implemented for type '!cir.struct<padded {{.*}}pad 
!cir.array<!cir.int<s, 8> x 2>}>'
+  // CHECK: not yet implemented for type '!cir.struct<{{.*}}pad 
!cir.array<!cir.int<s, 8> x 2>}>'
 
   // A scalable vector has no size the eightbyte rules can read.
   cir.func @take_scalable(%arg0: !cir.vector<[4] x !cir.float>) {

diff  --git a/clang/test/CIR/Transforms/cxx-abi-lowering-attrs.cir 
b/clang/test/CIR/Transforms/cxx-abi-lowering-attrs.cir
index ebc54e9875dea..569b1f6a87ab1 100644
--- a/clang/test/CIR/Transforms/cxx-abi-lowering-attrs.cir
+++ b/clang/test/CIR/Transforms/cxx-abi-lowering-attrs.cir
@@ -3,7 +3,7 @@
 
 !u8i = !cir.int<u, 8>
 !s32i = !cir.int<s, 32>
-!rec_Other = !cir.struct<"Other" padded {data !u8i}>
+!rec_Other = !cir.struct<"Other" {pad !u8i}>
 !rec_Struct = !cir.struct<"Struct" {data !cir.data_member<!s32i in 
!rec_Other>}>
 // CHECK: !rec_Struct = !cir.struct<"Struct" {data !s64i}>
 

diff  --git 
a/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch-invalid.cir 
b/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch-invalid.cir
index 25e1365b59da1..8d191bb05c8ac 100644
--- a/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch-invalid.cir
+++ b/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch-invalid.cir
@@ -7,7 +7,7 @@
 !s32i = !cir.int<s, 32>
 !u8i = !cir.int<u, 8>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 
 module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
 
@@ -65,7 +65,7 @@ cir.func @bad_kind() {
 !s32i = !cir.int<s, 32>
 !u8i = !cir.int<u, 8>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 
 module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
 
@@ -118,7 +118,7 @@ cir.func @copy_fn_no_body() {
 !s32i = !cir.int<s, 32>
 !u8i = !cir.int<u, 8>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 
 module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
 

diff  --git a/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch.cir 
b/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch.cir
index 81c17ae8f64c3..25c8e5695c0d5 100644
--- a/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch.cir
+++ b/clang/test/CIR/Transforms/eh-abi-lowering-construct-catch.cir
@@ -22,7 +22,7 @@
 !u32i = !cir.int<u, 32>
 !u8i = !cir.int<u, 8>
 !void = !cir.void
-!rec_E = !cir.struct<"E" padded {data !u8i}>
+!rec_E = !cir.struct<"E" {pad !u8i}>
 !rec_anon_struct = !cir.struct<{data !cir.ptr<!u8i>, data !cir.ptr<!u8i>}>
 
 module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {

diff  --git a/clang/test/CIR/Transforms/flatten-cleanup-scope-eh.cir 
b/clang/test/CIR/Transforms/flatten-cleanup-scope-eh.cir
index 5fc9f4fdbe42f..93b3f99188562 100644
--- a/clang/test/CIR/Transforms/flatten-cleanup-scope-eh.cir
+++ b/clang/test/CIR/Transforms/flatten-cleanup-scope-eh.cir
@@ -4,7 +4,7 @@
 !s32i = !cir.int<s, 32>
 !u8i = !cir.int<u, 8>
 !rec_SomeClass = !cir.struct<"SomeClass" {data !s32i}>
-!rec_NonTrivial = !cir.struct<"NonTrivial" padded {data !u8i}>
+!rec_NonTrivial = !cir.struct<"NonTrivial" {pad !u8i}>
 #false = #cir.bool<false> : !cir.bool
 #true = #cir.bool<true> : !cir.bool
 

diff  --git a/clang/test/CIR/Transforms/flatten-loop-cleanup.cir 
b/clang/test/CIR/Transforms/flatten-loop-cleanup.cir
index b0dded0f931a2..0db66822ad4aa 100644
--- a/clang/test/CIR/Transforms/flatten-loop-cleanup.cir
+++ b/clang/test/CIR/Transforms/flatten-loop-cleanup.cir
@@ -3,7 +3,7 @@
 
 !u8i = !cir.int<u, 8>
 !s32i = !cir.int<s, 32>
-!rec_S = !cir.struct<"S" padded {data !u8i}>
+!rec_S = !cir.struct<"S" {pad !u8i}>
 
 module {
   cir.func private @ctor(!cir.ptr<!rec_S>)

diff  --git a/clang/test/CIR/Transforms/mem2reg.cir 
b/clang/test/CIR/Transforms/mem2reg.cir
index 84f83595f1c6c..3162a3f5e09a5 100644
--- a/clang/test/CIR/Transforms/mem2reg.cir
+++ b/clang/test/CIR/Transforms/mem2reg.cir
@@ -1,6 +1,10 @@
 // RUN: cir-opt %s -cir-flatten-cfg -mem2reg -o - | FileCheck %s
 
 !s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!u16i = !cir.int<u, 16>
+!rec_Whole = !cir.struct<"Whole" {data !u16i, data !u16i}>
+!rec_AllPad = !cir.struct<"AllPad" {pad !u8i}>
 
 module {
   // Promote CIR stack slots through the generic mem2reg pass after CFG 
flattening.
@@ -67,4 +71,27 @@ module {
     %2 = cir.load %0 : !cir.ptr<!s32i>, !s32i
     cir.return %2 : !s32i
   }
+
+  // A skip_tail_padding copy that covers the whole slot still promotes.
+  // CHECK-LABEL: cir.func @promote_copy_covering_slot
+  cir.func @promote_copy_covering_slot(%src: !cir.ptr<!rec_Whole>) -> 
!rec_Whole {
+    // CHECK-NOT: cir.alloca
+    // CHECK-NOT: cir.copy
+    %slot = cir.alloca "s" align(2) init : !cir.ptr<!rec_Whole>
+    cir.copy %src to %slot skip_tail_padding : !cir.ptr<!rec_Whole>
+    %v = cir.load %slot : !cir.ptr<!rec_Whole>, !rec_Whole
+    cir.return %v : !rec_Whole
+  }
+
+  // An all-pad record has a data size of zero, so the copy leaves the slot's
+  // byte untouched and the slot cannot be promoted.
+  // CHECK-LABEL: cir.func @do_not_promote_partial_copy
+  cir.func @do_not_promote_partial_copy(%src: !cir.ptr<!rec_AllPad>) -> 
!rec_AllPad {
+    // CHECK: %[[SLOT:.*]] = cir.alloca "s" align(1) init : 
!cir.ptr<!rec_AllPad>
+    // CHECK: cir.copy %{{.+}} to %[[SLOT]] skip_tail_padding : 
!cir.ptr<!rec_AllPad>
+    %slot = cir.alloca "s" align(1) init : !cir.ptr<!rec_AllPad>
+    cir.copy %src to %slot skip_tail_padding : !cir.ptr<!rec_AllPad>
+    %v = cir.load %slot : !cir.ptr<!rec_AllPad>, !rec_AllPad
+    cir.return %v : !rec_AllPad
+  }
 }

diff  --git a/clang/unittests/CIR/PointerLikeTest.cpp 
b/clang/unittests/CIR/PointerLikeTest.cpp
index af0faa4a866cb..8e5dc6636e01e 100644
--- a/clang/unittests/CIR/PointerLikeTest.cpp
+++ b/clang/unittests/CIR/PointerLikeTest.cpp
@@ -170,8 +170,7 @@ class CIROpenACCPointerLikeTest : public ::testing::Test {
       structTy = cir::StructType::get(&context, getUniqueRecordName("S"),
                                       /*is_class=*/false);
     mlir::Type members[] = {ty1, ty2};
-    structTy.complete(members, /*packed=*/false, /*padded=*/false,
-                      /*padding=*/{},
+    structTy.complete(members, /*packed=*/false, /*padding=*/{},
                       cir::RecordType::getAllDataKinds(members));
     mlir::Type ptrTy = cir::PointerType::get(structTy);
 
@@ -255,8 +254,7 @@ class CIROpenACCPointerLikeTest : public ::testing::Test {
         cir::StructType::get(&context, getUniqueRecordName("S"),
                              /*is_class=*/false);
     mlir::Type members[] = {ptrTy, ptrTy};
-    structTy.complete(members, /*packed=*/false, /*padded=*/false,
-                      /*padding=*/{},
+    structTy.complete(members, /*packed=*/false, /*padding=*/{},
                       cir::RecordType::getAllDataKinds(members));
     mlir::Type structPptrTy = cir::PointerType::get(structTy);
 
@@ -368,7 +366,7 @@ TEST_F(CIROpenACCPointerLikeTest, 
testPointerToStructMember) {
       cir::StructType::get(&context, getUniqueRecordName("S"),
                            /*is_class=*/false);
   mlir::Type members[] = {i32Ty, i32Ty};
-  structTy.complete(members, /*packed=*/false, /*padded=*/false,
-                    /*padding=*/{}, cir::RecordType::getAllDataKinds(members));
+  structTy.complete(members, /*packed=*/false, /*padding=*/{},
+                    cir::RecordType::getAllDataKinds(members));
   testPointerToMemberType(structTy, 
mlir::acc::VariableTypeCategory::composite);
 }

diff  --git a/clang/unittests/CIR/RecordMemberKindTest.cpp 
b/clang/unittests/CIR/RecordMemberKindTest.cpp
index fd6de7a4296d4..9bbaf39c2ca17 100644
--- a/clang/unittests/CIR/RecordMemberKindTest.cpp
+++ b/clang/unittests/CIR/RecordMemberKindTest.cpp
@@ -53,7 +53,7 @@ class RecordMemberKindTest : public ::testing::Test {
                         llvm::ArrayRef<mlir::Type> members,
                         llvm::ArrayRef<RecordMemberKind> kinds) {
     auto ty = StructType::get(&context, getName(name), /*is_class=*/false);
-    ty.complete(members, /*packed=*/false, /*isPadded=*/false, kinds);
+    ty.complete(members, /*packed=*/false, kinds);
     return ty;
   }
 };
@@ -75,6 +75,42 @@ TEST_F(RecordMemberKindTest, 
EmptyForTheABIWhenNoMemberHoldsData) {
                    .isEmptyForABI());
 }
 
+TEST_F(RecordMemberKindTest, PaddedFollowsThePadKinds) {
+  IntType u8 = getU8();
+  EXPECT_FALSE(makeStruct("d", {u8}, {RecordMemberKind::Data}).getPadded());
+  EXPECT_FALSE(makeStruct("e", {u8}, {RecordMemberKind::Empty}).getPadded());
+  EXPECT_TRUE(makeStruct("p", {u8}, {RecordMemberKind::Pad}).getPadded());
+  // Interior padding counts too, not just a trailing run.
+  EXPECT_TRUE(makeStruct("dpd", {u8, u8, u8},
+                         {RecordMemberKind::Data, RecordMemberKind::Pad,
+                          RecordMemberKind::Data})
+                  .getPadded());
+  // An incomplete struct has no members to read a kind from.
+  EXPECT_FALSE(StructType::get(&context, getName("inc"), /*is_class=*/false)
+                   .getPadded());
+}
+
+TEST_F(RecordMemberKindTest, AUnionsPaddingComesFromItsPaddingSlot) {
+  IntType u8 = getU8();
+  llvm::SmallVector<mlir::Type> members{u8};
+  llvm::SmallVector<RecordMemberKind> empty{RecordMemberKind::Empty};
+  llvm::ArrayRef<mlir::Type> membersRef(members);
+
+  EXPECT_FALSE(UnionType::get(&context, membersRef, getName("ub"),
+                              /*packed=*/false, /*padding=*/mlir::Type{},
+                              RecordType::getAllDataKinds(membersRef))
+                   .getPadded());
+  EXPECT_TRUE(UnionType::get(&context, membersRef, getName("up"),
+                             /*packed=*/false, /*padding=*/u8,
+                             RecordType::getAllDataKinds(membersRef))
+                  .getPadded());
+  // An empty kind on a member is not padding.
+  EXPECT_FALSE(UnionType::get(&context, membersRef, getName("ue"),
+                              /*packed=*/false, /*padding=*/mlir::Type{},
+                              llvm::ArrayRef<RecordMemberKind>(empty))
+                   .getPadded());
+}
+
 TEST_F(RecordMemberKindTest, RejectsAKindListThatDoesNotNameEveryMember) {
   // The assembly syntax cannot express either of these, since it builds one
   // kind per member, but a C++ caller can.
@@ -85,15 +121,14 @@ TEST_F(RecordMemberKindTest, 
RejectsAKindListThatDoesNotNameEveryMember) {
   llvm::ArrayRef<mlir::Type> membersRef(members);
   llvm::ArrayRef<RecordMemberKind> kindsRef(tooFew);
   EXPECT_FALSE(StructType::getChecked(getLoc(), &context, membersRef,
-                                      /*packed=*/false, /*padded=*/false,
-                                      /*is_class=*/false, kindsRef));
+                                      /*packed=*/false, /*is_class=*/false,
+                                      kindsRef));
   EXPECT_EQ(diags.count, 1u);
   EXPECT_EQ(diags.lastMessage, "expected 2 member kinds, got 1");
 
   // An omitted list is not shorthand for all-data.
   EXPECT_FALSE(StructType::getChecked(getLoc(), &context, membersRef,
-                                      /*packed=*/false, /*padded=*/false,
-                                      /*is_class=*/false,
+                                      /*packed=*/false, /*is_class=*/false,
                                       llvm::ArrayRef<RecordMemberKind>{}));
   EXPECT_EQ(diags.count, 2u);
   EXPECT_EQ(diags.lastMessage, "expected 2 member kinds, got 0");
@@ -146,12 +181,12 @@ TEST_F(RecordMemberKindTest, 
AUnionsTailPaddingSlotIsNotAMember) {
 
 TEST_F(RecordMemberKindTest, KindsTakePartInAnonymousTypeIdentity) {
   IntType u8 = getU8();
-  auto kindsPad = StructType::get(
-      &context, {u8, u8}, /*packed=*/false, /*padded=*/false,
-      /*is_class=*/false, {RecordMemberKind::Data, RecordMemberKind::Pad});
-  auto kindsEmpty = StructType::get(
-      &context, {u8, u8}, /*packed=*/false, /*padded=*/false,
-      /*is_class=*/false, {RecordMemberKind::Data, RecordMemberKind::Empty});
+  auto kindsPad =
+      StructType::get(&context, {u8, u8}, /*packed=*/false, /*is_class=*/false,
+                      {RecordMemberKind::Data, RecordMemberKind::Pad});
+  auto kindsEmpty =
+      StructType::get(&context, {u8, u8}, /*packed=*/false, /*is_class=*/false,
+                      {RecordMemberKind::Data, RecordMemberKind::Empty});
   EXPECT_NE(kindsPad, kindsEmpty);
 
   // Kinds are provenance rather than layout.

diff  --git a/clang/unittests/CIR/RecordTypeMetadataTest.cpp 
b/clang/unittests/CIR/RecordTypeMetadataTest.cpp
index b96f7ad36874a..3e7b0d9054970 100644
--- a/clang/unittests/CIR/RecordTypeMetadataTest.cpp
+++ b/clang/unittests/CIR/RecordTypeMetadataTest.cpp
@@ -64,8 +64,7 @@ TEST_F(RecordLayoutAttrTest, RecordTypeUnchanged) {
   IntType i32 = IntType::get(&context, 32, true);
   auto ty = StructType::get(&context, getName("Foo"), /*is_class=*/false);
   mlir::Type members[] = {i32, i32};
-  ty.complete(members, /*packed=*/false, /*padded=*/false,
-              RecordType::getAllDataKinds(members));
+  ty.complete(members, /*packed=*/false, RecordType::getAllDataKinds(members));
   EXPECT_TRUE(ty.isComplete());
   EXPECT_EQ(ty.getMembers().size(), 2u);
 }


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to