[Mlir-commits] [mlir] [mlir][memref] Fix runtime verification of expand_shape (PR #206125)
Longsheng Mou
llvmlistbot at llvm.org
Fri Jun 26 09:28:20 PDT 2026
https://github.com/CoTinker created https://github.com/llvm/llvm-project/pull/206125
The original runtime verification only multiplied static output dimensions per reassociation group and checked `srcDim % staticProduct` == 0, which failed to catch invalid dynamic dimension values.
The new verification uses getMixedOutputShape() to get all output dimensions (static and dynamic), computes their product, and asserts `product == srcDim`. Fixes #205981.
Assisted-by: MiMo-V2.5-Pro
>From 484e054a77c1c1d7ce14e824eb7470747660f54b Mon Sep 17 00:00:00 2001
From: Longsheng Mou <longshengmou at gmail.com>
Date: Sat, 27 Jun 2026 00:10:32 +0800
Subject: [PATCH] [mlir][memref] Fix runtime verification of expand_shape
The original runtime verification only multiplied static output dimensions per
reassociation group and checked `srcDim % staticProduct` == 0, which failed to catch
invalid dynamic dimension values.
The new verification uses getMixedOutputShape() to get all output dimensions (static and
dynamic), computes their product, and asserts `product == srcDim`.
---
.../Transforms/RuntimeOpVerification.cpp | 40 ++++-----
.../Dialect/MemRef/runtime-verification.mlir | 12 +--
.../expand-shape-runtime-verification.mlir | 86 +++++++++++++++++++
3 files changed, 107 insertions(+), 31 deletions(-)
create mode 100644 mlir/test/Integration/Dialect/MemRef/expand-shape-runtime-verification.mlir
diff --git a/mlir/lib/Dialect/MemRef/Transforms/RuntimeOpVerification.cpp b/mlir/lib/Dialect/MemRef/Transforms/RuntimeOpVerification.cpp
index d7fdedce415d9..8f49ed471fbf7 100644
--- a/mlir/lib/Dialect/MemRef/Transforms/RuntimeOpVerification.cpp
+++ b/mlir/lib/Dialect/MemRef/Transforms/RuntimeOpVerification.cpp
@@ -362,37 +362,27 @@ struct ExpandShapeOpInterface
generateErrorMessage) const {
auto expandShapeOp = cast<ExpandShapeOp>(op);
- // Verify that the expanded dim sizes are a product of the collapsed dim
- // size.
+ SmallVector<OpFoldResult> outputShape = expandShapeOp.getMixedOutputShape();
+
+ // Verify that the product of output dim sizes in each reassociation group
+ // equals the corresponding input dim size.
for (const auto &it :
llvm::enumerate(expandShapeOp.getReassociationIndices())) {
Value srcDimSz =
DimOp::create(builder, loc, expandShapeOp.getSrc(), it.index());
- int64_t groupSz = 1;
- bool foundDynamicDim = false;
- for (int64_t resultDim : it.value()) {
- if (expandShapeOp.getResultType().isDynamicDim(resultDim)) {
- // Keep this assert here in case the op is extended in the future.
- assert(!foundDynamicDim &&
- "more than one dynamic dim found in reassoc group");
- (void)foundDynamicDim;
- foundDynamicDim = true;
- continue;
- }
- groupSz *= expandShapeOp.getResultType().getDimSize(resultDim);
+ Value groupProduct = getValueOrCreateConstantIndexOp(
+ builder, loc, outputShape[it.value().front()]);
+ for (int64_t resultDim : llvm::drop_begin(it.value())) {
+ Value dimSz = getValueOrCreateConstantIndexOp(builder, loc,
+ outputShape[resultDim]);
+ groupProduct = arith::MulIOp::create(builder, loc, groupProduct, dimSz);
}
- Value staticResultDimSz =
- arith::ConstantIndexOp::create(builder, loc, groupSz);
- // staticResultDimSz must divide srcDimSz evenly.
- Value mod =
- arith::RemSIOp::create(builder, loc, srcDimSz, staticResultDimSz);
- Value isModZero = arith::CmpIOp::create(
- builder, loc, arith::CmpIPredicate::eq, mod,
- arith::ConstantIndexOp::create(builder, loc, 0));
+ Value isEqual = arith::CmpIOp::create(
+ builder, loc, arith::CmpIPredicate::eq, groupProduct, srcDimSz);
cf::AssertOp::create(
- builder, loc, isModZero,
- generateErrorMessage(op, "static result dims in reassoc group do not "
- "divide src dim evenly"));
+ builder, loc, isEqual,
+ generateErrorMessage(op, "product of output dims in reassoc group "
+ "does not equal input dim"));
}
}
};
diff --git a/mlir/test/Dialect/MemRef/runtime-verification.mlir b/mlir/test/Dialect/MemRef/runtime-verification.mlir
index 2198f0032aff5..f449ac6e3b30c 100644
--- a/mlir/test/Dialect/MemRef/runtime-verification.mlir
+++ b/mlir/test/Dialect/MemRef/runtime-verification.mlir
@@ -2,12 +2,12 @@
// CHECK-LABEL: func @expand_shape(
// CHECK-SAME: %[[m:.*]]: memref<?xf32>
-// CHECK-SAME: %[[sz0:.*]]: index
-// CHECK-DAG: %[[c0:.*]] = arith.constant 0 : index
-// CHECK-DAG: %[[c5:.*]] = arith.constant 5 : index
-// CHECK-DAG: %[[dim:.*]] = memref.dim %[[m]], %[[c0]]
-// CHECK: %[[mod:.*]] = arith.remsi %[[dim]], %[[c5]]
-// CHECK: %[[cmpi:.*]] = arith.cmpi eq, %[[mod]], %[[c0]]
+// CHECK-SAME: %[[sz0:.*]]: index
+// CHECK: %[[c0:.*]] = arith.constant 0 : index
+// CHECK: %[[dim:.*]] = memref.dim %[[m]], %[[c0]]
+// CHECK: %[[c5:.*]] = arith.constant 5 : index
+// CHECK: %[[prod:.*]] = arith.muli %[[sz0]], %[[c5]]
+// CHECK: %[[cmpi:.*]] = arith.cmpi eq, %[[prod]], %[[dim]]
// CHECK: cf.assert %[[cmpi]], "ERROR: Runtime op verification failed
func.func @expand_shape(%m: memref<?xf32>, %sz0: index) -> memref<?x5xf32> {
%0 = memref.expand_shape %m [[0, 1]] output_shape [%sz0, 5] : memref<?xf32> into memref<?x5xf32>
diff --git a/mlir/test/Integration/Dialect/MemRef/expand-shape-runtime-verification.mlir b/mlir/test/Integration/Dialect/MemRef/expand-shape-runtime-verification.mlir
new file mode 100644
index 0000000000000..8ef8ade1808b3
--- /dev/null
+++ b/mlir/test/Integration/Dialect/MemRef/expand-shape-runtime-verification.mlir
@@ -0,0 +1,86 @@
+// RUN: mlir-opt %s -generate-runtime-verification \
+// RUN: -lower-affine \
+// RUN: -test-cf-assert \
+// RUN: -convert-scf-to-cf \
+// RUN: -convert-to-llvm | \
+// RUN: mlir-runner -e main -entry-point-result=void \
+// RUN: -shared-libs=%mlir_runner_utils 2>&1 | \
+// RUN: FileCheck %s
+
+// RUN: mlir-opt %s -generate-runtime-verification \
+// RUN: -lower-affine \
+// RUN: -test-cf-assert \
+// RUN: -convert-scf-to-cf \
+// RUN: -convert-to-llvm="allow-pattern-rollback=0" \
+// RUN: -reconcile-unrealized-casts | \
+// RUN: mlir-runner -e main -entry-point-result=void \
+// RUN: -shared-libs=%mlir_runner_utils 2>&1 | \
+// RUN: FileCheck %s
+
+// Dynamic source into dynamic target.
+func.func @expand_shape_dynamic(%m: memref<?xf32>, %sz0: index, %sz1: index) {
+ %0 = memref.expand_shape %m [[0, 1]] output_shape [%sz0, %sz1]
+ : memref<?xf32> into memref<?x?xf32>
+ return
+}
+
+// Dynamic source into mixed static/dynamic target.
+func.func @expand_shape_mixed(%m: memref<?xf32>, %sz0: index) {
+ %0 = memref.expand_shape %m [[0, 1]] output_shape [%sz0, 5]
+ : memref<?xf32> into memref<?x5xf32>
+ return
+}
+
+// Multiple reassociation groups: 2D -> 3D.
+func.func @expand_shape_multi_group(%m: memref<?x?xf32>, %sz0: index, %sz1: index) {
+ %0 = memref.expand_shape %m [[0], [1, 2]] output_shape [%sz0, %sz1, 4]
+ : memref<?x?xf32> into memref<?x?x4xf32>
+ return
+}
+
+func.func @main() {
+ %alloca_10 = memref.alloca() : memref<10xf32>
+ %alloca_10_dyn = memref.cast %alloca_10 : memref<10xf32> to memref<?xf32>
+
+ %alloca_3x20 = memref.alloca() : memref<3x20xf32>
+ %alloca_3x20_dyn = memref.cast %alloca_3x20 : memref<3x20xf32> to memref<?x?xf32>
+
+ %2 = arith.constant 2 : index
+ %3 = arith.constant 3 : index
+ %4 = arith.constant 4 : index
+ %5 = arith.constant 5 : index
+
+ // Product 3*5=15 does not equal input dim 10.
+ // CHECK: ERROR: Runtime op verification failed
+ // CHECK-NEXT: memref.expand_shape %{{.*}} {{\[}}[0, 1]{{\]}} output_shape [%{{.*}}, %{{.*}}] : memref<?xf32> into memref<?x?xf32>
+ // CHECK-NEXT: ^ product of output dims in reassoc group does not equal input dim
+ // CHECK-NEXT: Location: loc({{.*}})
+ func.call @expand_shape_dynamic(%alloca_10_dyn, %3, %5)
+ : (memref<?xf32>, index, index) -> ()
+
+ // Product 2*5=10 equals input dim 10. No error.
+ // CHECK-NOT: ERROR: Runtime op verification failed
+ func.call @expand_shape_dynamic(%alloca_10_dyn, %2, %5)
+ : (memref<?xf32>, index, index) -> ()
+
+ // Product 4*5=20 does not equal input dim 10.
+ // CHECK: ERROR: Runtime op verification failed
+ // CHECK-NEXT: memref.expand_shape %{{.*}} {{\[}}[0, 1]{{\]}} output_shape [%{{.*}}, 5] : memref<?xf32> into memref<?x5xf32>
+ // CHECK-NEXT: ^ product of output dims in reassoc group does not equal input dim
+ // CHECK-NEXT: Location: loc({{.*}})
+ func.call @expand_shape_mixed(%alloca_10_dyn, %4)
+ : (memref<?xf32>, index) -> ()
+
+ // Product 2*5=10 equals input dim 10. No error.
+ // CHECK-NOT: ERROR: Runtime op verification failed
+ func.call @expand_shape_mixed(%alloca_10_dyn, %2)
+ : (memref<?xf32>, index) -> ()
+
+ // Group 0: dim 0 -> [dim 0] product 3, input dim 0 = 3 -> OK.
+ // Group 1: dim 1 -> [dim 1, dim 2] product 5*4=20, input dim 1 = 20 -> OK.
+ // CHECK-NOT: ERROR: Runtime op verification failed
+ func.call @expand_shape_multi_group(%alloca_3x20_dyn, %3, %5)
+ : (memref<?x?xf32>, index, index) -> ()
+
+ return
+}
More information about the Mlir-commits
mailing list