[Mlir-commits] [mlir] [mlir][vector] Mask a write that fills its dims but starts at a non-zero index (PR #233243)
Dhairyashil R G
llvmlistbot at llvm.org
Sun Oct 18 08:18:00 PDT 2026
https://github.com/dhairyashilRG created https://github.com/llvm/llvm-project/pull/233243
`createWriteOrMaskedWrite` skipped the mask when the vector shape matched the destination dims it writes to. It did not look at the write indices, so a write that starts at a non-zero index was left unmasked and marked `in_bounds`, although it runs past the end of the destination.
For example, vectorizing this with `vector_sizes [8]`:
```mlir
%r = tensor.insert_slice %src into %dest[%offset] [%size] [1]
: tensor<?xi32> into tensor<8xi32>
```
gave an unmasked `vector.transfer_write` of 8 elements at `%offset`. The mask is now skipped only when the write also starts at 0, so this write is masked with `8 - %offset`.
Of the in-tree callers that mask, only `tensor.insert_slice` vectorization passes write indices, so nothing else changes.
Part of #227821.
Assisted-by: Claude
>From b342db9a363917fe36e3c4051f8b05d82eebb4d6 Mon Sep 17 00:00:00 2001
From: Dhairyashil R G <dhairyashil25 at gmail.com>
Date: Sat, 3 Oct 2026 11:51:40 +0530
Subject: [PATCH] [mlir][vector] Mask a write that fills its dims but starts at
a non-zero index
`createWriteOrMaskedWrite` left a write unmasked when the vector shape matched
the destination dims it writes to, without looking at the write indices. A
write that starts at a non-zero index then runs past the end of the
destination.
For example, vectorizing this with `vector_sizes [8]`:
%r = tensor.insert_slice %src into %dest[2] [6] [1]
: tensor<6xi32> into tensor<8xi32>
gave an unmasked `vector.transfer_write` of 8 elements at index 2, which also
writes `%dest[8]` and `%dest[9]`. The mask is now skipped only when the write
starts at 0 in those dims, so this write is masked to its first 6 elements.
Of the in-tree callers that mask, only `tensor.insert_slice` vectorization
passes write indices, so nothing else changes.
Part of #227821.
Assisted-by: Claude
---
mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp | 8 +++-
.../Linalg/vectorization/insert-slice.mlir | 38 +++++++++++++++++++
2 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp b/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp
index 14a6571549897..c69edef936ad4 100644
--- a/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp
+++ b/mlir/lib/Dialect/Vector/Utils/VectorUtils.cpp
@@ -591,8 +591,12 @@ Operation *vector::createWriteOrMaskedWrite(OpBuilder &builder, Location loc,
: AffineMap::getMinorIdentityMap(destRank, vecToStoreRank,
builder.getContext());
- // Check if masking is needed. If not, exit.
- if (llvm::equal(vecToStoreShape, applyPermutationMap(map, destShape)))
+ // A write that starts at 0 and fills the dims it writes to needs no mask.
+ bool startsAtZero = llvm::all_of(map.getResults(), [&](AffineExpr expr) {
+ return isZeroInteger(writeIndices[cast<AffineDimExpr>(expr).getPosition()]);
+ });
+ if (startsAtZero &&
+ llvm::equal(vecToStoreShape, applyPermutationMap(map, destShape)))
return write;
// Compute the mask and mask the write Op.
diff --git a/mlir/test/Dialect/Linalg/vectorization/insert-slice.mlir b/mlir/test/Dialect/Linalg/vectorization/insert-slice.mlir
index 319095f97e151..639068f588124 100644
--- a/mlir/test/Dialect/Linalg/vectorization/insert-slice.mlir
+++ b/mlir/test/Dialect/Linalg/vectorization/insert-slice.mlir
@@ -328,6 +328,44 @@ func.func private @insert_slice_constant_non_zero_offset(
// -----
+// The vector shape equals the destination shape, but the insert index for dim 1
+// is 2: only 6 elements are left in that dim, so the write needs a mask.
+
+func.func private @insert_slice_vector_matches_dest_non_zero_offset(
+ %source: tensor<4x6xi32>, %dest: tensor<4x8xi32>) -> tensor<4x8xi32> {
+ %res = tensor.insert_slice %source into %dest[0, 2] [4, 6] [1, 1] : tensor<4x6xi32> into tensor<4x8xi32>
+ return %res : tensor<4x8xi32>
+}
+
+// CHECK-LABEL: func.func private @insert_slice_vector_matches_dest_non_zero_offset(
+// CHECK-SAME: %[[SRC:.*]]: tensor<4x6xi32>,
+// CHECK-SAME: %[[DEST:.*]]: tensor<4x8xi32>) -> tensor<4x8xi32> {
+// CHECK: %[[PAD:.*]] = arith.constant 0 : i32
+// CHECK: %[[C_0:.*]] = arith.constant 0 : index
+// CHECK: %[[C_0_1:.*]] = arith.constant 0 : index
+// CHECK: %[[C_4:.*]] = arith.constant 4 : index
+// CHECK: %[[C_6:.*]] = arith.constant 6 : index
+// CHECK: %[[MASK_READ:.*]] = vector.create_mask %[[C_4]], %[[C_6]] : vector<4x8xi1>
+// CHECK: %[[READ:.*]] = vector.mask %[[MASK_READ]] { vector.transfer_read %[[SRC]][%[[C_0_1]], %[[C_0_1]]], %[[PAD]] {in_bounds = [true, true]} : tensor<4x6xi32>, vector<4x8xi32> } : vector<4x8xi1> -> vector<4x8xi32>
+// CHECK: %[[C_0_2:.*]] = arith.constant 0 : index
+// CHECK: %[[C_2:.*]] = arith.constant 2 : index
+// CHECK: %[[C_4_1:.*]] = arith.constant 4 : index
+// CHECK: %[[C_8:.*]] = arith.constant 8 : index
+// CHECK: %[[C_6_1:.*]] = arith.constant 6 : index
+// CHECK: %[[MASK_WRITE:.*]] = vector.create_mask %[[C_4_1]], %[[C_6_1]] : vector<4x8xi1>
+// CHECK: %[[RES:.*]] = vector.mask %[[MASK_WRITE]] { vector.transfer_write %[[READ]], %[[DEST]][%[[C_0_2]], %[[C_2]]] {in_bounds = [true, true]} : vector<4x8xi32>, tensor<4x8xi32> } : vector<4x8xi1> -> tensor<4x8xi32>
+// CHECK: return %[[RES]] : tensor<4x8xi32>
+
+ module attributes {transform.with_named_sequence} {
+ transform.named_sequence @__transform_main(%arg0: !transform.any_op {transform.readonly}) {
+ %0 = transform.structured.match ops{["tensor.insert_slice"]} in %arg0 : (!transform.any_op) -> !transform.any_op
+ transform.structured.vectorize %0 vector_sizes [4, 8] : !transform.any_op
+ transform.yield
+ }
+ }
+
+// -----
+
// One of the destination dimensions is dynamic and the the corresponding
// insert index is != 0. Make sure that the mask is computed correctly (note arith.subi in the output).
More information about the Mlir-commits
mailing list