[Mlir-commits] [mlir] [mlir][memref] Fix mem2reg crash on zero-extent alloca (PR #216851)
Alessandro Potenza
llvmlistbot at llvm.org
Tue Aug 18 10:12:08 PDT 2026
https://github.com/alepot55 updated https://github.com/llvm/llvm-project/pull/216851
>From 3782460f038941fa618221cf84889f22c749747b Mon Sep 17 00:00:00 2001
From: Alessandro Potenza <ap.alessandro.potenza at gmail.com>
Date: Mon, 17 Aug 2026 22:56:39 +0200
Subject: [PATCH 1/5] [mlir][memref] Fix mem2reg crash on zero-extent alloca
`memref` allows an extent of 0, but vector types must have strictly
positive extents. When `memref::AllocaOp::getPromotableSlots` considers
whole-buffer promotion it builds a `VectorType` from the memref shape,
so a legal zero-extent alloca such as
func.func @z() { %a = memref.alloca() : memref<0xf32> return }
crashes `mlir-opt --mem2reg` inside `VectorType::get`. The alloca needs
no uses at all: `getPromotableSlots` runs before the `use_empty` filter
in `tryToPromoteMemorySlots`.
Bail out of whole-buffer promotion when the shape contains a zero
extent. Such a memref holds no elements, so there is nothing to promote.
Apply the same reasoning to the scalable path, where the extent comes
from a `vector.vscale * C` operand rather than the shape and `C` is only
known to be a constant: require it to be strictly positive.
The single-element scalar path is unaffected: a shape containing a zero
extent can never have exactly one element.
---
.../Dialect/MemRef/IR/MemRefMemorySlot.cpp | 10 +++++++--
mlir/test/Dialect/MemRef/mem2reg.mlir | 22 +++++++++++++++++++
2 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp b/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp
index a832b0c47809b..7a2bc0bbee0b8 100644
--- a/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp
+++ b/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp
@@ -103,15 +103,21 @@ SmallVector<MemorySlot> memref::AllocaOp::getPromotableSlots() {
// is only ever accessed as a whole buffer (e.g. through whole-buffer
// `vector.transfer_read`/`vector.transfer_write`).
if (VectorType::isValidElementType(type.getElementType())) {
+ // Vector types require strictly positive extents, so a memref with a zero
+ // extent (which holds no elements) has nothing to promote.
+ if (llvm::is_contained(type.getShape(), 0))
+ return {};
+
// Static shape: a fixed-size vector of the same extents.
if (type.hasStaticShape())
return {MemorySlot{getResult(), VectorType::get(type.getShape(),
type.getElementType())}};
// A 1-D memref whose single dynamic extent is `vector.vscale * C` maps to a
- // scalable `vector<[C]x...>` slot.
+ // scalable `vector<[C]x...>` slot, for a strictly positive `C`.
if (type.getRank() == 1 && type.isDynamicDim(0)) {
- if (std::optional<int64_t> c = matchVScaleMultiple(getDynamicSizes()[0]))
+ if (std::optional<int64_t> c = matchVScaleMultiple(getDynamicSizes()[0]);
+ c && *c > 0)
return {
MemorySlot{getResult(), VectorType::get({*c}, type.getElementType(),
/*scalableDims=*/{true})}};
diff --git a/mlir/test/Dialect/MemRef/mem2reg.mlir b/mlir/test/Dialect/MemRef/mem2reg.mlir
index 8f937c4efe75e..add6bc2e457f0 100644
--- a/mlir/test/Dialect/MemRef/mem2reg.mlir
+++ b/mlir/test/Dialect/MemRef/mem2reg.mlir
@@ -309,3 +309,25 @@ func.func @two_consecutive_merge_points(%cond1: i1, %cond2: i1) -> i32 {
// CHECK: return %[[RESULT]] : i32
return %result : i32
}
+
+// -----
+
+// A memref with a zero extent holds no elements and cannot be promoted: the
+// vector type mem2reg would build for whole-buffer promotion requires strictly
+// positive extents.
+
+// CHECK-LABEL: func.func @zero_extent_alloca
+func.func @zero_extent_alloca() {
+ // CHECK: memref.alloca() : memref<0xf32>
+ %alloca = memref.alloca() : memref<0xf32>
+ return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @zero_extent_alloca_multi_dim
+func.func @zero_extent_alloca_multi_dim() {
+ // CHECK: memref.alloca() : memref<2x0x3xf32>
+ %alloca = memref.alloca() : memref<2x0x3xf32>
+ return
+}
>From 5555bc092e8064ac2429339f488fcdf81896569f Mon Sep 17 00:00:00 2001
From: Alessandro Potenza <ap.alessandro.potenza at gmail.com>
Date: Tue, 18 Aug 2026 01:35:49 +0200
Subject: [PATCH 2/5] Add a test for the scalable path
vscale * 0 is a constant zero extent, so the scalable VectorType cannot be
built either. Unpatched mlir-opt aborts on this input.
---
mlir/test/Dialect/MemRef/mem2reg.mlir | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/mlir/test/Dialect/MemRef/mem2reg.mlir b/mlir/test/Dialect/MemRef/mem2reg.mlir
index add6bc2e457f0..76b8a5fcf6a32 100644
--- a/mlir/test/Dialect/MemRef/mem2reg.mlir
+++ b/mlir/test/Dialect/MemRef/mem2reg.mlir
@@ -331,3 +331,18 @@ func.func @zero_extent_alloca_multi_dim() {
%alloca = memref.alloca() : memref<2x0x3xf32>
return
}
+
+// -----
+
+// Same for the scalable path: the extent is `vector.vscale * 0`, a constant
+// zero, so the scalable vector type cannot be built either.
+
+// CHECK-LABEL: func.func @scalable_zero_extent_alloca
+func.func @scalable_zero_extent_alloca() {
+ %vscale = vector.vscale
+ %c0 = arith.constant 0 : index
+ %size = arith.muli %vscale, %c0 : index
+ // CHECK: memref.alloca(%{{.*}}) : memref<?xf32>
+ %alloca = memref.alloca(%size) : memref<?xf32>
+ return
+}
>From cc5b8309240ac5048727d58f3e6a7356d0abf58c Mon Sep 17 00:00:00 2001
From: Alessandro Potenza <ap.alessandro.potenza at gmail.com>
Date: Tue, 18 Aug 2026 01:43:51 +0200
Subject: [PATCH 3/5] Address review: shorten the comments, test the scalable
path
---
mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp | 2 +-
mlir/test/Dialect/MemRef/mem2reg.mlir | 4 +---
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp b/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp
index 7a2bc0bbee0b8..84b01eed4bc3a 100644
--- a/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp
+++ b/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp
@@ -104,7 +104,7 @@ SmallVector<MemorySlot> memref::AllocaOp::getPromotableSlots() {
// `vector.transfer_read`/`vector.transfer_write`).
if (VectorType::isValidElementType(type.getElementType())) {
// Vector types require strictly positive extents, so a memref with a zero
- // extent (which holds no elements) has nothing to promote.
+ // extent has nothing to promote.
if (llvm::is_contained(type.getShape(), 0))
return {};
diff --git a/mlir/test/Dialect/MemRef/mem2reg.mlir b/mlir/test/Dialect/MemRef/mem2reg.mlir
index 76b8a5fcf6a32..e2e204c807b20 100644
--- a/mlir/test/Dialect/MemRef/mem2reg.mlir
+++ b/mlir/test/Dialect/MemRef/mem2reg.mlir
@@ -312,9 +312,7 @@ func.func @two_consecutive_merge_points(%cond1: i1, %cond2: i1) -> i32 {
// -----
-// A memref with a zero extent holds no elements and cannot be promoted: the
-// vector type mem2reg would build for whole-buffer promotion requires strictly
-// positive extents.
+// A memref with a zero extent holds no elements and cannot be promoted.
// CHECK-LABEL: func.func @zero_extent_alloca
func.func @zero_extent_alloca() {
>From 25bc8513861d6fba0bdc43c0d40e35b403c9b0d1 Mon Sep 17 00:00:00 2001
From: Alessandro Potenza <ap.alessandro.potenza at gmail.com>
Date: Tue, 18 Aug 2026 02:11:59 +0200
Subject: [PATCH 4/5] Address review: shorten the scalable-path test comment
---
mlir/test/Dialect/MemRef/mem2reg.mlir | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/mlir/test/Dialect/MemRef/mem2reg.mlir b/mlir/test/Dialect/MemRef/mem2reg.mlir
index e2e204c807b20..ba26b5f984a27 100644
--- a/mlir/test/Dialect/MemRef/mem2reg.mlir
+++ b/mlir/test/Dialect/MemRef/mem2reg.mlir
@@ -332,8 +332,7 @@ func.func @zero_extent_alloca_multi_dim() {
// -----
-// Same for the scalable path: the extent is `vector.vscale * 0`, a constant
-// zero, so the scalable vector type cannot be built either.
+// A memref with a `vector.vscale * 0` extent holds no elements and cannot be promoted.
// CHECK-LABEL: func.func @scalable_zero_extent_alloca
func.func @scalable_zero_extent_alloca() {
>From ab541fe1e711288e8b6176b6e7526d91cfd60210 Mon Sep 17 00:00:00 2001
From: Alessandro Potenza <ap.alessandro.potenza at gmail.com>
Date: Tue, 18 Aug 2026 19:11:53 +0200
Subject: [PATCH 5/5] Address review: rename the vscale multiple to 'multiple'
---
mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp b/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp
index 84b01eed4bc3a..cfcca90b2b969 100644
--- a/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp
+++ b/mlir/lib/Dialect/MemRef/IR/MemRefMemorySlot.cpp
@@ -113,14 +113,15 @@ SmallVector<MemorySlot> memref::AllocaOp::getPromotableSlots() {
return {MemorySlot{getResult(), VectorType::get(type.getShape(),
type.getElementType())}};
- // A 1-D memref whose single dynamic extent is `vector.vscale * C` maps to a
- // scalable `vector<[C]x...>` slot, for a strictly positive `C`.
+ // A 1-D memref whose single dynamic extent is `vector.vscale * N` maps to a
+ // scalable `vector<[N]x...>` slot, for a strictly positive multiple `N`.
if (type.getRank() == 1 && type.isDynamicDim(0)) {
- if (std::optional<int64_t> c = matchVScaleMultiple(getDynamicSizes()[0]);
- c && *c > 0)
- return {
- MemorySlot{getResult(), VectorType::get({*c}, type.getElementType(),
- /*scalableDims=*/{true})}};
+ if (std::optional<int64_t> multiple =
+ matchVScaleMultiple(getDynamicSizes()[0]);
+ multiple && *multiple > 0)
+ return {MemorySlot{getResult(),
+ VectorType::get({*multiple}, type.getElementType(),
+ /*scalableDims=*/{true})}};
}
}
More information about the Mlir-commits
mailing list