Skip to content

Commit 916a974

Browse files
[mlir][vector] reject negative strides for vector.load/vector.store (#204611)
This PR follows up #204309 and #204309. It simply rejects negative strides for vector.load/vector.store :D AI Disclaimer: I used AI for the tests. --------- Signed-off-by: Federico Bruzzone <federico.bruzzone.i@gmail.com> Co-authored-by: Andrzej Warzyński <andrzej.warzynski@gmail.com>
1 parent 779c908 commit 916a974

7 files changed

Lines changed: 52 additions & 14 deletions

File tree

mlir/include/mlir/Dialect/MemRef/Utils/MemRefUtils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ LogicalResult resolveSourceIndicesRankReducingSubview(
194194
Location loc, OpBuilder &b, memref::SubViewOp subViewOp, ValueRange indices,
195195
SmallVectorImpl<Value> &sourceIndices);
196196

197+
/// Returns true if any stride of `memRefTy` is statically known to be
198+
/// negative.
199+
bool hasNegativeStaticStride(MemRefType memRefTy);
200+
197201
} // namespace memref
198202
} // namespace mlir
199203

mlir/include/mlir/Dialect/Vector/IR/VectorOps.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1717,6 +1717,9 @@ def Vector_LoadOp : Vector_Op<"load", [
17171717
%result = vector.load %memref[%i, %j] : memref<200x100xvector<4x8xf32>>, vector<4x8xf32>
17181718
```
17191719

1720+
The memref must have non-negative strides. Negative strides are not supported
1721+
and will trigger a verification error.
1722+
17201723
Representation-wise, the 'vector.load' operation permits out-of-bounds
17211724
reads. Support and implementation of out-of-bounds vector loads is
17221725
target-specific. No assumptions should be made on the value of elements
@@ -1835,6 +1838,9 @@ def Vector_StoreOp : Vector_Op<"store", [
18351838
vector.store %valueToStore, %memref[%i, %j] : memref<200x100xvector<4x8xf32>>, vector<4x8xf32>
18361839
```
18371840

1841+
The memref must have non-negative strides. Negative strides are not supported
1842+
and will trigger a verification error.
1843+
18381844
Representation-wise, the 'vector.store' operation permits out-of-bounds
18391845
writes. Support and implementation of out-of-bounds vector stores are
18401846
target-specific. No assumptions should be made on the memory written out of

mlir/lib/Conversion/VectorToLLVM/ConvertVectorToLLVM.cpp

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "mlir/Dialect/LLVMIR/FunctionCallUtils.h"
1919
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
2020
#include "mlir/Dialect/MemRef/IR/MemRef.h"
21+
#include "mlir/Dialect/MemRef/Utils/MemRefUtils.h"
2122
#include "mlir/Dialect/Vector/IR/VectorOps.h"
2223
#include "mlir/Dialect/Vector/Interfaces/MaskableOpInterface.h"
2324
#include "mlir/Dialect/Vector/Transforms/LoweringPatterns.h"
@@ -223,16 +224,6 @@ static void replaceLoadOrStoreOp(vector::MaskedStoreOp storeOp,
223224
storeOp, adaptor.getValueToStore(), ptr, adaptor.getMask(), align);
224225
}
225226

226-
/// Returns true if all strides of `memRefTy` are static and non-negative. A
227-
/// negative (or dynamic, hence unknown-sign) stride would make `mul nuw` on the
228-
/// index arithmetic wrap, so `nuw` must not be emitted in that case.
229-
static bool hasNonNegativeStrides(MemRefType memRefTy) {
230-
auto [strides, offset] = memRefTy.getStridesAndOffset();
231-
return llvm::all_of(strides, [](int64_t stride) {
232-
return !ShapedType::isDynamic(stride) && stride >= 0;
233-
});
234-
}
235-
236227
/// Conversion pattern for a vector.load, vector.store, vector.maskedload, and
237228
/// vector.maskedstore.
238229
template <class LoadOrStoreOp>
@@ -283,10 +274,12 @@ class VectorLoadStoreConversion : public ConvertOpToLLVMPattern<LoadOrStoreOp> {
283274
"vector.load/store requires unit trailing memref stride");
284275
if (enableGEPInboundsNuw) {
285276
noWrapFlags = noWrapFlags | LLVM::GEPNoWrapFlags::inbounds;
286-
// `nuw` additionally requires non-negative strides; skip it when the
287-
// memref has dynamic or negative strides to avoid emitting poison.
288-
if (hasNonNegativeStrides(memRefTy))
289-
noWrapFlags = noWrapFlags | LLVM::GEPNoWrapFlags::nuw;
277+
278+
// `nuw` additionally requires non-negative strides.
279+
assert(
280+
!(memref::hasNegativeStaticStride(memRefTy)) &&
281+
"Invalid MemRef type - should have been rejected by Op verifier.");
282+
noWrapFlags = noWrapFlags | LLVM::GEPNoWrapFlags::nuw;
290283
}
291284
}
292285
auto vtype = cast<VectorType>(

mlir/lib/Dialect/MemRef/Utils/MemRefUtils.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,5 +344,12 @@ LogicalResult resolveSourceIndicesRankReducingSubview(
344344
return success();
345345
}
346346

347+
bool hasNegativeStaticStride(MemRefType memRefTy) {
348+
auto [strides, offset] = memRefTy.getStridesAndOffset();
349+
return llvm::any_of(strides, [](int64_t stride) {
350+
return ShapedType::isStatic(stride) && stride < 0;
351+
});
352+
}
353+
347354
} // namespace memref
348355
} // namespace mlir

