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