From deb5c9f43fe315de5442ecbe93ac4d5e808a9271 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Sun, 22 Jun 2025 22:07:32 +0800 Subject: [PATCH 1/4] [CIR] support union without field in C++ In [libstdc++ std::variant implementation](https://github.com/gcc-mirror/gcc/blob/b0419798447ae25de2f58d1a695db6dadb5d8547/libstdc%2B%2B-v3/include/std/variant#L387-L394), union without any fields is used. According to current CodeGen logic, append 1 byte padding for this kind of union. Handle this union in `mlir::RecordType` also --- .../CIR/CodeGen/CIRRecordLayoutBuilder.cpp | 2 +- clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 9 ++++--- clang/test/CIR/CodeGen/union-empty.cpp | 25 +++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 clang/test/CIR/CodeGen/union-empty.cpp diff --git a/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp index 2d3bdb522171..c4edbee33dea 100644 --- a/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp @@ -364,7 +364,7 @@ void CIRRecordLowering::lowerUnion() { } // If we have no storage type just pad to the appropriate size and return. if (!StorageType) - llvm_unreachable("no-storage union NYI"); + return appendPaddingBytes(LayoutSize); // If our storage size was bigger than our required size (can happen in the // case of packed bitfields on Itanium) then just use an I8 array. if (LayoutSize < getSize(StorageType)) diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index e34232af2d83..b6dca4cb6da4 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -453,8 +453,9 @@ RecordType::computeUnionSize(const mlir::DataLayout &dataLayout) const { unsigned recordSize = 0; llvm::Align recordAlignment{1}; - auto largestMember = getLargestMember(dataLayout); - recordSize = dataLayout.getTypeSize(largestMember); + Type largestMember = getLargestMember(dataLayout); + if (largestMember) + recordSize = dataLayout.getTypeSize(largestMember); // If the union is padded, add the padding to the size. if (getPadded()) { @@ -517,7 +518,9 @@ RecordType::computeStructAlignment(const mlir::DataLayout &dataLayout) const { uint64_t RecordType::computeUnionAlignment(const mlir::DataLayout &dataLayout) const { - auto largestMember = getLargestMember(dataLayout); + Type largestMember = getLargestMember(dataLayout); + if (!largestMember) + return 1; return dataLayout.getTypeABIAlignment(largestMember); } diff --git a/clang/test/CIR/CodeGen/union-empty.cpp b/clang/test/CIR/CodeGen/union-empty.cpp new file mode 100644 index 000000000000..4bc465d86d10 --- /dev/null +++ b/clang/test/CIR/CodeGen/union-empty.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=LLVM + +union EmptyUnion { + EmptyUnion() = default; +}; + +void f0() { + EmptyUnion e{}; +}; + +// CIR: !rec_EmptyUnion = !cir.record +// CIR: cir.func dso_local @_Z2f0v() +// CIR: %0 = cir.alloca !rec_EmptyUnion, !cir.ptr, ["e"] {alignment = 1 : i64} +// CIR: %1 = cir.const #cir.undef : !rec_EmptyUnion +// CIR: cir.store align(1) %1, %0 : !rec_EmptyUnion, !cir.ptr +// CIR: cir.return + +// LLVM: %union.EmptyUnion = type { i8 } +// LLVM: define dso_local void @_Z2f0v() +// LLVM: %1 = alloca %union.EmptyUnion, i64 1, align 1 +// LLVM: store %union.EmptyUnion undef, ptr %1, align 1 +// LLVM: ret void From 3c7b7a3a99a22c22856191e1aebb7724192494d0 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Tue, 24 Jun 2025 23:02:09 +0800 Subject: [PATCH 2/4] add comment --- clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index b6dca4cb6da4..614c66835c2a 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -520,6 +520,7 @@ uint64_t RecordType::computeUnionAlignment(const mlir::DataLayout &dataLayout) const { Type largestMember = getLargestMember(dataLayout); if (!largestMember) + // use 1 byte alignment for empty union return 1; return dataLayout.getTypeABIAlignment(largestMember); } From f1c39aef46140ef7c7f688d546da4681adfcc75e Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Tue, 24 Jun 2025 23:28:02 +0800 Subject: [PATCH 3/4] remove undef in test --- clang/test/CIR/CodeGen/union-empty.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/clang/test/CIR/CodeGen/union-empty.cpp b/clang/test/CIR/CodeGen/union-empty.cpp index 4bc465d86d10..9fab3134e190 100644 --- a/clang/test/CIR/CodeGen/union-empty.cpp +++ b/clang/test/CIR/CodeGen/union-empty.cpp @@ -8,18 +8,15 @@ union EmptyUnion { }; void f0() { - EmptyUnion e{}; + EmptyUnion e; }; // CIR: !rec_EmptyUnion = !cir.record // CIR: cir.func dso_local @_Z2f0v() // CIR: %0 = cir.alloca !rec_EmptyUnion, !cir.ptr, ["e"] {alignment = 1 : i64} -// CIR: %1 = cir.const #cir.undef : !rec_EmptyUnion -// CIR: cir.store align(1) %1, %0 : !rec_EmptyUnion, !cir.ptr // CIR: cir.return // LLVM: %union.EmptyUnion = type { i8 } // LLVM: define dso_local void @_Z2f0v() // LLVM: %1 = alloca %union.EmptyUnion, i64 1, align 1 -// LLVM: store %union.EmptyUnion undef, ptr %1, align 1 // LLVM: ret void From a03356604666605584f7632652f8f54326527f02 Mon Sep 17 00:00:00 2001 From: Congcong Cai Date: Wed, 25 Jun 2025 11:00:14 +0800 Subject: [PATCH 4/4] fix comment --- clang/lib/CIR/Dialect/IR/CIRTypes.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp index 614c66835c2a..a1fa1f7067b0 100644 --- a/clang/lib/CIR/Dialect/IR/CIRTypes.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRTypes.cpp @@ -519,8 +519,8 @@ RecordType::computeStructAlignment(const mlir::DataLayout &dataLayout) const { uint64_t RecordType::computeUnionAlignment(const mlir::DataLayout &dataLayout) const { Type largestMember = getLargestMember(dataLayout); + // use 1 byte alignment for empty union if (!largestMember) - // use 1 byte alignment for empty union return 1; return dataLayout.getTypeABIAlignment(largestMember); }