[clang] e927f4b - [CIR] Handle empty unions in record lowering and LLVM conversion (#172666)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jan 9 09:52:34 PST 2026
Author: Akimasa Watanuki
Date: 2026-01-09T09:52:30-08:00
New Revision: e927f4bbf4800fe9b68fc32b935799d5136925fb
URL: https://github.com/llvm/llvm-project/commit/e927f4bbf4800fe9b68fc32b935799d5136925fb
DIFF: https://github.com/llvm/llvm-project/commit/e927f4bbf4800fe9b68fc32b935799d5136925fb.diff
LOG: [CIR] Handle empty unions in record lowering and LLVM conversion (#172666)
Handle empty unions in CIR record lowering and LLVM conversion by
emitting padding when needed, guarding `getLargestMember` for
empty/padded unions, and lowering to empty or padded LLVM structs based
on language rules.
Added regression tests for C and C++ empty union lowering in
`clang/test/CIR/CodeGen/empty-union.c` and `empty-union.cpp`.
Added:
clang/test/CIR/CodeGen/empty-union.c
clang/test/CIR/CodeGen/empty-union.cpp
Modified:
clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
clang/lib/CIR/Dialect/IR/CIRTypes.cpp
clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Removed:
################################################################################
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index 87f23409e8e4b..29eda175c208c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -811,9 +811,10 @@ void CIRRecordLowering::lowerUnion() {
fieldTypes.push_back(fieldType);
}
- if (!storageType)
- cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(),
- "No-storage Union NYI");
+ if (!storageType) {
+ appendPaddingBytes(layoutSize);
+ return;
+ }
if (layoutSize < getSize(storageType))
storageType = getByteArrayType(layoutSize);
diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
index f72eae94e2e5d..43853b6696a94 100644
--- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp
@@ -297,11 +297,16 @@ void RecordType::complete(ArrayRef<Type> members, bool packed, bool padded) {
Type RecordType::getLargestMember(const ::mlir::DataLayout &dataLayout) const {
assert(isUnion() && "Only call getLargestMember on unions");
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(), getPadded() ? members.end() - 1 : members.end(),
- [&](Type lhs, Type rhs) {
+ members.begin(), endIt, [&](Type lhs, Type rhs) {
return dataLayout.getTypeABIAlignment(lhs) <
dataLayout.getTypeABIAlignment(rhs) ||
(dataLayout.getTypeABIAlignment(lhs) ==
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 914233d8f6b8f..d0fa8bae545be 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -3010,6 +3010,8 @@ 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));
diff --git a/clang/test/CIR/CodeGen/empty-union.c b/clang/test/CIR/CodeGen/empty-union.c
new file mode 100644
index 0000000000000..87cc012706160
--- /dev/null
+++ b/clang/test/CIR/CodeGen/empty-union.c
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=OGCG
+
+// Empty union (no padding, size 0 in CIR)
+union Empty {};
+// CIR: !rec_Empty = !cir.record<union "Empty" {}>
+// LLVM: %union.Empty = type {}
+// OGCG: %union.Empty = type {}
+
+// Aligned empty union (size 0, alignment 16)
+union EmptyAligned {} __attribute__((aligned(16)));
+// CIR: !rec_EmptyAligned = !cir.record<union "EmptyAligned" {}>
+// LLVM: %union.EmptyAligned = type {}
+// OGCG: %union.EmptyAligned = type {}
+
+void useEmpty() {
+ union Empty e;
+}
+// CIR: cir.func {{.*}}@useEmpty()
+// CIR: cir.alloca !rec_Empty, !cir.ptr<!rec_Empty>, ["e"] {alignment = 1 : i64}
+// LLVM: define {{.*}} void @useEmpty()
+// LLVM: alloca %union.Empty, i64 1, align 1
+// OGCG: define {{.*}} void @useEmpty()
+// OGCG: alloca %union.Empty, align 1
+
+void useEmptyAligned() {
+ union EmptyAligned e;
+}
+// CIR: cir.func {{.*}}@useEmptyAligned()
+// CIR: cir.alloca !rec_EmptyAligned, !cir.ptr<!rec_EmptyAligned>, ["e"] {alignment = 16 : i64}
+// LLVM: define {{.*}} void @useEmptyAligned()
+// LLVM: alloca %union.EmptyAligned, i64 1, align 16
+// OGCG: define {{.*}} void @useEmptyAligned()
+// OGCG: alloca %union.EmptyAligned, align 16
diff --git a/clang/test/CIR/CodeGen/empty-union.cpp b/clang/test/CIR/CodeGen/empty-union.cpp
new file mode 100644
index 0000000000000..e1d7e793cac39
--- /dev/null
+++ b/clang/test/CIR/CodeGen/empty-union.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=OGCG
+
+// Empty union (should be padded to size 1)
+union Empty {};
+// CIR: !rec_Empty = !cir.record<union "Empty" padded {!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>}>
+// LLVM: %union.EmptyAligned = type { [16 x i8] }
+// OGCG: %union.EmptyAligned = type { [16 x i8] }
+
+void useEmpty() {
+ Empty e;
+}
+// CIR: cir.func {{.*}}@_Z8useEmptyv()
+// CIR: cir.alloca !rec_Empty, !cir.ptr<!rec_Empty>, ["e"] {alignment = 1 : i64}
+// LLVM: define {{.*}} void @_Z8useEmptyv()
+// LLVM: alloca %union.Empty, i64 1, align 1
+// OGCG: define {{.*}} void @_Z8useEmptyv()
+// OGCG: alloca %union.Empty, align 1
+
+void useEmptyAligned() {
+ EmptyAligned e;
+}
+// CIR: cir.func {{.*}}@_Z15useEmptyAlignedv()
+// CIR: cir.alloca !rec_EmptyAligned, !cir.ptr<!rec_EmptyAligned>, ["e"] {alignment = 16 : i64}
+// LLVM: define {{.*}} void @_Z15useEmptyAlignedv()
+// LLVM: alloca %union.EmptyAligned, i64 1, align 16
+// OGCG: define {{.*}} void @_Z15useEmptyAlignedv()
+// OGCG: alloca %union.EmptyAligned, align 16
More information about the cfe-commits
mailing list