[Mlir-commits] [mlir] [mlir][vector] reject negative strides for `vector.load`/`vector.store` (PR #204611)
Federico Bruzzone
llvmlistbot at llvm.org
Tue Jun 23 07:38:04 PDT 2026
https://github.com/FedericoBruzzone updated https://github.com/llvm/llvm-project/pull/204611
>From c6a1ff6bff42bc124711d23620bfbb6fd1531a8b Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Thu, 18 Jun 2026 16:18:08 +0200
Subject: [PATCH 01/10] [mlir][vector] reject negative strides
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
.../mlir/Dialect/Vector/IR/VectorOps.td | 6 +++++
mlir/lib/Dialect/Vector/IR/VectorOps.cpp | 14 +++++++++++
.../Vector/load-store-negative-strides.mlir | 25 +++++++++++++++++++
3 files changed, 45 insertions(+)
create mode 100644 mlir/test/Dialect/Vector/load-store-negative-strides.mlir
diff --git a/mlir/include/mlir/Dialect/Vector/IR/VectorOps.td b/mlir/include/mlir/Dialect/Vector/IR/VectorOps.td
index 7578ce78a0f00..24442a6336090 100644
--- a/mlir/include/mlir/Dialect/Vector/IR/VectorOps.td
+++ b/mlir/include/mlir/Dialect/Vector/IR/VectorOps.td
@@ -1717,6 +1717,9 @@ def Vector_LoadOp : Vector_Op<"load", [
%result = vector.load %memref[%i, %j] : memref<200x100xvector<4x8xf32>>, vector<4x8xf32>
```
+ The memref must have non-negative strides. Negative strides are not supported
+ and will trigger a verification error.
+
Representation-wise, the 'vector.load' operation permits out-of-bounds
reads. Support and implementation of out-of-bounds vector loads is
target-specific. No assumptions should be made on the value of elements
@@ -1835,6 +1838,9 @@ def Vector_StoreOp : Vector_Op<"store", [
vector.store %valueToStore, %memref[%i, %j] : memref<200x100xvector<4x8xf32>>, vector<4x8xf32>
```
+ The memref must have non-negative strides. Negative strides are not supported
+ and will trigger a verification error.
+
Representation-wise, the 'vector.store' operation permits out-of-bounds
writes. Support and implementation of out-of-bounds vector stores are
target-specific. No assumptions should be made on the memory written out of
diff --git a/mlir/lib/Dialect/Vector/IR/VectorOps.cpp b/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
index 67c31730f4b65..651f75dc2498d 100644
--- a/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
+++ b/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
@@ -6194,6 +6194,13 @@ LogicalResult vector::LoadOp::verify() {
if (failed(verifyLoadStoreMemRefLayout(*this, resVecTy, memRefTy)))
return failure();
+ // Negative strides are not supported on vector.load.
+ auto [strides, offset] = memRefTy.getStridesAndOffset();
+ for (int64_t stride : strides) {
+ if (!ShapedType::isDynamic(stride) && stride < 0)
+ return emitOpError("memref strides must be non-negative");
+ }
+
if (memRefTy.getRank() < resVecTy.getRank())
return emitOpError(
"destination memref has lower rank than the result vector");
@@ -6240,6 +6247,13 @@ LogicalResult vector::StoreOp::verify() {
if (failed(verifyLoadStoreMemRefLayout(*this, valueVecTy, memRefTy)))
return failure();
+ // Negative strides are not supported on vector.store.
+ auto [strides, offset] = memRefTy.getStridesAndOffset();
+ for (int64_t stride : strides) {
+ if (!ShapedType::isDynamic(stride) && stride < 0)
+ return emitOpError("memref strides must be non-negative");
+ }
+
if (memRefTy.getRank() < valueVecTy.getRank())
return emitOpError("source memref has lower rank than the vector to store");
diff --git a/mlir/test/Dialect/Vector/load-store-negative-strides.mlir b/mlir/test/Dialect/Vector/load-store-negative-strides.mlir
new file mode 100644
index 0000000000000..777053fd632d2
--- /dev/null
+++ b/mlir/test/Dialect/Vector/load-store-negative-strides.mlir
@@ -0,0 +1,25 @@
+// RUN: mlir-opt %s -split-input-file -verify-diagnostics
+
+// -----
+
+func.func @load_negative_stride(%base: memref<100x100xf32>) -> vector<8xf32> {
+ // expected-error @+5 {{'vector.load' op memref strides must be non-negative}}
+ %flip = memref.reinterpret_cast %base to
+ offset: [0], sizes: [100, 100], strides: [-100, 1]
+ : memref<100x100xf32> to memref<100x100xf32, strided<[-100, 1]>>
+ %c0 = arith.constant 0 : index
+ %v = vector.load %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<8xf32>
+ return %v : vector<8xf32>
+}
+
+// -----
+
+func.func @store_negative_stride(%base: memref<100x100xf32>, %val: vector<4xf32>) {
+ // expected-error @+5 {{'vector.store' op memref strides must be non-negative}}
+ %flip = memref.reinterpret_cast %base to
+ offset: [0], sizes: [100, 100], strides: [-100, 1]
+ : memref<100x100xf32> to memref<100x100xf32, strided<[-100, 1]>>
+ %c0 = arith.constant 0 : index
+ vector.store %val, %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<4xf32>
+ return
+}
>From a9dcf9ba488805411b24b743201d55b30022725d Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Fri, 19 Jun 2026 07:44:28 +0200
Subject: [PATCH 02/10] Address comments
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
mlir/lib/Dialect/Vector/IR/VectorOps.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/mlir/lib/Dialect/Vector/IR/VectorOps.cpp b/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
index 651f75dc2498d..35f9c4ecbf319 100644
--- a/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
+++ b/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
@@ -6197,7 +6197,7 @@ LogicalResult vector::LoadOp::verify() {
// Negative strides are not supported on vector.load.
auto [strides, offset] = memRefTy.getStridesAndOffset();
for (int64_t stride : strides) {
- if (!ShapedType::isDynamic(stride) && stride < 0)
+ if (ShapedType::isStatic(stride) && stride < 0)
return emitOpError("memref strides must be non-negative");
}
@@ -6250,7 +6250,7 @@ LogicalResult vector::StoreOp::verify() {
// Negative strides are not supported on vector.store.
auto [strides, offset] = memRefTy.getStridesAndOffset();
for (int64_t stride : strides) {
- if (!ShapedType::isDynamic(stride) && stride < 0)
+ if (ShapedType::isStatic(stride) && stride < 0)
return emitOpError("memref strides must be non-negative");
}
>From dfda370907d23e18593a0310bf46f565fd81b6bd Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Fri, 19 Jun 2026 11:33:36 +0200
Subject: [PATCH 03/10] Use existing `invalid.mlir` file for verifier tests
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
mlir/test/Dialect/Vector/invalid.mlir | 18 +++++++++++++
.../Vector/load-store-negative-strides.mlir | 25 -------------------
2 files changed, 18 insertions(+), 25 deletions(-)
delete mode 100644 mlir/test/Dialect/Vector/load-store-negative-strides.mlir
diff --git a/mlir/test/Dialect/Vector/invalid.mlir b/mlir/test/Dialect/Vector/invalid.mlir
index 2fed3002596a3..bc035964a6af6 100644
--- a/mlir/test/Dialect/Vector/invalid.mlir
+++ b/mlir/test/Dialect/Vector/invalid.mlir
@@ -2145,6 +2145,24 @@ func.func @store_non_unit_stride(%src : memref<?xi8, strided<[2], offset:?>>,%va
// -----
+func.func @load_negative_stride(%flip: memref<100x100xf32, strided<[-100, 1]>>) -> vector<8xf32> {
+ // expected-error @+2 {{'vector.load' op memref strides must be non-negative}}
+ %c0 = arith.constant 0 : index
+ %v = vector.load %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<8xf32>
+ return %v : vector<8xf32>
+}
+
+// -----
+
+func.func @store_negative_stride(%flip: memref<100x100xf32, strided<[-100, 1]>>, %val: vector<4xf32>) {
+ // expected-error @+2 {{'vector.store' op memref strides must be non-negative}}
+ %c0 = arith.constant 0 : index
+ vector.store %val, %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<4xf32>
+ return
+}
+
+// -----
+
// Verify that vector.bitcast rejects vectors with i0 (zero-bitwidth) element type.
func.func @bitcast_i0(%a: vector<4xi0>) -> vector<4xi0> {
// expected-error @+1 {{'vector.bitcast' op operand #0 must be vector of non-zero-bitwidth type values, but got 'vector<4xi0>'}}
diff --git a/mlir/test/Dialect/Vector/load-store-negative-strides.mlir b/mlir/test/Dialect/Vector/load-store-negative-strides.mlir
deleted file mode 100644
index 777053fd632d2..0000000000000
--- a/mlir/test/Dialect/Vector/load-store-negative-strides.mlir
+++ /dev/null
@@ -1,25 +0,0 @@
-// RUN: mlir-opt %s -split-input-file -verify-diagnostics
-
-// -----
-
-func.func @load_negative_stride(%base: memref<100x100xf32>) -> vector<8xf32> {
- // expected-error @+5 {{'vector.load' op memref strides must be non-negative}}
- %flip = memref.reinterpret_cast %base to
- offset: [0], sizes: [100, 100], strides: [-100, 1]
- : memref<100x100xf32> to memref<100x100xf32, strided<[-100, 1]>>
- %c0 = arith.constant 0 : index
- %v = vector.load %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<8xf32>
- return %v : vector<8xf32>
-}
-
-// -----
-
-func.func @store_negative_stride(%base: memref<100x100xf32>, %val: vector<4xf32>) {
- // expected-error @+5 {{'vector.store' op memref strides must be non-negative}}
- %flip = memref.reinterpret_cast %base to
- offset: [0], sizes: [100, 100], strides: [-100, 1]
- : memref<100x100xf32> to memref<100x100xf32, strided<[-100, 1]>>
- %c0 = arith.constant 0 : index
- vector.store %val, %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<4xf32>
- return
-}
>From 4bb020efa9c03c458f94a065f92639523f435f41 Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Fri, 19 Jun 2026 13:21:50 +0200
Subject: [PATCH 04/10] Update mlir/test/Dialect/Vector/invalid.mlir
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Andrzej Warzyński <andrzej.warzynski at gmail.com>
---
mlir/test/Dialect/Vector/invalid.mlir | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/test/Dialect/Vector/invalid.mlir b/mlir/test/Dialect/Vector/invalid.mlir
index bc035964a6af6..edd8dadabaa68 100644
--- a/mlir/test/Dialect/Vector/invalid.mlir
+++ b/mlir/test/Dialect/Vector/invalid.mlir
@@ -2145,7 +2145,7 @@ func.func @store_non_unit_stride(%src : memref<?xi8, strided<[2], offset:?>>,%va
// -----
-func.func @load_negative_stride(%flip: memref<100x100xf32, strided<[-100, 1]>>) -> vector<8xf32> {
+func.func @load_negative_stride(%src: memref<100x100xf32, strided<[-100, 1]>>) -> vector<8xf32> {
// expected-error @+2 {{'vector.load' op memref strides must be non-negative}}
%c0 = arith.constant 0 : index
%v = vector.load %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<8xf32>
>From d2d3cd6ab2aed33567ad69965fb1d2a35d2f8906 Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Fri, 19 Jun 2026 13:22:08 +0200
Subject: [PATCH 05/10] Update mlir/test/Dialect/Vector/invalid.mlir
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Andrzej Warzyński <andrzej.warzynski at gmail.com>
---
mlir/test/Dialect/Vector/invalid.mlir | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/test/Dialect/Vector/invalid.mlir b/mlir/test/Dialect/Vector/invalid.mlir
index edd8dadabaa68..80057de5ad142 100644
--- a/mlir/test/Dialect/Vector/invalid.mlir
+++ b/mlir/test/Dialect/Vector/invalid.mlir
@@ -2154,7 +2154,7 @@ func.func @load_negative_stride(%src: memref<100x100xf32, strided<[-100, 1]>>) -
// -----
-func.func @store_negative_stride(%flip: memref<100x100xf32, strided<[-100, 1]>>, %val: vector<4xf32>) {
+func.func @store_negative_stride(%src: memref<100x100xf32, strided<[-100, 1]>>, %val: vector<4xf32>) {
// expected-error @+2 {{'vector.store' op memref strides must be non-negative}}
%c0 = arith.constant 0 : index
vector.store %val, %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<4xf32>
>From c3bbd1972a4528f0a52e992564a5839accc405dc Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Fri, 19 Jun 2026 13:26:27 +0200
Subject: [PATCH 06/10] Move load test under the correct section
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
mlir/test/Dialect/Vector/invalid.mlir | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/mlir/test/Dialect/Vector/invalid.mlir b/mlir/test/Dialect/Vector/invalid.mlir
index 80057de5ad142..bc06c1e19dc2d 100644
--- a/mlir/test/Dialect/Vector/invalid.mlir
+++ b/mlir/test/Dialect/Vector/invalid.mlir
@@ -2145,15 +2145,6 @@ func.func @store_non_unit_stride(%src : memref<?xi8, strided<[2], offset:?>>,%va
// -----
-func.func @load_negative_stride(%src: memref<100x100xf32, strided<[-100, 1]>>) -> vector<8xf32> {
- // expected-error @+2 {{'vector.load' op memref strides must be non-negative}}
- %c0 = arith.constant 0 : index
- %v = vector.load %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<8xf32>
- return %v : vector<8xf32>
-}
-
-// -----
-
func.func @store_negative_stride(%src: memref<100x100xf32, strided<[-100, 1]>>, %val: vector<4xf32>) {
// expected-error @+2 {{'vector.store' op memref strides must be non-negative}}
%c0 = arith.constant 0 : index
@@ -2216,3 +2207,12 @@ func.func @scan_i0(%a: vector<4xi0>, %init: vector<1xi0>) -> (vector<4xi0>, vect
vector<4xi0>, vector<1xi0>
return %0#0, %0#1 : vector<4xi0>, vector<1xi0>
}
+
+// -----
+
+func.func @load_negative_stride(%src: memref<100x100xf32, strided<[-100, 1]>>) -> vector<8xf32> {
+ // expected-error @+2 {{'vector.load' op memref strides must be non-negative}}
+ %c0 = arith.constant 0 : index
+ %v = vector.load %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<8xf32>
+ return %v : vector<8xf32>
+}
>From 2458d4aa7959684ea58531afcc6302e2236950d5 Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Fri, 19 Jun 2026 13:40:00 +0200
Subject: [PATCH 07/10] ups: minor fix
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
mlir/test/Dialect/Vector/invalid.mlir | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/mlir/test/Dialect/Vector/invalid.mlir b/mlir/test/Dialect/Vector/invalid.mlir
index bc06c1e19dc2d..403581e338a6f 100644
--- a/mlir/test/Dialect/Vector/invalid.mlir
+++ b/mlir/test/Dialect/Vector/invalid.mlir
@@ -2148,7 +2148,7 @@ func.func @store_non_unit_stride(%src : memref<?xi8, strided<[2], offset:?>>,%va
func.func @store_negative_stride(%src: memref<100x100xf32, strided<[-100, 1]>>, %val: vector<4xf32>) {
// expected-error @+2 {{'vector.store' op memref strides must be non-negative}}
%c0 = arith.constant 0 : index
- vector.store %val, %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<4xf32>
+ vector.store %val, %src[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<4xf32>
return
}
@@ -2213,6 +2213,6 @@ func.func @scan_i0(%a: vector<4xi0>, %init: vector<1xi0>) -> (vector<4xi0>, vect
func.func @load_negative_stride(%src: memref<100x100xf32, strided<[-100, 1]>>) -> vector<8xf32> {
// expected-error @+2 {{'vector.load' op memref strides must be non-negative}}
%c0 = arith.constant 0 : index
- %v = vector.load %flip[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<8xf32>
+ %v = vector.load %src[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<8xf32>
return %v : vector<8xf32>
}
>From ce5d7099840a0ba9b09a008a5506e133a8462d94 Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Mon, 22 Jun 2026 13:23:27 +0200
Subject: [PATCH 08/10] Healthy code reuse
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
.../mlir/Dialect/MemRef/Utils/MemRefUtils.h | 19 +++++++++++++++++++
.../VectorToLLVM/ConvertVectorToLLVM.cpp | 13 ++-----------
mlir/lib/Dialect/MemRef/Utils/MemRefUtils.cpp | 14 ++++++++++++++
mlir/lib/Dialect/Vector/IR/VectorOps.cpp | 15 +++++----------
4 files changed, 40 insertions(+), 21 deletions(-)
diff --git a/mlir/include/mlir/Dialect/MemRef/Utils/MemRefUtils.h b/mlir/include/mlir/Dialect/MemRef/Utils/MemRefUtils.h
index 613d567de2457..9bf9ba4a16b20 100644
--- a/mlir/include/mlir/Dialect/MemRef/Utils/MemRefUtils.h
+++ b/mlir/include/mlir/Dialect/MemRef/Utils/MemRefUtils.h
@@ -194,6 +194,25 @@ LogicalResult resolveSourceIndicesRankReducingSubview(
Location loc, OpBuilder &b, memref::SubViewOp subViewOp, ValueRange indices,
SmallVectorImpl<Value> &sourceIndices);
+/// Returns true if all strides of `memRefTy` are static and non-negative.
+///
+/// Dynamic strides cause this to return false because their sign is unknown at
+/// compile time. Use `hasNegativeStaticStride` instead when dynamic strides
+/// should be treated as acceptable.
+///
+/// Typical use: guard `mul nuw` GEP flags during LLVM lowering, where an
+/// unknown-sign stride could make the arithmetic wrap.
+bool hasNonNegativeStaticStrides(MemRefType memRefTy);
+
+/// Returns true if any stride of `memRefTy` is statically known to be
+/// negative.
+///
+/// Dynamic strides are conservatively treated as non-negative (sign unknown),
+/// so only static negative values trigger this predicate. This is the
+/// complement of `hasNonNegativeStaticStrides` for legality checks that must
+/// not reject dynamically-strided memrefs.
+bool hasNegativeStaticStride(MemRefType memRefTy);
+
} // namespace memref
} // namespace mlir
diff --git a/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp b/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp
index 8e9d37648841a..60618ffec168f 100644
--- a/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp
+++ b/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp
@@ -18,6 +18,7 @@
#include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
+#include "mlir/Dialect/MemRef/Utils/MemRefUtils.h"
#include "mlir/Dialect/Vector/IR/VectorOps.h"
#include "mlir/Dialect/Vector/Interfaces/MaskableOpInterface.h"
#include "mlir/Dialect/Vector/Transforms/LoweringPatterns.h"
@@ -223,16 +224,6 @@ static void replaceLoadOrStoreOp(vector::MaskedStoreOp storeOp,
storeOp, adaptor.getValueToStore(), ptr, adaptor.getMask(), align);
}
-/// Returns true if all strides of `memRefTy` are static and non-negative. A
-/// negative (or dynamic, hence unknown-sign) stride would make `mul nuw` on the
-/// index arithmetic wrap, so `nuw` must not be emitted in that case.
-static bool hasNonNegativeStrides(MemRefType memRefTy) {
- auto [strides, offset] = memRefTy.getStridesAndOffset();
- return llvm::all_of(strides, [](int64_t stride) {
- return !ShapedType::isDynamic(stride) && stride >= 0;
- });
-}
-
/// Conversion pattern for a vector.load, vector.store, vector.maskedload, and
/// vector.maskedstore.
template <class LoadOrStoreOp>
@@ -285,7 +276,7 @@ class VectorLoadStoreConversion : public ConvertOpToLLVMPattern<LoadOrStoreOp> {
noWrapFlags = noWrapFlags | LLVM::GEPNoWrapFlags::inbounds;
// `nuw` additionally requires non-negative strides; skip it when the
// memref has dynamic or negative strides to avoid emitting poison.
- if (hasNonNegativeStrides(memRefTy))
+ if (memref::hasNonNegativeStaticStrides(memRefTy))
noWrapFlags = noWrapFlags | LLVM::GEPNoWrapFlags::nuw;
}
}
diff --git a/mlir/lib/Dialect/MemRef/Utils/MemRefUtils.cpp b/mlir/lib/Dialect/MemRef/Utils/MemRefUtils.cpp
index 2aea597dd5b90..d0c6f58dc0a9e 100644
--- a/mlir/lib/Dialect/MemRef/Utils/MemRefUtils.cpp
+++ b/mlir/lib/Dialect/MemRef/Utils/MemRefUtils.cpp
@@ -344,5 +344,19 @@ LogicalResult resolveSourceIndicesRankReducingSubview(
return success();
}
+bool hasNonNegativeStaticStrides(MemRefType memRefTy) {
+ auto [strides, offset] = memRefTy.getStridesAndOffset();
+ return llvm::all_of(strides, [](int64_t stride) {
+ return ShapedType::isStatic(stride) && stride >= 0;
+ });
+}
+
+bool hasNegativeStaticStride(MemRefType memRefTy) {
+ auto [strides, offset] = memRefTy.getStridesAndOffset();
+ return llvm::any_of(strides, [](int64_t stride) {
+ return ShapedType::isStatic(stride) && stride < 0;
+ });
+}
+
} // namespace memref
} // namespace mlir
diff --git a/mlir/lib/Dialect/Vector/IR/VectorOps.cpp b/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
index 35f9c4ecbf319..81ffabca6ecf0 100644
--- a/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
+++ b/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
@@ -20,6 +20,7 @@
#include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/Dialect/MemRef/IR/MemoryAccessOpInterfaces.h"
+#include "mlir/Dialect/MemRef/Utils/MemRefUtils.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"
#include "mlir/Dialect/UB/IR/UBMatchers.h"
#include "mlir/Dialect/Utils/IndexingUtils.h"
@@ -6195,11 +6196,8 @@ LogicalResult vector::LoadOp::verify() {
return failure();
// Negative strides are not supported on vector.load.
- auto [strides, offset] = memRefTy.getStridesAndOffset();
- for (int64_t stride : strides) {
- if (ShapedType::isStatic(stride) && stride < 0)
- return emitOpError("memref strides must be non-negative");
- }
+ if (memref::hasNegativeStaticStride(memRefTy))
+ return emitOpError("memref strides must be non-negative");
if (memRefTy.getRank() < resVecTy.getRank())
return emitOpError(
@@ -6248,11 +6246,8 @@ LogicalResult vector::StoreOp::verify() {
return failure();
// Negative strides are not supported on vector.store.
- auto [strides, offset] = memRefTy.getStridesAndOffset();
- for (int64_t stride : strides) {
- if (ShapedType::isStatic(stride) && stride < 0)
- return emitOpError("memref strides must be non-negative");
- }
+ if (memref::hasNegativeStaticStride(memRefTy))
+ return emitOpError("memref strides must be non-negative");
if (memRefTy.getRank() < valueVecTy.getRank())
return emitOpError("source memref has lower rank than the vector to store");
>From e650649cd59bde40b83752bb1b10ac294a9389d4 Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Tue, 23 Jun 2026 10:26:02 +0200
Subject: [PATCH 09/10] Fix toy? :D
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
mlir/lib/Dialect/Vector/IR/CMakeLists.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/mlir/lib/Dialect/Vector/IR/CMakeLists.txt b/mlir/lib/Dialect/Vector/IR/CMakeLists.txt
index 0248896e096a0..f45618cda8ece 100644
--- a/mlir/lib/Dialect/Vector/IR/CMakeLists.txt
+++ b/mlir/lib/Dialect/Vector/IR/CMakeLists.txt
@@ -24,6 +24,7 @@ add_mlir_dialect_library(MLIRVectorDialect
MLIRMaskableOpInterface
MLIRMaskingOpInterface
MLIRMemRefDialect
+ MLIRMemRefUtils
MLIRSideEffectInterfaces
MLIRTensorDialect
MLIRUBDialect
>From cf2b4f73742339128a3b397c2bccf8814ef7f1cf Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Tue, 23 Jun 2026 16:37:16 +0200
Subject: [PATCH 10/10] Address comments
Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
.../mlir/Dialect/MemRef/Utils/MemRefUtils.h | 15 ---------------
.../VectorToLLVM/ConvertVectorToLLVM.cpp | 10 ++++++----
mlir/lib/Dialect/MemRef/Utils/MemRefUtils.cpp | 7 -------
3 files changed, 6 insertions(+), 26 deletions(-)
diff --git a/mlir/include/mlir/Dialect/MemRef/Utils/MemRefUtils.h b/mlir/include/mlir/Dialect/MemRef/Utils/MemRefUtils.h
index 9bf9ba4a16b20..c93da6ebada7c 100644
--- a/mlir/include/mlir/Dialect/MemRef/Utils/MemRefUtils.h
+++ b/mlir/include/mlir/Dialect/MemRef/Utils/MemRefUtils.h
@@ -194,23 +194,8 @@ LogicalResult resolveSourceIndicesRankReducingSubview(
Location loc, OpBuilder &b, memref::SubViewOp subViewOp, ValueRange indices,
SmallVectorImpl<Value> &sourceIndices);
-/// Returns true if all strides of `memRefTy` are static and non-negative.
-///
-/// Dynamic strides cause this to return false because their sign is unknown at
-/// compile time. Use `hasNegativeStaticStride` instead when dynamic strides
-/// should be treated as acceptable.
-///
-/// Typical use: guard `mul nuw` GEP flags during LLVM lowering, where an
-/// unknown-sign stride could make the arithmetic wrap.
-bool hasNonNegativeStaticStrides(MemRefType memRefTy);
-
/// Returns true if any stride of `memRefTy` is statically known to be
/// negative.
-///
-/// Dynamic strides are conservatively treated as non-negative (sign unknown),
-/// so only static negative values trigger this predicate. This is the
-/// complement of `hasNonNegativeStaticStrides` for legality checks that must
-/// not reject dynamically-strided memrefs.
bool hasNegativeStaticStride(MemRefType memRefTy);
} // namespace memref
diff --git a/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp b/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp
index 60618ffec168f..e186df33bb7a4 100644
--- a/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp
+++ b/mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp
@@ -274,10 +274,12 @@ class VectorLoadStoreConversion : public ConvertOpToLLVMPattern<LoadOrStoreOp> {
"vector.load/store requires unit trailing memref stride");
if (enableGEPInboundsNuw) {
noWrapFlags = noWrapFlags | LLVM::GEPNoWrapFlags::inbounds;
- // `nuw` additionally requires non-negative strides; skip it when the
- // memref has dynamic or negative strides to avoid emitting poison.
- if (memref::hasNonNegativeStaticStrides(memRefTy))
- noWrapFlags = noWrapFlags | LLVM::GEPNoWrapFlags::nuw;
+
+ // `nuw` additionally requires non-negative strides.
+ assert(
+ !(memref::hasNegativeStaticStride(memRefTy)) &&
+ "Invalid MemRef type - should have been rejected by Op verifier.");
+ noWrapFlags = noWrapFlags | LLVM::GEPNoWrapFlags::nuw;
}
}
auto vtype = cast<VectorType>(
diff --git a/mlir/lib/Dialect/MemRef/Utils/MemRefUtils.cpp b/mlir/lib/Dialect/MemRef/Utils/MemRefUtils.cpp
index d0c6f58dc0a9e..bc019d601dcd9 100644
--- a/mlir/lib/Dialect/MemRef/Utils/MemRefUtils.cpp
+++ b/mlir/lib/Dialect/MemRef/Utils/MemRefUtils.cpp
@@ -344,13 +344,6 @@ LogicalResult resolveSourceIndicesRankReducingSubview(
return success();
}
-bool hasNonNegativeStaticStrides(MemRefType memRefTy) {
- auto [strides, offset] = memRefTy.getStridesAndOffset();
- return llvm::all_of(strides, [](int64_t stride) {
- return ShapedType::isStatic(stride) && stride >= 0;
- });
-}
-
bool hasNegativeStaticStride(MemRefType memRefTy) {
auto [strides, offset] = memRefTy.getStridesAndOffset();
return llvm::any_of(strides, [](int64_t stride) {
More information about the Mlir-commits
mailing list