[Mlir-commits] [mlir] [mlir][VectorToSCF] Decline transfer ops with no automatic allocation scope (PR #216947)

Alessandro Potenza llvmlistbot at llvm.org
Mon Aug 24 02:11:02 PDT 2026


https://github.com/alepot55 updated https://github.com/llvm/llvm-project/pull/216947

>From 74a2aa81d7bb9975cf8340e0c72c7cd289e8fc4e Mon Sep 17 00:00:00 2001
From: Alessandro Potenza <ap.alessandro.potenza at gmail.com>
Date: Tue, 18 Aug 2026 09:31:07 +0200
Subject: [PATCH 1/4] [mlir][VectorToSCF] Decline transfer ops with no
 automatic allocation scope

Lowering a transfer op whose vector rank exceeds the target rank allocates a
temporary buffer via memref.alloca, whose verifier requires an ancestor with
the AutomaticAllocationScope trait. The pattern did not check for one, so a
transfer op at module scope hit an assertion in getAutomaticAllocationScope
instead of being left alone.

Fixes #216225.
---
 mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp      |  6 ++++++
 mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir | 12 ++++++++++++
 2 files changed, 18 insertions(+)
 create mode 100644 mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir

diff --git a/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp b/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp
index c9eba6962e6a4..1778fc2493c91 100644
--- a/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp
+++ b/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp
@@ -567,6 +567,12 @@ static LogicalResult checkPrepareXferOp(OpTy xferOp, PatternRewriter &rewriter,
       xferOp.getShapedType().getElementType())
     return rewriter.notifyMatchFailure(
         xferOp, "Mismatching source and destination element types.");
+  // The lowering allocates a temporary buffer, which `memref.alloca` only
+  // allows inside an automatic allocation scope.
+  Operation *op = xferOp.getOperation();
+  if (!op->getParentWithTrait<OpTrait::AutomaticAllocationScope>())
+    return rewriter.notifyMatchFailure(
+        xferOp, "xferOp is not inside an automatic allocation scope");
 
   return success();
 }
diff --git a/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir b/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir
new file mode 100644
index 0000000000000..6b817b933aa86
--- /dev/null
+++ b/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir
@@ -0,0 +1,12 @@
+// RUN: mlir-opt %s -convert-vector-to-scf | FileCheck %s
+
+// The lowering of a transfer op whose rank exceeds the target rank allocates a
+// temporary buffer, so it needs an enclosing automatic allocation scope. These
+// ops sit directly in the implicit `builtin.module`, which is not one, so the
+// pattern has to decline instead of building an invalid `memref.alloca`.
+
+// CHECK: vector.transfer_write
+%c0 = arith.constant 0 : index
+%cst = arith.constant dense<0.0> : vector<2x3xf32>
+%m = memref.alloc() : memref<2x3xf32>
+vector.transfer_write %cst, %m[%c0, %c0] : vector<2x3xf32>, memref<2x3xf32>

>From cfa8818653ff0ec556c565ecf99ebda7e5d58ea4 Mon Sep 17 00:00:00 2001
From: Alessandro Potenza <ap.alessandro.potenza at gmail.com>
Date: Tue, 18 Aug 2026 17:10:43 +0200
Subject: [PATCH 2/4] Address review: trim the test comment and drop the
 duplicated one in the pattern

---
 mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp      | 2 --
 mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir | 4 +---
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp b/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp
index 1778fc2493c91..f1085e3c86b36 100644
--- a/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp
+++ b/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp
@@ -567,8 +567,6 @@ static LogicalResult checkPrepareXferOp(OpTy xferOp, PatternRewriter &rewriter,
       xferOp.getShapedType().getElementType())
     return rewriter.notifyMatchFailure(
         xferOp, "Mismatching source and destination element types.");
