Skip to content

Conversation

@Men-cotton
Copy link
Contributor

Handles empty unions (cir.record<union {}>) by generating empty LLVM structs. This ensures successful lowering without triggering crashes in getLargestMember due to missing members.

Added regression test in clang/test/CIR/Lowering/union.cir.

@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Dec 17, 2025
@llvmbot
Copy link
Member

llvmbot commented Dec 17, 2025

@llvm/pr-subscribers-clangir

@llvm/pr-subscribers-clang

Author: Akimasa Watanuki (Men-cotton)

Changes

Handles empty unions (cir.record&lt;union {}&gt;) by generating empty LLVM structs. This ensures successful lowering without triggering crashes in getLargestMember due to missing members.

Added regression test in clang/test/CIR/Lowering/union.cir.


Full diff: https://github.com/llvm/llvm-project/pull/172666.diff

2 Files Affected:

  • (modified) clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp (+2)
  • (added) clang/test/CIR/Lowering/union.cir (+10)
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 7d854997848aa..06cce00a9aca4 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -2969,6 +2969,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/Lowering/union.cir b/clang/test/CIR/Lowering/union.cir
new file mode 100644
index 0000000000000..c7bb8efccadcd
--- /dev/null
+++ b/clang/test/CIR/Lowering/union.cir
@@ -0,0 +1,10 @@
+// RUN: cir-opt %s --cir-to-llvm | FileCheck %s
+
+!u_empty = !cir.record<union {}>
+
+module {
+  cir.func @empty_union(%u: !u_empty) {
+    // CHECK-LABEL:   llvm.func @empty_union
+    cir.return
+  }
+}

@@ -0,0 +1,10 @@
// RUN: cir-opt %s --cir-to-llvm | FileCheck %s

!u_empty = !cir.record<union {}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a check for the LLVM IR type here? I think we're probably generating the wrong thing. In the incubator, we generate this:

%union.U = type {}

Whereas classic codegen generates this:

%union.U = type { [4 x i8] }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me amend what I said above. I didn't realize the classic codegen I was referring to was using the Windows C ABI. Classic codegen does generate %union.U = type {} for C and %union.U = type { i8 } for C++ (which the incubator also does).

So, I guess my question is whether we identify these padded forms as "empty".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants