[Mlir-commits] [mlir] [mlir][vector] Add opt-in `inbounds`/`nneg` flags to `vector.load`/`vector.store` (PR #202118)
Federico Bruzzone
llvmlistbot at llvm.org
Tue Jun 16 23:34:32 PDT 2026
FedericoBruzzone wrote:
> `%v = vector.load %flip[%c5] : memref<100xf32, strided<[-1], offset: 1000>>, vector<1xf32>`
>
> I'm not sure that's valid inside the LLVM lowering - don't we restrict to trailing unit strides?
Sorry for my ignorance but I don't know how to answer this question due to lack of experience, I'm new :'D (maybe @banach-space does).
By "restrict" do you mean that it should panic via an assertion (it's an invariant that the user must respect) or does MLIR just stay conservative?
BTW, to provide a concrete view of the patch I'll submit is the following:
With the following pipeline:
```bash
mlir-opt memref_neg_stride.mlir \
-finalize-memref-to-llvm \
-convert-func-to-llvm \
-reconcile-unrealized-casts 2>&1
```
and the following code:
```mlir
func.func @memref_load_neg_stride(%base: memref<2000xf32>) -> f32 {
%flip = memref.reinterpret_cast %base to
offset: [1000], sizes: [100], strides: [-1]
: memref<2000xf32> to memref<100xf32, strided<[-1], offset: 1000>>
%c5 = arith.constant 5 : index
%v = memref.load %flip[%c5] : memref<100xf32, strided<[-1], offset: 1000>>
return %v : f32
}
```
we are currently emitting:
<details>
<summary>Unfold me :D</summary>
```mlir
module {
llvm.func @memref_load_neg_stride(%arg0: !llvm.ptr, %arg1: !llvm.ptr, %arg2: i64, %arg3: i64, %arg4: i64) -> f32 {
%0 = llvm.mlir.poison : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%1 = llvm.insertvalue %arg0, %0[0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%2 = llvm.insertvalue %arg1, %1[1] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%3 = llvm.insertvalue %arg2, %2[2] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%4 = llvm.insertvalue %arg3, %3[3, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%5 = llvm.insertvalue %arg4, %4[4, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%6 = llvm.mlir.poison : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%7 = llvm.extractvalue %5[0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%8 = llvm.extractvalue %5[1] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%9 = llvm.insertvalue %7, %6[0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%10 = llvm.insertvalue %8, %9[1] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%11 = llvm.mlir.constant(1000 : index) : i64
%12 = llvm.insertvalue %11, %10[2] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%13 = llvm.mlir.constant(100 : index) : i64
%14 = llvm.insertvalue %13, %12[3, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%15 = llvm.mlir.constant(-1 : index) : i64
%16 = llvm.insertvalue %15, %14[4, 0] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%c5 = arith.constant 5 : index
%17 = builtin.unrealized_conversion_cast %c5 : index to i64
%18 = llvm.extractvalue %16[1] : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)>
%19 = llvm.mlir.constant(1000 : index) : i64
%20 = llvm.getelementptr %18[%19] : (!llvm.ptr, i64) -> !llvm.ptr, f32
%21 = llvm.mlir.constant(-1 : index) : i64
%22 = llvm.mul %17, %21 overflow<nsw, nuw> : i64
%23 = llvm.getelementptr inbounds|nuw %20[%22] : (!llvm.ptr, i64) -> !llvm.ptr, f32
%24 = llvm.load %23 : !llvm.ptr -> f32
llvm.return %24 : f32
}
}
```
<details>
The code with `vector.load` **already fixed** (does not emit inbound/nuw inconditionally).
But this is out of scope for now.
https://github.com/llvm/llvm-project/pull/202118
More information about the Mlir-commits
mailing list