[Mlir-commits] [mlir] [mlir][llvm] Fix SROA crash on empty LLVM struct types (PR #184596)
Mehdi Amini
llvmlistbot at llvm.org
Wed Mar 4 03:55:40 PST 2026
https://github.com/joker-eph created https://github.com/llvm/llvm-project/pull/184596
When SROA runs on an alloca of an empty struct type (llvm.struct<()>), it crashes with:
Assertion `\!subelementIndexMap->empty()' failed.
The root cause is in LLVMStructType::getSubelementIndexMap(): for an empty struct (no body fields), the loop doesn't execute and an empty DenseMap is returned as a non-null optional. Later, getTypeAtIndex() asserts the map is non-empty, triggering the crash.
Fix this by returning std::nullopt for empty structs, indicating they cannot be destructured. This is consistent with how LLVMArrayType handles the zero-element case.
Fixes #108366
>From fcb423cbdb357b169dd58079b20ccc2c53881ce5 Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Mon, 2 Mar 2026 07:56:38 -0800
Subject: [PATCH] [mlir][llvm] Fix SROA crash on empty LLVM struct types
When SROA runs on an alloca of an empty struct type (llvm.struct<()>),
it crashes with:
Assertion `\!subelementIndexMap->empty()' failed.
The root cause is in LLVMStructType::getSubelementIndexMap(): for an
empty struct (no body fields), the loop doesn't execute and an empty
DenseMap is returned as a non-null optional. Later, getTypeAtIndex()
asserts the map is non-empty, triggering the crash.
Fix this by returning std::nullopt for empty structs, indicating they
cannot be destructured. This is consistent with how LLVMArrayType
handles the zero-element case.
Fixes #108366
---
mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp | 3 +++
mlir/test/Dialect/LLVMIR/sroa.mlir | 15 +++++++++++++++
2 files changed, 18 insertions(+)
diff --git a/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp b/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
index 7d9058c262562..8f3c0dac026a7 100644
--- a/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
+++ b/mlir/lib/Dialect/LLVMIR/IR/LLVMMemorySlot.cpp
@@ -1585,6 +1585,9 @@ DeletionKind LLVM::MemmoveOp::rewire(const DestructurableMemorySlot &slot,
std::optional<DenseMap<Attribute, Type>>
LLVM::LLVMStructType::getSubelementIndexMap() const {
+ // Empty structs have no sub-elements and cannot be destructured.
+ if (getBody().empty())
+ return std::nullopt;
Type i32 = IntegerType::get(getContext(), 32);
DenseMap<Attribute, Type> destructured;
for (const auto &[index, elemType] : llvm::enumerate(getBody()))
diff --git a/mlir/test/Dialect/LLVMIR/sroa.mlir b/mlir/test/Dialect/LLVMIR/sroa.mlir
index 1674bbd8c796f..b019e66d5a430 100644
--- a/mlir/test/Dialect/LLVMIR/sroa.mlir
+++ b/mlir/test/Dialect/LLVMIR/sroa.mlir
@@ -448,3 +448,18 @@ llvm.func @out_of_bound_gep_array_access(%arg: i32) {
llvm.store %arg, %2 : i32, !llvm.ptr
llvm.return
}
+
+// -----
+
+// Regression test: SROA must not crash when processing an alloca of an empty
+// struct type. Empty structs have no sub-elements and cannot be destructured,
+// so the alloca must be left unchanged.
+// https://github.com/llvm/llvm-project/issues/108366
+// CHECK-LABEL: llvm.func @empty_struct_not_destructured
+llvm.func @empty_struct_not_destructured() -> !llvm.struct<()> {
+ %0 = llvm.mlir.constant(1 : i32) : i32
+ // CHECK: llvm.alloca %{{.*}} x !llvm.struct<()>
+ %1 = llvm.alloca %0 x !llvm.struct<()> : (i32) -> !llvm.ptr
+ %2 = llvm.load %1 : !llvm.ptr -> !llvm.struct<()>
+ llvm.return %2 : !llvm.struct<()>
+}
More information about the Mlir-commits
mailing list