Skip to content

[mlir][memref] Fix invalid folds in ReinterpretCastOpConstantFolder for negative constants#189237

Merged
joker-eph merged 1 commit into
llvm:mainfrom
joker-eph:fix/issue-188407
Mar 30, 2026
Merged

[mlir][memref] Fix invalid folds in ReinterpretCastOpConstantFolder for negative constants#189237
joker-eph merged 1 commit into
llvm:mainfrom
joker-eph:fix/issue-188407

Conversation

@joker-eph

@joker-eph joker-eph commented Mar 29, 2026

Copy link
Copy Markdown
Contributor

ReinterpretCastOpConstantFolder could fold memref.reinterpret_cast
ops whose offset or sizes contain negative constants (e.g. -1 : index).

  • A negative constant size passed into ReinterpretCastOp::create reaches
    MemRefType::get, which asserts that all static dimension sizes are
    non-negative, causing a crash.

  • A negative constant offset produces an op with a static negative offset,
    which the ViewLikeInterface verifier then rejects ("expected offsets to
    be non-negative").

Fix by skipping the fold when any constant size or the offset is negative.
Negative strides are intentionally left foldable: they are valid in
strided MemRef layouts (e.g. for reverse iteration) and neither
MemRefType::get nor ViewLikeInterface places a non-negativity
constraint on strides.

Fixes #188407

Assisted-by: Claude Code

@llvmbot

llvmbot commented Mar 29, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-mlir-memref

Author: Mehdi Amini (joker-eph)

Changes

ReinterpretCastOpConstantFolder could fold memref.reinterpret_cast ops whose sizes contain negative constants (e.g. -1 : index). After calling getConstifiedMixedSizes() the folder passed these values into ReinterpretCastOp::create, which internally calls MemRefType::get and hits an assertion that all static dimension sizes are non-negative.

Fix by skipping the fold when any constant size is negative.

Fixes #188407

Assisted-by: Claude Code


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

2 Files Affected:

  • (modified) mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp (+8)
  • (modified) mlir/test/Dialect/MemRef/canonicalize.mlir (+21)
diff --git a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
index 404b2aacf1450..5354c628ae6db 100644
--- a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
+++ b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
@@ -2291,6 +2291,14 @@ struct ReinterpretCastOpConstantFolder
                        [](OpFoldResult ofr) { return isa<Attribute>(ofr); }))
       return failure();
 
+    // Do not fold if any size is a negative constant; MemRefType::get asserts
+    // non-negative static sizes.
+    for (OpFoldResult sizeOfr : sizes)
+      if (auto cst = getConstantIntValue(sizeOfr))
+        if (*cst < 0)
+          return rewriter.notifyMatchFailure(
+              op, "negative constant size is invalid");
+
     auto newReinterpretCast = ReinterpretCastOp::create(
         rewriter, op->getLoc(), op.getSource(), offsets[0], sizes, strides);
 
diff --git a/mlir/test/Dialect/MemRef/canonicalize.mlir b/mlir/test/Dialect/MemRef/canonicalize.mlir
index 92754dc919695..604da0592c93a 100644
--- a/mlir/test/Dialect/MemRef/canonicalize.mlir
+++ b/mlir/test/Dialect/MemRef/canonicalize.mlir
@@ -1287,6 +1287,27 @@ func.func @reinterpret_of_extract_strided_metadata_w_different_offset(%arg0 : me
 
 // -----
 
+// Check that reinterpret_cast with a negative constant size is not folded.
+// Folding would attempt to create a MemRefType with a negative static dimension,
+// which triggers an assertion in MemRefType::get (issue #188407).
+// CHECK-LABEL: func @reinterpret_cast_no_fold_negative_size
+//  CHECK-SAME: (%[[ARG:.*]]: memref<2x3xf32>)
+//       CHECK: %[[C0:.*]] = arith.constant 0 : index
+//       CHECK: %[[C1:.*]] = arith.constant 1 : index
+//       CHECK: %[[SZ:.*]] = arith.constant -1 : index
+//       CHECK: memref.reinterpret_cast %[[ARG]] to offset: [%[[C0]]], sizes: [%[[C1]], %[[SZ]]], strides: [%[[SZ]], %[[C1]]]
+func.func @reinterpret_cast_no_fold_negative_size(%arg0: memref<2x3xf32>) -> memref<?x?xf32, strided<[?, ?], offset: ?>> {
+  %c0 = arith.constant 0 : index
+  %c1 = arith.constant 1 : index
+  %sz = arith.constant -1 : index
+  %output = memref.reinterpret_cast %arg0 to
+            offset: [%c0], sizes: [%c1, %sz], strides: [%sz, %c1]
+            : memref<2x3xf32> to memref<?x?xf32, strided<[?, ?], offset: ?>>
+  return %output : memref<?x?xf32, strided<[?, ?], offset: ?>>
+}
+
+// -----
+
 func.func @canonicalize_rank_reduced_subview(%arg0 : memref<8x?xf32>,
     %arg1 : index) -> memref<?xf32, strided<[?], offset: ?>> {
   %c0 = arith.constant 0 : index

@llvmbot

llvmbot commented Mar 29, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-mlir

Author: Mehdi Amini (joker-eph)

Changes

ReinterpretCastOpConstantFolder could fold memref.reinterpret_cast ops whose sizes contain negative constants (e.g. -1 : index). After calling getConstifiedMixedSizes() the folder passed these values into ReinterpretCastOp::create, which internally calls MemRefType::get and hits an assertion that all static dimension sizes are non-negative.

Fix by skipping the fold when any constant size is negative.

Fixes #188407

Assisted-by: Claude Code


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

2 Files Affected:

  • (modified) mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp (+8)
  • (modified) mlir/test/Dialect/MemRef/canonicalize.mlir (+21)
diff --git a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
index 404b2aacf1450..5354c628ae6db 100644
--- a/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
+++ b/mlir/lib/Dialect/MemRef/IR/MemRefOps.cpp
@@ -2291,6 +2291,14 @@ struct ReinterpretCastOpConstantFolder
                        [](OpFoldResult ofr) { return isa<Attribute>(ofr); }))
       return failure();
 
+    // Do not fold if any size is a negative constant; MemRefType::get asserts
+    // non-negative static sizes.
+    for (OpFoldResult sizeOfr : sizes)
+      if (auto cst = getConstantIntValue(sizeOfr))
+        if (*cst < 0)
+          return rewriter.notifyMatchFailure(
+              op, "negative constant size is invalid");
+
     auto newReinterpretCast = ReinterpretCastOp::create(
         rewriter, op->getLoc(), op.getSource(), offsets[0], sizes, strides);
 
