Skip to content

[mlir] ViewLikeInterface - verify ranks in verifyOffsetSizeAndStrideOp #147926

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

amrami
Copy link
Contributor

@amrami amrami commented Jul 10, 2025

getMixedOffsets() calls getMixedValues() with static_offsets and offsets. It is assumed that the number of dynamic offsets in static_offsets equals the rank of offsets. Otherwise, we fail on assert when trying to access an array out of its bounds.
The same applies to getMixedStrides() and getMixedOffsets().

A verification of this assumption is added to verifyOffsetSizeAndStrideOp() and a clear assert is added in getMixedValues().

@llvmbot
Copy link
Member

llvmbot commented Jul 10, 2025

@llvm/pr-subscribers-mlir-memref

@llvm/pr-subscribers-mlir

Author: Maya Amrami (amrami)

Changes

getMixedOffsets() calls getMixedValues() with static_offsets and offsets. It is assumed that the number of dynamic offsets in static_offsets equals the rank of offsets. Otherwise, we fail on assert when trying to access an array out of its bounds.
The same applies to getMixedStrides() and getMixedOffsets().

A verification of this assumption is added to verifyOffsetSizeAndStrideOp() and a clear assert is added in getMixedValues().


Full diff: https://github.com/llvm/llvm-project/pull/147926.diff

3 Files Affected:

  • (modified) mlir/lib/Dialect/Utils/StaticValueUtils.cpp (+5-1)
  • (modified) mlir/lib/Interfaces/ViewLikeInterface.cpp (+26)
  • (modified) mlir/test/Dialect/MemRef/invalid.mlir (+12)
diff --git a/mlir/lib/Dialect/Utils/StaticValueUtils.cpp b/mlir/lib/Dialect/Utils/StaticValueUtils.cpp
index 1cded38c4419e..af494b99cb4cb 100644
--- a/mlir/lib/Dialect/Utils/StaticValueUtils.cpp
+++ b/mlir/lib/Dialect/Utils/StaticValueUtils.cpp
@@ -181,12 +181,16 @@ bool isEqualConstantIntOrValueArray(ArrayRef<OpFoldResult> ofrs1,
   return true;
 }
 