-  // The lowering allocates a temporary buffer, which `memref.alloca` only
-  // allows inside an automatic allocation scope.
   Operation *op = xferOp.getOperation();
   if (!op->getParentWithTrait<OpTrait::AutomaticAllocationScope>())
     return rewriter.notifyMatchFailure(
diff --git a/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir b/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir
index 6b817b933aa86..b6d4ffb53e01e 100644
--- a/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir
+++ b/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir
@@ -1,9 +1,7 @@
 // RUN: mlir-opt %s -convert-vector-to-scf | FileCheck %s
 
 // The lowering of a transfer op whose rank exceeds the target rank allocates a
-// temporary buffer, so it needs an enclosing automatic allocation scope. These
-// ops sit directly in the implicit `builtin.module`, which is not one, so the
-// pattern has to decline instead of building an invalid `memref.alloca`.
+// temporary buffer, so it needs an enclosing automatic allocation scope.
 
 // CHECK: vector.transfer_write
 %c0 = arith.constant 0 : index

>From e26ee232b10dfa5f4df3f5c34d19154a545acbf8 Mon Sep 17 00:00:00 2001
From: Alessandro Potenza <ap.alessandro.potenza at gmail.com>
Date: Wed, 19 Aug 2026 16:38:49 +0200
Subject: [PATCH 3/4] Make the no-alloc-scope test explicitly negative

---
 mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir b/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir
index b6d4ffb53e01e..40b959d2d7df5 100644
--- a/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir
+++ b/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir
@@ -1,8 +1,11 @@
 // RUN: mlir-opt %s -convert-vector-to-scf | FileCheck %s
 
-// The lowering of a transfer op whose rank exceeds the target rank allocates a
-// temporary buffer, so it needs an enclosing automatic allocation scope.
+// Negative test. Lowering a transfer op whose rank exceeds the target rank
+// allocates a temporary buffer, so it needs an enclosing automatic allocation
+// scope. These ops sit directly in the implicit `builtin.module`, which is not
+// one, so the pattern has to decline and leave the transfer op alone.
 
+// CHECK-NOT: memref.alloca
 // CHECK: vector.transfer_write
 %c0 = arith.constant 0 : index
 %cst = arith.constant dense<0.0> : vector<2x3xf32>

>From 2c7c15846a1e5584165544e457bbb97f5b00aea5 Mon Sep 17 00:00:00 2001
From: Alessandro Potenza <ap.alessandro.potenza at gmail.com>
Date: Wed, 19 Aug 2026 17:11:13 +0200
Subject: [PATCH 4/4] Fold the negative test into vector-to-scf.mlir and drop
 the nested pipeline from its RUN line

---
 .../Conversion/VectorToSCF/no-alloc-scope.mlir  | 13 -------------
 .../Conversion/VectorToSCF/vector-to-scf.mlir   | 17 ++++++++++++++++-
 2 files changed, 16 insertions(+), 14 deletions(-)
 delete mode 100644 mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir

diff --git a/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir b/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir
deleted file mode 100644
index 40b959d2d7df5..0000000000000
--- a/mlir/test/Conversion/VectorToSCF/no-alloc-scope.mlir
+++ /dev/null
@@ -1,13 +0,0 @@
-// RUN: mlir-opt %s -convert-vector-to-scf | FileCheck %s
-
-// Negative test. Lowering a transfer op whose rank exceeds the target rank
-// allocates a temporary buffer, so it needs an enclosing automatic allocation
-// scope. These ops sit directly in the implicit `builtin.module`, which is not
-// one, so the pattern has to decline and leave the transfer op alone.
-
-// CHECK-NOT: memref.alloca
-// CHECK: vector.transfer_write
-%c0 = arith.constant 0 : index
-%cst = arith.constant dense<0.0> : vector<2x3xf32>
-%m = memref.alloc() : memref<2x3xf32>
-vector.transfer_write %cst, %m[%c0, %c0] : vector<2x3xf32>, memref<2x3xf32>
diff --git a/mlir/test/Conversion/VectorToSCF/vector-to-scf.mlir b/mlir/test/Conversion/VectorToSCF/vector-to-scf.mlir
index 1ed82954398f0..e3a7f57e144ed 100644
--- a/mlir/test/Conversion/VectorToSCF/vector-to-scf.mlir
+++ b/mlir/test/Conversion/VectorToSCF/vector-to-scf.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt %s -pass-pipeline="builtin.module(func.func(convert-vector-to-scf))" -split-input-file -allow-unregistered-dialect | FileCheck %s
+// RUN: mlir-opt %s -convert-vector-to-scf -split-input-file -allow-unregistered-dialect | FileCheck %s
 // RUN: mlir-opt %s -pass-pipeline="builtin.module(func.func(convert-vector-to-scf{full-unroll=true lower-scalable=true}))" -split-input-file -allow-unregistered-dialect | FileCheck %s --check-prefix=FULL-UNROLL
 // RUN: mlir-opt %s "-convert-vector-to-scf=full-unroll target-rank=0" -split-input-file -allow-unregistered-dialect | FileCheck %s --check-prefix=TARGET-RANK-ZERO
 
@@ -908,3 +908,18 @@ func.func @negative_scalable_transpose_store_3(%vec: vector<[4]x4xf32>, %dest: m
 }
 // FULL-UNROLL-LABEL: @negative_scalable_transpose_store_3
 // FULL-UNROLL-NOT:   scf.for
+
+// -----
+
+// Negative test: lowering a transfer op whose rank exceeds the target rank
+// allocates a temporary buffer, which requires an enclosing automatic
+// allocation scope.
+
+// CHECK-LABEL: @transfer_write_no_alloc_scope
+func.func private @transfer_write_no_alloc_scope()
+// CHECK-NOT: memref.alloca
+// CHECK: vector.transfer_write
+%c0 = arith.constant 0 : index
+%cst = arith.constant dense<0.0> : vector<2x3xf32>
+%m = memref.alloc() : memref<2x3xf32>
+vector.transfer_write %cst, %m[%c0, %c0] : vector<2x3xf32>, memref<2x3xf32>



More information about the Mlir-commits mailing list