mlir/lib/Dialect/Vector/IR/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_mlir_dialect_library(MLIRVectorDialect
2424
MLIRMaskableOpInterface
2525
MLIRMaskingOpInterface
2626
MLIRMemRefDialect
27+
MLIRMemRefUtils
2728
MLIRSideEffectInterfaces
2829
MLIRTensorDialect
2930
MLIRUBDialect

mlir/lib/Dialect/Vector/IR/VectorOps.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h"
2121
#include "mlir/Dialect/MemRef/IR/MemRef.h"
2222
#include "mlir/Dialect/MemRef/IR/MemoryAccessOpInterfaces.h"
23+
#include "mlir/Dialect/MemRef/Utils/MemRefUtils.h"
2324
#include "mlir/Dialect/Tensor/IR/Tensor.h"
2425
#include "mlir/Dialect/UB/IR/UBMatchers.h"
2526
#include "mlir/Dialect/Utils/IndexingUtils.h"
@@ -6194,6 +6195,10 @@ LogicalResult vector::LoadOp::verify() {
61946195
if (failed(verifyLoadStoreMemRefLayout(*this, resVecTy, memRefTy)))
61956196
return failure();
61966197

6198+
// Negative strides are not supported on vector.load.
6199+
if (memref::hasNegativeStaticStride(memRefTy))
6200+
return emitOpError("memref strides must be non-negative");
6201+
61976202
if (memRefTy.getRank() < resVecTy.getRank())
61986203
return emitOpError(
61996204
"destination memref has lower rank than the result vector");
@@ -6240,6 +6245,10 @@ LogicalResult vector::StoreOp::verify() {
62406245
if (failed(verifyLoadStoreMemRefLayout(*this, valueVecTy, memRefTy)))
62416246
return failure();
62426247

6248+
// Negative strides are not supported on vector.store.
6249+
if (memref::hasNegativeStaticStride(memRefTy))
6250+
return emitOpError("memref strides must be non-negative");
6251+
62436252
if (memRefTy.getRank() < valueVecTy.getRank())
62446253
return emitOpError("source memref has lower rank than the vector to store");
62456254

mlir/test/Dialect/Vector/invalid.mlir

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2145,6 +2145,15 @@ func.func @store_non_unit_stride(%src : memref<?xi8, strided<[2], offset:?>>,%va
21452145

21462146
// -----
21472147

2148+
func.func @store_negative_stride(%src: memref<100x100xf32, strided<[-100, 1]>>, %val: vector<4xf32>) {
2149+
// expected-error @+2 {{'vector.store' op memref strides must be non-negative}}
2150+
%c0 = arith.constant 0 : index
2151+
vector.store %val, %src[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<4xf32>
2152+
return
2153+
}
2154+
2155+
// -----
2156+
21482157
// Verify that vector.bitcast rejects vectors with i0 (zero-bitwidth) element type.
21492158
func.func @bitcast_i0(%a: vector<4xi0>) -> vector<4xi0> {
21502159
// expected-error @+1 {{'vector.bitcast' op operand #0 must be vector of non-zero-bitwidth type values, but got 'vector<4xi0>'}}
@@ -2198,3 +2207,12 @@ func.func @scan_i0(%a: vector<4xi0>, %init: vector<1xi0>) -> (vector<4xi0>, vect
21982207
vector<4xi0>, vector<1xi0>
21992208
return %0#0, %0#1 : vector<4xi0>, vector<1xi0>
22002209
}
2210+
2211+
// -----
2212+
2213+
func.func @load_negative_stride(%src: memref<100x100xf32, strided<[-100, 1]>>) -> vector<8xf32> {
2214+
// expected-error @+2 {{'vector.load' op memref strides must be non-negative}}
2215+
%c0 = arith.constant 0 : index
2216+
%v = vector.load %src[%c0, %c0] : memref<100x100xf32, strided<[-100, 1]>>, vector<8xf32>
2217+
return %v : vector<8xf32>
2218+
}

0 commit comments

Comments
 (0)