diff --git a/mlir/test/Dialect/MemRef/canonicalize.mlir b/mlir/test/Dialect/MemRef/canonicalize.mlir
index 92754dc919695..604da0592c93a 100644
--- a/mlir/test/Dialect/MemRef/canonicalize.mlir
+++ b/mlir/test/Dialect/MemRef/canonicalize.mlir
@@ -1287,6 +1287,27 @@ func.func @reinterpret_of_extract_strided_metadata_w_different_offset(%arg0 : me
 
 // -----
 
+// Check that reinterpret_cast with a negative constant size is not folded.
+// Folding would attempt to create a MemRefType with a negative static dimension,
+// which triggers an assertion in MemRefType::get (issue #188407).
+// CHECK-LABEL: func @reinterpret_cast_no_fold_negative_size
+//  CHECK-SAME: (%[[ARG:.*]]: memref<2x3xf32>)
+//       CHECK: %[[C0:.*]] = arith.constant 0 : index
+//       CHECK: %[[C1:.*]] = arith.constant 1 : index
+//       CHECK: %[[SZ:.*]] = arith.constant -1 : index
+//       CHECK: memref.reinterpret_cast %[[ARG]] to offset: [%[[C0]]], sizes: [%[[C1]], %[[SZ]]], strides: [%[[SZ]], %[[C1]]]
+func.func @reinterpret_cast_no_fold_negative_size(%arg0: memref<2x3xf32>) -> memref<?x?xf32, strided<[?, ?], offset: ?>> {
+  %c0 = arith.constant 0 : index
+  %c1 = arith.constant 1 : index
+  %sz = arith.constant -1 : index
+  %output = memref.reinterpret_cast %arg0 to
+            offset: [%c0], sizes: [%c1, %sz], strides: [%sz, %c1]
+            : memref<2x3xf32> to memref<?x?xf32, strided<[?, ?], offset: ?>>
+  return %output : memref<?x?xf32, strided<[?, ?], offset: ?>>
+}
+
+// -----
+
 func.func @canonicalize_rank_reduced_subview(%arg0 : memref<8x?xf32>,
     %arg1 : index) -> memref<?xf32, strided<[?], offset: ?>> {
   %c0 = arith.constant 0 : index

@NexMing

NexMing commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

The size of a memref cannot be negative, the verifier should reject memref.reinterpret_cast operations with a statically known negative size.

I suspect memref should treat the shape size variable as an unsigned integer, where "-1" represents the maximum unsigned value.

@joker-eph

joker-eph commented Mar 30, 2026

Copy link
Copy Markdown
Contributor Author

These aren't attributes but dynamic values, the verifier cannot check across def-use chains: https://mlir.llvm.org/getting_started/DeveloperGuide/#ir-verifier

@matthias-springer matthias-springer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we want the same check for negative offsets and strides?

…or negative constants

`ReinterpretCastOpConstantFolder` could fold `memref.reinterpret_cast`
ops whose offset or sizes contain negative constants (e.g. `-1 : index`).

- A negative constant size passed into `ReinterpretCastOp::create` reaches
  `MemRefType::get`, which asserts that all static dimension sizes are
  non-negative, causing a crash.

- A negative constant offset produces an op with a static negative offset,
  which the `ViewLikeInterface` verifier then rejects ("expected offsets to
  be non-negative").

Fix by skipping the fold when any constant size or the offset is negative.
Negative strides are intentionally left foldable: they are valid in
strided MemRef layouts (e.g. for reverse iteration) and neither
`MemRefType::get` nor `ViewLikeInterface` places a non-negativity
constraint on strides.

Fixes llvm#188407

Assisted-by: Claude Code
@joker-eph joker-eph changed the title [mlir][memref] Fix crash in ReinterpretCastOpConstantFolder for negative sizes [mlir][memref] Fix invalid folds in ReinterpretCastOpConstantFolder for negative constants Mar 30, 2026
@joker-eph

Copy link
Copy Markdown
Contributor Author

offsets had the same problem, added fix/test for this. Negative Strides are legit I believe.

@joker-eph joker-eph enabled auto-merge (squash) March 30, 2026 12:29
@joker-eph joker-eph disabled auto-merge March 30, 2026 12:31
@joker-eph joker-eph enabled auto-merge (squash) March 30, 2026 12:35
@joker-eph joker-eph merged commit 79a7b57 into llvm:main Mar 30, 2026
10 checks passed
@llvm-ci

llvm-ci commented Mar 30, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder ppc64le-flang-rhel-clang running on ppc64le-flang-rhel-test while building mlir at step 5 "build-unified-tree".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/157/builds/45560

Here is the relevant piece of the build log for the reference
Step 5 (build-unified-tree) failure: build (failure) (timed out)
...
116.581 [4/11/126] Building CXX object flang-rt/lib/runtime/CMakeFiles/flang_rt.runtime.static.dir/sum.cpp.o
168.498 [4/10/127] Building CXX object flang-rt/lib/runtime/CMakeFiles/flang_rt.runtime.static.dir/reduction.cpp.o
208.973 [4/9/128] Building CXX object third-party/runtimes_gtest/CMakeFiles/runtimes_gtest.dir/googletest/src/gtest-all.cc.o
209.731 [3/9/129] Linking CXX static library /home/buildbots/llvm-external-buildbots/workers/ppc64le-flang-rhel-test/ppc64le-flang-rhel-clang-build/build/lib/libruntimes_gtest.a
210.478 [1/10/130] Linking CXX static library /home/buildbots/llvm-external-buildbots/workers/ppc64le-flang-rhel-test/ppc64le-flang-rhel-clang-build/build/lib/libruntimes_gtest_main.a
212.158 [1/9/131] Linking CXX shared library openmp/tools/omptest/libomptest.so
240.213 [1/8/132] Building CXX object flang-rt/lib/runtime/CMakeFiles/flang_rt.runtime.static.dir/tools.cpp.o
241.796 [1/7/133] Building CXX object flang-rt/lib/runtime/CMakeFiles/flang_rt.runtime.static.dir/reduce.cpp.o
416.079 [1/6/134] Building CXX object flang-rt/lib/runtime/CMakeFiles/flang_rt.runtime.static.dir/character.cpp.o
457.470 [1/5/135] Building CXX object flang-rt/lib/runtime/CMakeFiles/flang_rt.runtime.static.dir/dot-product.cpp.o
command timed out: 1200 seconds without output running [b'ninja'], attempting to kill
process killed by signal 9
program finished with exit code -1
elapsedTime=2696.679937

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

Projects

None yet

5 participants