-/// Return a vector of OpFoldResults with the same size a staticValues, but all
+/// Return a vector of OpFoldResults with the same size as staticValues, but all
 /// elements for which ShapedType::isDynamic is true, will be replaced by
 /// dynamicValues.
 SmallVector<OpFoldResult> getMixedValues(ArrayRef<int64_t> staticValues,
                                          ValueRange dynamicValues,
                                          MLIRContext *context) {
+  assert(llvm::count_if(staticValues, ShapedType::isDynamic) ==
+                 dynamicValues.size() &&
+             "expected the rank of dynamic values to match the number of"
+             " values known to be dynamic";);
   SmallVector<OpFoldResult> res;
   res.reserve(staticValues.size());
   unsigned numDynamic = 0;
diff --git a/mlir/lib/Interfaces/ViewLikeInterface.cpp b/mlir/lib/Interfaces/ViewLikeInterface.cpp
index 3112da9ef182a..677d7dcc1b135 100644
--- a/mlir/lib/Interfaces/ViewLikeInterface.cpp
+++ b/mlir/lib/Interfaces/ViewLikeInterface.cpp
@@ -94,6 +94,32 @@ SliceBoundsVerificationResult mlir::verifyInBoundsSlice(
 
 LogicalResult
 mlir::detail::verifyOffsetSizeAndStrideOp(OffsetSizeAndStrideOpInterface op) {
+  // A dynamic size is represented as ShapedType::kDynamic in `static_sizes`.
+  // Its corresponding Value appears in `sizes`. Thus, the number of dynamic
+  // dimensions in `static_sizes` must equal the rank of `sizes`.
+  // The same applies to strides and offsets.
+  unsigned int numDynamicDims =
+      llvm::count_if(op.getStaticSizes(), ShapedType::isDynamic);
+  if (numDynamicDims != op.getSizes().size()) {
+    return op->emitError("expected sizes rank to match the number of dynamic "
+                         "dimensions (")
+           << numDynamicDims << " vs " << op.getSizes().size() << ")";
+  }
+  unsigned int numDynamicStrides =
+      llvm::count_if(op.getStaticStrides(), ShapedType::isDynamic);
+  if (numDynamicStrides != op.getStrides().size()) {
+    return op->emitError("expected strides rank to match the number of dynamic "
+                         "dimensions (")
+           << numDynamicStrides << " vs " << op.getStrides().size() << ")";
+  }
+  unsigned int numDynamicOffsets =
+      llvm::count_if(op.getStaticOffsets(), ShapedType::isDynamic);
+  if (numDynamicOffsets != op.getOffsets().size()) {
+    return op->emitError("expected offsets rank to match the number of dynamic "
+                         "dimensions (")
+           << numDynamicOffsets << " vs " << op.getOffsets().size() << ")";
+  }
+
   std::array<unsigned, 3> maxRanks = op.getArrayAttrMaxRanks();
   // Offsets can come in 2 flavors:
   //   1. Either single entry (when maxRanks == 1).
diff --git a/mlir/test/Dialect/MemRef/invalid.mlir b/mlir/test/Dialect/MemRef/invalid.mlir
index 704cdaf838f45..43d0362b78e1d 100644
--- a/mlir/test/Dialect/MemRef/invalid.mlir
+++ b/mlir/test/Dialect/MemRef/invalid.mlir
@@ -658,6 +658,18 @@ func.func @invalid_subview(%arg0 : index, %arg1 : index, %arg2 : index) {
 
 // -----
 
+// This test is not written in the op's assembly format, to reproduce a mismatch
+// between the rank of static_offsets and the number of Values sent as the
+// dynamic offsets.
+func.func @invalid_subview(%arg0 : memref<?x128xi8, 1>) {
+  %0 = memref.alloc() :memref<1xf32>
+  // expected-error@+1 {{expected offsets rank to match the number of dynamic dimensions (1 vs 0)}}
+  "memref.subview"(%0) <{operandSegmentSizes = array<i32: 1, 0, 0, 0>, static_offsets = array<i64: -9223372036854775808>, static_sizes = array<i64: 1>, static_strides = array<i64: 1>}> : (memref<1xf32>) -> memref<1xf32, strided<[1], offset: ?>>
+  return
+}
+
+// -----
+
 func.func @invalid_subview(%arg0 : index, %arg1 : index, %arg2 : index) {
   %0 = memref.alloc() : memref<8x16x4xf32>
   // expected-error@+1 {{expected mixed sizes rank to match mixed strides rank (3 vs 2) so the rank of the result type is well-formed}}

@amrami amrami force-pushed the mamrami/master/verify_addition_viewLike branch 2 times, most recently from 27248ee to 6568205 Compare July 10, 2025 09:42
getMixedOffsets() calls getMixedValues() with `static_offsets` and `offsets`.
It is assumed that the number of dynamic offsets in `static_offsets` equals
the rank of `offsets`. Otherwise, we fail on assert when trying to
access an array out of its bounds.
The same applies to getMixedStrides() and getMixedOffsets().

A verification of this assumption is added to verifyOffsetSizeAndStrideOp()
and a clear assert is added in getMixedValues().
@amrami amrami force-pushed the mamrami/master/verify_addition_viewLike branch from 6568205 to 41481df Compare July 10, 2025 09:45
/// elements for which ShapedType::isDynamic is true, will be replaced by
/// dynamicValues.
SmallVector<OpFoldResult> getMixedValues(ArrayRef<int64_t> staticValues,
ValueRange dynamicValues,
MLIRContext *context) {
assert(dynamicValues.size() ==
(unsigned)llvm::count_if(staticValues, ShapedType::isDynamic) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use C++ style static_cast and size_t.

unsigned int numDynamicDims =
llvm::count_if(op.getStaticSizes(), ShapedType::isDynamic);
if (op.getSizes().size() != numDynamicDims) {
return op->emitError("expected sizes rank to match the number of dynamic "
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I wouldn't use the term "rank" because users may think of the rank of the source

e.g.:

expected the number of 'sizes' to match the number of dynamic entries in 'static_sizes'

unsigned int numDynamicStrides =
llvm::count_if(op.getStaticStrides(), ShapedType::isDynamic);
if (op.getStrides().size() != numDynamicStrides) {
return op->emitError("expected strides rank to match the number of dynamic "
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

unsigned int numDynamicOffsets =
llvm::count_if(op.getStaticOffsets(), ShapedType::isDynamic);
if (op.getOffsets().size() != numDynamicOffsets) {
return op->emitError("expected offsets rank to match the number of dynamic "
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

// Its corresponding Value appears in `sizes`. Thus, the number of dynamic
// dimensions in `static_sizes` must equal the rank of `sizes`.
// The same applies to strides and offsets.
unsigned int numDynamicDims =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto or size_t?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants