[Mlir-commits] [mlir] 4098f56 - [mlir][vector] Don't fold in_bounds for negative constant indices (#219681)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Sun Sep 13 11:02:04 PDT 2026
Author: Dhairyashil R G
Date: 2026-09-13T19:01:59+01:00
New Revision: 4098f568c46e06b6df470111868b4c165dd80f4d
URL: https://github.com/llvm/llvm-project/commit/4098f568c46e06b6df470111868b4c165dd80f4d
DIFF: https://github.com/llvm/llvm-project/commit/4098f568c46e06b6df470111868b4c165dd80f4d.diff
LOG: [mlir][vector] Don't fold in_bounds for negative constant indices (#219681)
`isInBounds` checked only that the transfer *ends* inside the source:
return cstOp.value() + vectorSize <= sourceSize;
The `in_bounds` attribute promises more than that. Its definition in
VectorOps.td says accesses "(including the starting point)" may run
out-of-bounds when it is "false", so setting it to "true" is a claim
about the start of the transfer as well as its end.
With a negative constant index the two disagree. For
vector.transfer_read %m[-1] : memref<8xf32>, vector<4xf32>
`-1 + 4 <= 8` holds, so the fold set `in_bounds = [true]`, even though
element -1 is read from outside `%m`.
The same expression also overflows for a large enough index: at `index =
2^63 - 1` the addition wraps and the fold again reports `in_bounds =
[true]`. Computing the bound as `sourceSize - vectorSize` instead avoids
the addition entirely; both operands are static, non-negative dimension
sizes, so the subtraction cannot overflow.
This is a spec-conformance fix, not a live miscompile report. A negative
index into a memref is already undefined behaviour, and the masked
lowering would not have caught it either: the guard
`generateInBoundsCheck` emits in VectorToSCF is `memrefDim > index +
iv`, an upper-bound check only.
Adds three regression tests, each of which fails without the change.
Assisted-by: Claude
Added:
Modified:
mlir/lib/Dialect/Vector/IR/VectorOps.cpp
mlir/test/Dialect/Vector/canonicalize.mlir
Removed:
################################################################################
diff --git a/mlir/lib/Dialect/Vector/IR/VectorOps.cpp b/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
index 70342afa5fc4b..b77796a42232f 100644
--- a/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
+++ b/mlir/lib/Dialect/Vector/IR/VectorOps.cpp
@@ -5404,8 +5404,16 @@ static bool isInBounds(TransferOp op, int64_t resultIdx, int64_t indicesIdx) {
int64_t sourceSize = op.getShapedType().getDimSize(indicesIdx);
int64_t vectorSize = op.getVectorType().getDimSize(resultIdx);
-
- return cstOp.value() + vectorSize <= sourceSize;
+ // Largest index at which a full vector still fits. Computed as a subtraction
+ // rather than adding to the index, which could overflow. The subtraction is
+ // safe only because of the `isDynamicDim` early return above: `sourceSize`
+ // is a real static extent here, never `ShapedType::kDynamic`, which is
+ // `INT64_MIN` and would make this signed overflow.
+ int64_t maxStart = sourceSize - vectorSize;
+
+ // `in_bounds` guarantees that the transfer stays within the source *including
+ // its starting point*, so a negative index is not in bounds.
+ return *cstOp >= 0 && *cstOp <= maxStart;
}
template <typename TransferOp>
diff --git a/mlir/test/Dialect/Vector/canonicalize.mlir b/mlir/test/Dialect/Vector/canonicalize.mlir
index 90ef51c6d9c6a..6b271ee5cce19 100644
--- a/mlir/test/Dialect/Vector/canonicalize.mlir
+++ b/mlir/test/Dialect/Vector/canonicalize.mlir
@@ -1435,6 +1435,45 @@ func.func @no_fold_transfer_write_in_bounds_scalable(%m: memref<4xf32>, %v: vect
// -----
+// `-1 + 4 <= 8` holds, but `in_bounds` covers the starting point too, so this
+// must not fold.
+
+// CHECK-LABEL: func @no_fold_transfer_read_in_bounds_negative_index
+// CHECK: vector.transfer_read
+// CHECK-NOT: in_bounds
+// CHECK: : memref<8xf32>, vector<4xf32>
+func.func @no_fold_transfer_read_in_bounds_negative_index(%m: memref<8xf32>, %p: f32) -> vector<4xf32> {
+ %c-1 = arith.constant -1 : index
+ %v = vector.transfer_read %m[%c-1], %p : memref<8xf32>, vector<4xf32>
+ return %v : vector<4xf32>
+}
+
+// -----
+
+// CHECK-LABEL: func @no_fold_transfer_write_in_bounds_negative_index
+// CHECK: vector.transfer_write
+// CHECK-NOT: in_bounds
+// CHECK: : vector<4xf32>, memref<8xf32>
+func.func @no_fold_transfer_write_in_bounds_negative_index(%m: memref<8xf32>, %v: vector<4xf32>) {
+ %c-1 = arith.constant -1 : index
+ vector.transfer_write %v, %m[%c-1] : vector<4xf32>, memref<8xf32>
+ return
+}
+
+// -----
+
+// CHECK-LABEL: func @no_fold_transfer_read_in_bounds_index_overflow
+// CHECK: vector.transfer_read
+// CHECK-NOT: in_bounds
+// CHECK: : memref<8xf32>, vector<4xf32>
+func.func @no_fold_transfer_read_in_bounds_index_overflow(%m: memref<8xf32>, %p: f32) -> vector<4xf32> {
+ %c = arith.constant 9223372036854775807 : index
+ %v = vector.transfer_read %m[%c], %p : memref<8xf32>, vector<4xf32>
+ return %v : vector<4xf32>
+}
+
+// -----
+
// CHECK-LABEL: fold_vector_transfers
func.func @fold_vector_transfers(%A: memref<?x8xf32>) -> (vector<4x8xf32>, vector<4x9xf32>) {
%c0 = arith.constant 0 : index
More information about the Mlir-commits
mailing list