https://github.com/adams381 created 
https://github.com/llvm/llvm-project/pull/198423

Union tail padding was stored as an extra entry in `members`, so
`getLargestMember()` had to treat the last element specially and the IR
looked like a three-field union.  Andy asked for tail pad in a separate
`padding = { … }` slot on `!cir.record<union …>` instead.

This adds optional `padding` on `RecordType` (parse/print, verify, storage),
wires CIRGen layout to set it for padded unions, and threads it through
CXXABILowering and LowerToLLVM.  Codegen tests and a unit test are updated.

Stacks on #198361 (size in `getTypeSizeInBits`); land that first or merge
both together.


>From e2900f6a5d2be97d968c756e49e5316a456de608 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Thu, 14 May 2026 14:45:43 -0700
Subject: [PATCH 1/2] [CIR] Include union tail pad in getTypeSizeInBits

When a union's highest-aligned variant is smaller than its highest-size
variant -- the canonical example is libstdc++'s std::basic_string SSO
union `{ char[16]; size_t; }` -- CIRRecordLowering::lowerUnion appends
a trailing byte-array member to extend the highest-aligned variant up
to the union's layout size, and the union record is marked `padded`.
LowerToLLVM mirrors this by emitting the union as
`{largest_member, padding}`.  RecordType::getTypeSizeInBits, however,
returned just the largest-aligned member's size, missing the padding
member -- so the CIR data-layout view of the union was 8 bytes smaller
than the lowered LLVM form.

CIRRecordLowering::insertPadding compensates by appending a tail
byte-array to the parent record when a child member's CIR-reported size
falls short of its AST offset implies it should reach.  So a struct
containing the SSO union (e.g., std::basic_string itself) ended up
with a spurious 8-byte trailing pad that LowerToLLVM faithfully
translated into the LLVM struct, making the lowered struct 8 bytes
wider than the AST type.  GEPs into arrays of those structs then ran
past the end of the allocation.  For std::basic_string-shaped types
this manifests as heap corruption: assigning to the last slot of an
N-slot heap-allocated array stomps the next chunk's malloc metadata,
and the next allocation aborts with
"sysmalloc: Assertion ... old_top == initial_top".

Have getTypeSizeInBits add the trailing padding member's size when the
union is `padded`, matching exactly what LowerToLLVM does.
insertPadding then sees the SSO union as 16 bytes and emits no
spurious parent-record trailing pad.  std::basic_string returns to its
32-byte AST layout and arrays of strings stride correctly.

Eigen's array_of_string test reproduces the corruption directly with a
heap-corruption assertion in libc malloc.  Six other Eigen
WRONG_RESULTs sharing the std::string-stride shape -- bicgstab,
conjugate_gradient, lscg, sparse_product, sparse_permutations,
incomplete_cholesky -- clear on the same fix, along with
eigensolver_selfadjoint, product_notemporary, and sparselu.
---
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         | 18 ++++++++-
 .../CIR/CodeGen/record-with-padded-union.cpp  | 39 +++++++++++++++++++
 2 files changed, 56 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/CIR/CodeGen/record-with-padded-union.cpp

diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 1ceeea954daa2..9ec1c20c0be73 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -382,7 +382,23 @@ RecordType::getTypeSizeInBits(const mlir::DataLayout 
&dataLayout,
     mlir::Type largest = getLargestMember(dataLayout);
     if (!largest)
       return llvm::TypeSize::getFixed(0);
-    return dataLayout.getTypeSizeInBits(largest);
+    // `getLargestMember` returns the highest-aligned variant (which dictates
+    // the union's alignment), not necessarily the largest by size.  When the
+    // union is `padded` -- i.e., its highest-aligned variant is strictly
+    // smaller than its layout size, as happens for any union containing both
+    // a small high-alignment scalar and a larger low-alignment array (e.g.,
+    // `union { char[16]; size_t; }`) -- `lowerUnion` appended a trailing
+    // byte-array member to extend the highest-aligned variant up to the
+    // layout size, and `LowerToLLVM` mirrors this by emitting the union as
+    // `{largest, padding}`.  Include that padding here so `getTypeSize`
+    // reports the same size `LowerToLLVM` produces; otherwise a parent
+    // record containing the union gets a spurious tail-padding member added
+    // by `insertPadding`, making `sizeof(parent)` and array GEPs off by the
+    // missing bytes.
+    llvm::TypeSize size = dataLayout.getTypeSizeInBits(largest);
+    if (getPadded())
+      size += dataLayout.getTypeSizeInBits(*getMembers().rbegin());
+    return size;
   }
 
   auto recordSize = static_cast<uint64_t>(computeStructSize(dataLayout));
diff --git a/clang/test/CIR/CodeGen/record-with-padded-union.cpp 
b/clang/test/CIR/CodeGen/record-with-padded-union.cpp
new file mode 100644
index 0000000000000..6711bb9d599c2
--- /dev/null
+++ b/clang/test/CIR/CodeGen/record-with-padded-union.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+struct SSO {
+  char *p;
+  unsigned long len;
+  union {
+    char local[16];
+    unsigned long capacity;
+  };
+};
+
+// Inner union's tail padding must not bleed into the outer record.
+// CIR: !rec_anon{{.*}} = !cir.record<union "anon{{.*}}" padded 
{!cir.array<!s8i x 16>, !u64i, !cir.array<!u8i x 8>}>
+// CIR: !rec_SSO = !cir.record<struct "SSO" {!cir.ptr<!s8i>, !u64i, 
!rec_anon{{.*}}}>
+
+// LLVM: %struct.SSO = type { ptr, i64, %union.anon{{.*}} }
+// LLVM: %union.anon{{.*}} = type { i64, [8 x i8] }
+
+// OGCG: %struct.SSO = type { ptr, i64, %union.anon }
+// OGCG: %union.anon = type { i64, [8 x i8] }
+
+extern "C" SSO *last_of_three() {
+  SSO *p = new SSO[3];
+  return &p[2];
+}
+
+// Allocation is 3*sizeof(SSO)=96; per-element stride comes from struct size.
+// LLVM-LABEL: define {{.*}}@last_of_three
+// LLVM: call {{.*}}@_Znam(i64 noundef 96)
+// LLVM: getelementptr %struct.SSO, ptr %{{.+}}, i64 2
+
+// OGCG-LABEL: define {{.*}}@last_of_three
+// OGCG: call {{.*}}@_Znam(i64 noundef 96)
+// OGCG: getelementptr {{.*}}%struct.SSO, ptr %{{.+}}, i64 2

>From abe65069dd698b0c214909910b83d4a5327722d7 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Mon, 18 May 2026 16:46:53 -0700
Subject: [PATCH 2/2] [CIR] Represent union tail padding in RecordType padding
 field

Union tail padding was modeled as an extra member in `members`,
which confused `getLargestMember()` and member indexing.  Move tail
pad into an optional `padding` type parameter on `!cir.record`
(`padding = { ty }` in assembly), populated from CIRGen layout and
preserved through CXXABILowering and LowerToLLVM.

Updates codegen tests and adds a unit test for padded unions with
separate padding.  Complements #198361, which fixes size computation for
the old representation.
---
 .../include/clang/CIR/Dialect/IR/CIRTypes.td  | 19 +++--
 .../clang/CIR/Dialect/IR/CIRTypesDetails.h    | 33 +++++----
 clang/lib/CIR/CodeGen/CIRGenBuilder.cpp       |  3 +-
 clang/lib/CIR/CodeGen/CIRGenBuilder.h         |  9 +--
 .../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 18 +++--
 clang/lib/CIR/Dialect/IR/CIRTypes.cpp         | 72 +++++++++++++------
 .../CIR/Dialect/Transforms/CXXABILowering.cpp | 15 ++--
 .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp |  8 +--
 clang/test/CIR/CodeGen/empty-union.cpp        |  4 +-
 .../CIR/CodeGen/record-with-padded-union.cpp  |  2 +-
 clang/test/CIR/CodeGen/union.c                |  4 +-
 clang/unittests/CIR/UnionTypeSizeTest.cpp     | 17 +++++
 12 files changed, 134 insertions(+), 70 deletions(-)

diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td 
b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
index 504ec850ddb5a..ecf646a1e97a1 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypes.td
@@ -661,6 +661,8 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
         !rec_p1 = !cir.record<struct "p1" packed {!u8i, !u8i}>
         !rec_p2 = !cir.record<struct "p2" padded {!u8i, !u8i}>
         !rec_p3 = !cir.record<struct "p3" packed padded {!s32i, !u8i, !u8i}>
+        !rec_u = !cir.record<union "u" padded {!s8i, !u64i},
+            padding = {!cir.array<!u8i x 8>}>
     ```
 
     Incomplete records are mutable, meaning they can be later completed with a
@@ -682,7 +684,8 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
     "bool":$incomplete,
     "bool":$packed,
     "bool":$padded,
-    "RecordType::RecordKind":$kind
+    "RecordType::RecordKind":$kind,
+    OptionalParameter<"mlir::Type">:$padding
   );
 
   // StorageClass is defined in C++ for mutability.
@@ -699,10 +702,11 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
       "mlir::StringAttr":$name,
       "bool":$packed,
       "bool":$padded,
-      "RecordKind":$kind
+      "RecordKind":$kind,
+      CArg<"mlir::Type", "{}">:$padding
     ), [{
       return $_get($_ctxt, members, name, /*incomplete=*/false, packed, padded,
-                   kind);
+                   kind, padding);
     }]>,
 
     // Create an identified and incomplete record type.
@@ -712,7 +716,7 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
     ), [{
       return $_get($_ctxt, /*members=*/llvm::ArrayRef<Type>{}, name,
                          /*incomplete=*/true, /*packed=*/false,
-                         /*padded=*/false, kind);
+                         /*padded=*/false, kind, mlir::Type{});
     }]>,
 
     // Create an anonymous record type (always complete).
@@ -720,10 +724,11 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
       "llvm::ArrayRef<mlir::Type>":$members,
       "bool":$packed,
       "bool":$padded,
-      "RecordKind":$kind
+      "RecordKind":$kind,
+      CArg<"mlir::Type", "{}">:$padding
     ), [{
       return $_get($_ctxt, members, mlir::StringAttr{}, /*incomplete=*/false,
-                      packed, padded, kind);
+                      packed, padded, kind, padding);
     }]>];
 
   let extraClassDeclaration = [{
@@ -758,7 +763,7 @@ def CIR_RecordType : CIR_Type<"Record", "record", [
     }
 
     void complete(llvm::ArrayRef<mlir::Type> members, bool packed,
-                  bool isPadded);
+                  bool isPadded, mlir::Type padding = {});
 
     uint64_t getElementOffset(const mlir::DataLayout &dataLayout,
               unsigned idx) const;
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h 
b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
index de8c17cab8642..8b3c48ee9ec27 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRTypesDetails.h
@@ -34,12 +34,13 @@ struct RecordTypeStorage : public mlir::TypeStorage {
     bool packed;
     bool padded;
     RecordType::RecordKind kind;
+    mlir::Type padding;
 
     KeyTy(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
           bool incomplete, bool packed, bool padded,
-          RecordType::RecordKind kind)
+          RecordType::RecordKind kind, mlir::Type padding)
         : members(members), name(name), incomplete(incomplete), packed(packed),
-          padded(padded), kind(kind) {}
+          padded(padded), kind(kind), padding(padding) {}
   };
 
   llvm::ArrayRef<mlir::Type> members;
@@ -48,39 +49,40 @@ struct RecordTypeStorage : public mlir::TypeStorage {
   bool packed;
   bool padded;
   RecordType::RecordKind kind;
+  mlir::Type padding;
 
   RecordTypeStorage(llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
                     bool incomplete, bool packed, bool padded,
-                    RecordType::RecordKind kind)
+                    RecordType::RecordKind kind, mlir::Type padding)
       : members(members), name(name), incomplete(incomplete), packed(packed),
-        padded(padded), kind(kind) {
+        padded(padded), kind(kind), padding(padding) {
     assert((name || !incomplete) && "Incomplete records must have a name");
   }
 
   KeyTy getAsKey() const {
-    return KeyTy(members, name, incomplete, packed, padded, kind);
+    return KeyTy(members, name, incomplete, packed, padded, kind, padding);
   }
 
   bool operator==(const KeyTy &key) const {
     if (name)
       return (name == key.name) && (kind == key.kind);
-    return std::tie(members, name, incomplete, packed, padded, kind) ==
+    return std::tie(members, name, incomplete, packed, padded, kind, padding) 
==
            std::tie(key.members, key.name, key.incomplete, key.packed,
-                    key.padded, key.kind);
+                    key.padded, key.kind, key.padding);
   }
 
   static llvm::hash_code hashKey(const KeyTy &key) {
     if (key.name)
       return llvm::hash_combine(key.name, key.kind);
     return llvm::hash_combine(key.members, key.incomplete, key.packed,
-                              key.padded, key.kind);
+                              key.padded, key.kind, key.padding);
   }
 
   static RecordTypeStorage *construct(mlir::TypeStorageAllocator &allocator,
                                       const KeyTy &key) {
-    return new (allocator.allocate<RecordTypeStorage>())
-        RecordTypeStorage(allocator.copyInto(key.members), key.name,
-                          key.incomplete, key.packed, key.padded, key.kind);
+    return new (allocator.allocate<RecordTypeStorage>()) RecordTypeStorage(
+        allocator.copyInto(key.members), key.name, key.incomplete, key.packed,
+        key.padded, key.kind, key.padding);
   }
 
   /// Mutates the members and attributes an identified record.
@@ -91,21 +93,22 @@ struct RecordTypeStorage : public mlir::TypeStorage {
   /// change the record.
   llvm::LogicalResult mutate(mlir::TypeStorageAllocator &allocator,
                              llvm::ArrayRef<mlir::Type> members, bool packed,
-                             bool padded) {
+                             bool padded, mlir::Type padding) {
     // Anonymous records cannot mutate.
     if (!name)
       return llvm::failure();
 
     // Mutation of complete records are allowed if they change nothing.
     if (!incomplete)
-      return mlir::success((this->members == members) &&
-                           (this->packed == packed) &&
-                           (this->padded == padded));
+      return mlir::success(
+          (this->members == members) && (this->packed == packed) &&
+          (this->padded == padded) && (this->padding == padding));
 
     // Mutate incomplete record.
     this->members = allocator.copyInto(members);
     this->packed = packed;
     this->padded = padded;
+    this->padding = padding;
 
     incomplete = false;
     return llvm::success();
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
index c48afa0c7c793..deb9e554c99a6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
@@ -194,7 +194,8 @@ cir::RecordType 
clang::CIRGen::CIRGenBuilderTy::getCompleteRecordType(
   if (name.empty())
     return getAnonRecordTy(members, packed, padded);
 
-  return getCompleteNamedRecordType(members, packed, padded, name);
+  return getCompleteNamedRecordType(members, packed, padded, mlir::Type{},
+                                    name);
 }
 
 mlir::Attribute clang::CIRGen::CIRGenBuilderTy::getConstRecordOrZeroAttr(
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h 
b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index f8d3d93e49075..3d7344540a078 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -201,24 +201,25 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
   /// it with a different set of attributes, this method will crash.
   cir::RecordType getCompleteNamedRecordType(llvm::ArrayRef<mlir::Type> 
members,
                                              bool packed, bool padded,
+                                             mlir::Type padding,
                                              llvm::StringRef name) {
     const auto nameAttr = getStringAttr(name);
     auto kind = cir::RecordType::RecordKind::Struct;
     assert(!cir::MissingFeatures::astRecordDeclAttr());
 
     // Create or get the record.
-    auto type =
-        getType<cir::RecordType>(members, nameAttr, packed, padded, kind);
+    auto type = getType<cir::RecordType>(members, nameAttr, packed, padded,
+                                         kind, padding);
 
     // 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.getPadded() == padded && type.getPadding() == padding));
 
     // Complete an incomplete record or ensure the existing complete record
     // matches the requested attributes.
-    type.complete(members, packed, padded);
+    type.complete(members, packed, padded, padding);
 
     return type;
   }
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp 
b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index 8399d6a333006..6be752a01970c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -196,10 +196,16 @@ struct CIRRecordLowering final {
   void fillOutputFields();
 
   void appendPaddingBytes(CharUnits size) {
-    if (!size.isZero()) {
-      fieldTypes.push_back(getByteArrayType(size));
-      padded = true;
+    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;
+      return;
     }
+    fieldTypes.push_back(padTy);
   }
 
   CIRGenTypes &cirGenTypes;
@@ -212,6 +218,7 @@ struct CIRRecordLowering final {
   std::vector<MemberInfo> members;
   // Output fields, consumed by CIRGenTypes::computeRecordLayout
   llvm::SmallVector<mlir::Type, 16> fieldTypes;
+  mlir::Type unionPadding;
   llvm::DenseMap<const FieldDecl *, CIRGenBitFieldInfo> bitFields;
   llvm::DenseMap<const FieldDecl *, unsigned> fieldIdxMap;
   llvm::DenseMap<const CXXRecordDecl *, unsigned> nonVirtualBases;
@@ -705,7 +712,7 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, 
cir::RecordType *ty) {
       std::string baseIdentifier = getRecordTypeName(rd, ".base");
       baseTy = builder.getCompleteNamedRecordType(
           baseLowering.fieldTypes, baseLowering.packed, baseLowering.padded,
-          baseIdentifier);
+          baseLowering.unionPadding, baseIdentifier);
       // TODO(cir): add something like addRecordTypeName
 
       // BaseTy and Ty must agree on their packedness for getCIRFieldNo to work
@@ -719,7 +726,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.fieldTypes, lowering.packed, lowering.padded);
+  ty->complete(lowering.fieldTypes, lowering.packed, lowering.padded,
+               lowering.unionPadding);
 
   // Queue ABI metadata for the module-level cir.record_layouts attribute.
   if (ty->getName()) {
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp 
b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index 9ec1c20c0be73..8effc57a352a4 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -137,6 +137,7 @@ Type RecordType::parse(mlir::AsmParser &parser) {
   const mlir::Location eLoc = parser.getEncodedSourceLoc(loc);
   bool packed = false;
   bool padded = false;
+  mlir::Type padding;
   RecordKind kind;
   mlir::MLIRContext *context = parser.getContext();
 
@@ -198,6 +199,19 @@ Type RecordType::parse(mlir::AsmParser &parser) {
       return {};
   }
 
+  if (parser.parseOptionalComma().succeeded()) {
+    if (parser.parseKeyword("padding").failed())
+      return {};
+    if (parser.parseEqual().failed())
+      return {};
+    if (parser.parseLBrace().failed())
+      return {};
+    if (parser.parseType(padding).failed())
+      return {};
+    if (parser.parseRBrace().failed())
+      return {};
+  }
+
   if (parser.parseGreater())
     return {};
 
@@ -207,13 +221,15 @@ Type RecordType::parse(mlir::AsmParser &parser) {
   if (name && incomplete) { // Identified & incomplete
     type = getChecked(eLoc, context, name, kind);
   } else if (!name && !incomplete) { // Anonymous & complete
-    type = getChecked(eLoc, context, membersRef, packed, padded, kind);
+    type = getChecked(eLoc, context, membersRef, packed, padded, kind, 
padding);
   } else if (!incomplete) { // Identified & complete
-    type = getChecked(eLoc, context, membersRef, name, packed, padded, kind);
+    type = getChecked(eLoc, context, membersRef, name, packed, padded, kind,
+                      padding);
     // If the record has a self-reference, its type already exists in a
     // incomplete state. In this case, we must complete it.
     if (mlir::cast<RecordType>(type).isIncomplete())
-      mlir::cast<RecordType>(type).complete(membersRef, packed, padded);
+      mlir::cast<RecordType>(type).complete(membersRef, packed, padded,
+                                            padding);
     assert(!cir::MissingFeatures::astRecordDeclAttr());
   } else { // anonymous & incomplete
     parser.emitError(loc, "anonymous records must be complete");
@@ -264,6 +280,11 @@ void RecordType::print(mlir::AsmPrinter &printer) const {
     printer << "{";
     llvm::interleaveComma(getMembers(), printer);
     printer << "}";
+    if (mlir::Type pad = getPadding()) {
+      printer << ", padding = {";
+      printer.printType(pad);
+      printer << '}';
+    }
   }
 
   printer << '>';
@@ -273,9 +294,15 @@ mlir::LogicalResult
 RecordType::verify(function_ref<mlir::InFlightDiagnostic()> emitError,
                    llvm::ArrayRef<mlir::Type> members, mlir::StringAttr name,
                    bool incomplete, bool packed, bool padded,
-                   RecordType::RecordKind kind) {
+                   RecordType::RecordKind kind, mlir::Type padding) {
   if (name && name.getValue().empty())
     return emitError() << "identified records cannot have an empty name";
+  if (padding) {
+    if (kind != RecordKind::Union)
+      return emitError() << "record padding is only supported on union types";
+    if (!padded)
+      return emitError() << "padded keyword required when padding is set";
+  }
   return mlir::success();
 }
 
@@ -293,6 +320,8 @@ bool RecordType::getPacked() const { return 
getImpl()->packed; }
 
 bool RecordType::getPadded() const { return getImpl()->padded; }
 
+mlir::Type RecordType::getPadding() const { return getImpl()->padding; }
+
 cir::RecordType::RecordKind RecordType::getKind() const {
   return getImpl()->kind;
 }
@@ -316,9 +345,10 @@ void RecordType::removeABIConversionNamePrefix() {
         recordName.getType());
 }
 
-void RecordType::complete(ArrayRef<Type> members, bool packed, bool padded) {
+void RecordType::complete(ArrayRef<Type> members, bool packed, bool padded,
+                          mlir::Type padding) {
   assert(!cir::MissingFeatures::astRecordDeclAttr());
-  if (mutate(members, packed, padded).failed())
+  if (mutate(members, packed, padded, padding).failed())
     llvm_unreachable("failed to complete record");
 }
 
@@ -330,19 +360,14 @@ Type RecordType::getLargestMember(const 
::mlir::DataLayout &dataLayout) const {
   llvm::ArrayRef<Type> members = getMembers();
   if (members.empty())
     return {};
-
-  // If the union is padded, we need to ignore the last member,
-  // which is the padding.
-  auto endIt = getPadded() ? std::prev(members.end()) : members.end();
-  if (endIt == members.begin())
-    return {};
-  return *std::max_element(members.begin(), endIt, [&](Type lhs, Type rhs) {
-    return dataLayout.getTypeABIAlignment(lhs) <
-               dataLayout.getTypeABIAlignment(rhs) ||
-           (dataLayout.getTypeABIAlignment(lhs) ==
-                dataLayout.getTypeABIAlignment(rhs) &&
-            dataLayout.getTypeSize(lhs) < dataLayout.getTypeSize(rhs));
-  });
+  return *std::max_element(
+      members.begin(), members.end(), [&](Type lhs, Type rhs) {
+        return dataLayout.getTypeABIAlignment(lhs) <
+                   dataLayout.getTypeABIAlignment(rhs) ||
+               (dataLayout.getTypeABIAlignment(lhs) ==
+                    dataLayout.getTypeABIAlignment(rhs) &&
+                dataLayout.getTypeSize(lhs) < dataLayout.getTypeSize(rhs));
+      });
 }
 
 bool RecordType::isLayoutIdentical(const RecordType &other) {
@@ -396,8 +421,8 @@ RecordType::getTypeSizeInBits(const mlir::DataLayout 
&dataLayout,
     // by `insertPadding`, making `sizeof(parent)` and array GEPs off by the
     // missing bytes.
     llvm::TypeSize size = dataLayout.getTypeSizeInBits(largest);
-    if (getPadded())
-      size += dataLayout.getTypeSizeInBits(*getMembers().rbegin());
+    if (mlir::Type tailPad = getPadding())
+      size += dataLayout.getTypeSizeInBits(tailPad);
     return size;
   }
 
@@ -457,8 +482,9 @@ RecordType::computeStructDataSize(const mlir::DataLayout 
&dataLayout) const {
   // 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();
+  unsigned numMembers = members.size();
+  if (getPadded() && !getPadding() && members.size() > 1)
+    numMembers = members.size() - 1;
   unsigned recordSize = 0;
   for (unsigned i = 0; i < numMembers; ++i) {
     mlir::Type ty = members[i];
diff --git a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp 
b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
index 5fee2301c775b..a099c66f455f5 100644
--- a/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp
@@ -837,10 +837,14 @@ class CIRABITypeConverter : public mlir::TypeConverter {
     // Unnamed record types can't be referred to recursively, so we can just
     // convert this one. It also doesn't have uniqueness problems, so we can
     // just do a conversion on it.
-    if (!type.getName())
+    if (!type.getName()) {
+      mlir::Type loweredPadding;
+      if (type.getPadding())
+        loweredPadding = convertType(type.getPadding());
       return cir::RecordType::get(
           type.getContext(), convertRecordMemberTypes(type), type.getPacked(),
-          type.getPadded(), type.getKind());
+          type.getPadded(), type.getKind(), loweredPadding);
+    }
 
     assert(!type.isIncomplete() || type.getMembers().empty());
 
@@ -873,8 +877,11 @@ class CIRABITypeConverter : public mlir::TypeConverter {
 
     SmallVector<mlir::Type> convertedMembers = convertRecordMemberTypes(type);
 
-    convertedType.complete(convertedMembers, type.getPacked(),
-                           type.getPadded());
+    mlir::Type loweredPadding;
+    if (type.getPadding())
+      loweredPadding = convertType(type.getPadding());
+    convertedType.complete(convertedMembers, type.getPacked(), 
type.getPadded(),
+                           loweredPadding);
     addConvertedRecordType(convertedType);
     return convertedType;
   }
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp 
b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 2720dd2500a94..f9b8b698788e2 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3313,16 +3313,12 @@ static void 
prepareTypeConverter(mlir::LLVMTypeConverter &converter,
       break;
     // Unions are lowered as only the largest member.
     case cir::RecordType::Union:
-      if (type.getMembers().empty())
-        break;
       if (auto largestMember = type.getLargestMember(dataLayout))
         llvmMembers.push_back(
             convertTypeForMemory(converter, dataLayout, largestMember));
-      if (type.getPadded()) {
-        auto last = *type.getMembers().rbegin();
+      if (mlir::Type tailPad = type.getPadding())
         llvmMembers.push_back(
-            convertTypeForMemory(converter, dataLayout, last));
-      }
+            convertTypeForMemory(converter, dataLayout, tailPad));
       break;
     }
 
diff --git a/clang/test/CIR/CodeGen/empty-union.cpp 
b/clang/test/CIR/CodeGen/empty-union.cpp
index e1d7e793cac39..fa8ff511d0f43 100644
--- a/clang/test/CIR/CodeGen/empty-union.cpp
+++ b/clang/test/CIR/CodeGen/empty-union.cpp
@@ -4,13 +4,13 @@
 
 // Empty union (should be padded to size 1)
 union Empty {};
-// CIR: !rec_Empty = !cir.record<union "Empty" padded {!u8i}>
+// CIR: !rec_Empty = !cir.record<union "Empty" padded {}, padding = {!u8i}>
 // LLVM: %union.Empty = type { i8 }
 // OGCG: %union.Empty = type { i8 }
 
 // Aligned empty union (should have aligned integer member in CIR)
 union alignas(16) EmptyAligned {};
-// CIR: !rec_EmptyAligned = !cir.record<union "EmptyAligned" padded 
{!cir.array<!u8i x 16>}>
+// CIR: !rec_EmptyAligned = !cir.record<union "EmptyAligned" padded {}, 
padding = {!cir.array<!u8i x 16>}>
 // LLVM: %union.EmptyAligned = type { [16 x i8] }
 // OGCG: %union.EmptyAligned = type { [16 x i8] }
 
diff --git a/clang/test/CIR/CodeGen/record-with-padded-union.cpp 
b/clang/test/CIR/CodeGen/record-with-padded-union.cpp
index 6711bb9d599c2..be1cffd29a4a0 100644
--- a/clang/test/CIR/CodeGen/record-with-padded-union.cpp
+++ b/clang/test/CIR/CodeGen/record-with-padded-union.cpp
@@ -15,7 +15,7 @@ struct SSO {
 };
 
 // Inner union's tail padding must not bleed into the outer record.
-// CIR: !rec_anon{{.*}} = !cir.record<union "anon{{.*}}" padded 
{!cir.array<!s8i x 16>, !u64i, !cir.array<!u8i x 8>}>
+// CIR: !rec_anon{{.*}} = !cir.record<union "anon{{.*}}" padded 
{!cir.array<!s8i x 16>, !u64i}, padding = {!cir.array<!u8i x 8>}>
 // CIR: !rec_SSO = !cir.record<struct "SSO" {!cir.ptr<!s8i>, !u64i, 
!rec_anon{{.*}}}>
 
 // LLVM: %struct.SSO = type { ptr, i64, %union.anon{{.*}} }
diff --git a/clang/test/CIR/CodeGen/union.c b/clang/test/CIR/CodeGen/union.c
index 5403982654067..c03497cbf839b 100644
--- a/clang/test/CIR/CodeGen/union.c
+++ b/clang/test/CIR/CodeGen/union.c
@@ -31,7 +31,7 @@ union U3 {
   int i;
 } __attribute__((packed));
 
-// CIR:  !rec_U3 = !cir.record<union "U3" packed padded {!cir.array<!s8i x 5>, 
!s32i, !u8i}>
+// CIR:  !rec_U3 = !cir.record<union "U3" packed padded {!cir.array<!s8i x 5>, 
!s32i}, padding = {!u8i}>
 // LLVM: %union.U3 = type <{ i32, i8 }>
 // OGCG: %union.U3 = type <{ i32, i8 }>
 
@@ -40,7 +40,7 @@ union U4 {
   int i;
 };
 
-// CIR:  !rec_U4 = !cir.record<union "U4" padded {!cir.array<!s8i x 5>, !s32i, 
!cir.array<!u8i x 4>}>
+// CIR:  !rec_U4 = !cir.record<union "U4" padded {!cir.array<!s8i x 5>, 
!s32i}, padding = {!cir.array<!u8i x 4>}>
 // LLVM: %union.U4 = type { i32, [4 x i8] }
 // OGCG: %union.U4 = type { i32, [4 x i8] }
 
diff --git a/clang/unittests/CIR/UnionTypeSizeTest.cpp 
b/clang/unittests/CIR/UnionTypeSizeTest.cpp
index a5b3f42cc0e99..8221feb8f8f0f 100644
--- a/clang/unittests/CIR/UnionTypeSizeTest.cpp
+++ b/clang/unittests/CIR/UnionTypeSizeTest.cpp
@@ -63,6 +63,23 @@ TEST_F(UnionTypeSizeTest, MultiMemberUnion) {
   module->erase();
 }
 
+TEST_F(UnionTypeSizeTest, PaddedUnionWithSeparatePadding) {
+  IntType i64 = IntType::get(&context, 64, false);
+  ArrayType pad = ArrayType::get(IntType::get(&context, 8, false), 8);
+  auto ty = RecordType::get(&context, getName("PaddedU"), RecordType::Union);
+  ty.complete({i64}, /*packed=*/false, /*padded=*/true, pad);
+
+  OpBuilder builder(&context);
+  auto loc = builder.getUnknownLoc();
+  auto module = ModuleOp::create(loc);
+  mlir::DataLayout dl(module);
+
+  llvm::TypeSize size = dl.getTypeSizeInBits(ty);
+  EXPECT_EQ(size.getFixedValue(), 128u);
+
+  module->erase();
+}
+
 TEST_F(UnionTypeSizeTest, EmptyUnion) {
   auto ty = RecordType::get(&context, getName("Empty"), RecordType::Union);
   ty.complete({}, /*packed=*/false, /*padded=*/false);

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

Reply via email to