[Mlir-commits] [mlir] [mlir][ValueBounds] Skip analysis for identical slice components (PR #226894)

Ziliang Zhang llvmlistbot at llvm.org
Mon Sep 28 23:07:08 PDT 2026


https://github.com/ziliangzl updated https://github.com/llvm/llvm-project/pull/226894

>From 4df93794112769d12db071e5ed00c31d2e56b60b Mon Sep 17 00:00:00 2001
From: Ziliang Zhang <zzl.coding at gmail.com>
Date: Mon, 28 Sep 2026 13:46:30 +0800
Subject: [PATCH 1/3] [mlir][ValueBounds] Skip analysis for identical slice
 components

One-Shot Bufferize repeatedly compares subset slices whose offsets, sizes, and strides often reuse the same SSA values and attributes. Avoid constructing a ValueBounds constraint set when the two OpFoldResults are already identical, while preserving the existing solver fallback for distinct values.
---
 .../lib/Interfaces/ValueBoundsOpInterface.cpp |  8 +++
 mlir/unittests/Interfaces/CMakeLists.txt      |  2 +
 .../Interfaces/ValueBoundsOpInterfaceTest.cpp | 61 +++++++++++++++++++
 3 files changed, 71 insertions(+)
 create mode 100644 mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp

diff --git a/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp b/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp
index d6d3b4d43cb55..0d22b1d39c14e 100644
--- a/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp
+++ b/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp
@@ -955,6 +955,10 @@ FailureOr<bool> ValueBoundsConstraintSet::areEquivalentSlices(
   // "failure".
   for (auto [offset1, offset2] :
        llvm::zip_equal(slice1.getMixedOffsets(), slice2.getMixedOffsets())) {
+    // Avoid constructing a constraint set for values/attributes that are
+    // already known to be identical.
+    if (offset1 == offset2)
+      continue;
     FailureOr<bool> equal = areEqual(offset1, offset2);
     if (failed(equal))
       return failure();
@@ -963,6 +967,8 @@ FailureOr<bool> ValueBoundsConstraintSet::areEquivalentSlices(
   }
   for (auto [size1, size2] :
        llvm::zip_equal(slice1.getMixedSizes(), slice2.getMixedSizes())) {
+    if (size1 == size2)
+      continue;
     FailureOr<bool> equal = areEqual(size1, size2);
     if (failed(equal))
       return failure();
@@ -971,6 +977,8 @@ FailureOr<bool> ValueBoundsConstraintSet::areEquivalentSlices(
   }
   for (auto [stride1, stride2] :
        llvm::zip_equal(slice1.getMixedStrides(), slice2.getMixedStrides())) {
+    if (stride1 == stride2)
+      continue;
     FailureOr<bool> equal = areEqual(stride1, stride2);
     if (failed(equal))
       return failure();
diff --git a/mlir/unittests/Interfaces/CMakeLists.txt b/mlir/unittests/Interfaces/CMakeLists.txt
index 193edda6bf8d8..5cb2948c814cd 100644
--- a/mlir/unittests/Interfaces/CMakeLists.txt
+++ b/mlir/unittests/Interfaces/CMakeLists.txt
@@ -5,6 +5,7 @@ add_mlir_unittest(MLIRInterfacesTests
   MemorySlotUtilsTest.cpp
   SideEffectInterfacesTest.cpp
   InferTypeOpInterfaceTest.cpp
+  ValueBoundsOpInterfaceTest.cpp
 
   DEPENDS
   MLIRTestInterfaceIncGen
@@ -23,5 +24,6 @@ mlir_target_link_libraries(MLIRInterfacesTests
   MLIRMemorySlotUtils
   MLIRParser
   MLIRSideEffectInterfaces
+  MLIRValueBoundsOpInterface
 )
 target_link_libraries(MLIRInterfacesTests PRIVATE MLIRTestDialect)
diff --git a/mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp b/mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp
new file mode 100644
index 0000000000000..1a7de44ecc7da
--- /dev/null
+++ b/mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp
@@ -0,0 +1,61 @@
+//===- ValueBoundsOpInterfaceTest.cpp - ValueBounds unit tests -----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Interfaces/ValueBoundsOpInterface.h"
+
+#include <gtest/gtest.h>
+
+using namespace mlir;
+
+namespace {
+
+TEST(ValueBoundsOpInterfaceTest, EquivalentSlices) {
+  MLIRContext context;
+  context.loadDialect<func::FuncDialect>();
+  OpBuilder builder(&context);
+
+  auto function = func::FuncOp::create(
+      builder.getUnknownLoc(), "test",
+      builder.getFunctionType({builder.getIndexType(), builder.getIndexType()},
+                              {}));
+  Block *entryBlock = function.addEntryBlock();
+  Value dynamicValue = entryBlock->getArgument(0);
+  Value otherDynamicValue = entryBlock->getArgument(1);
+
+  OpFoldResult zero = builder.getIndexAttr(0);
+  OpFoldResult one = builder.getIndexAttr(1);
+  OpFoldResult four = builder.getIndexAttr(4);
+  OpFoldResult five = builder.getIndexAttr(5);
+
+  HyperrectangularSlice slice({zero, dynamicValue}, {four, dynamicValue},
+                              {one, one});
+  HyperrectangularSlice identicalSlice({zero, dynamicValue},
+                                       {four, dynamicValue}, {one, one});
+  FailureOr<bool> equivalent = ValueBoundsConstraintSet::areEquivalentSlices(
+      &context, slice, identicalSlice);
+  ASSERT_TRUE(succeeded(equivalent));
+  EXPECT_TRUE(*equivalent);
+
+  // Identical components must not hide a later non-equivalent component.
+  HyperrectangularSlice differentSlice({zero, dynamicValue},
+                                       {five, dynamicValue}, {one, one});
+  equivalent = ValueBoundsConstraintSet::areEquivalentSlices(
+      &context, slice, differentSlice);
+  ASSERT_TRUE(succeeded(equivalent));
+  EXPECT_FALSE(*equivalent);
+
+  // Distinct dynamic values continue to use the ValueBounds fallback.
+  HyperrectangularSlice unknownSlice({zero, otherDynamicValue},
+                                     {four, dynamicValue}, {one, one});
+  equivalent = ValueBoundsConstraintSet::areEquivalentSlices(
+      &context, slice, unknownSlice);
+  EXPECT_TRUE(failed(equivalent));
+}
+
+} // namespace

>From c3b2807b58c14cbfe8f0d2e92cb7b591314b0019 Mon Sep 17 00:00:00 2001
From: Ziliang Zhang <zzl.coding at gmail.com>
Date: Mon, 28 Sep 2026 14:07:02 +0800
Subject: [PATCH 2/3] [mlir][ValueBounds] Fix unit test formatting

---
 .../Interfaces/ValueBoundsOpInterfaceTest.cpp          | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp b/mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp
index 1a7de44ecc7da..1571ab924bf6f 100644
--- a/mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp
+++ b/mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "mlir/Dialect/Func/IR/FuncOps.h"
 #include "mlir/Interfaces/ValueBoundsOpInterface.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
 
 #include <gtest/gtest.h>
 
@@ -45,16 +45,16 @@ TEST(ValueBoundsOpInterfaceTest, EquivalentSlices) {
   // Identical components must not hide a later non-equivalent component.
   HyperrectangularSlice differentSlice({zero, dynamicValue},
                                        {five, dynamicValue}, {one, one});
-  equivalent = ValueBoundsConstraintSet::areEquivalentSlices(
-      &context, slice, differentSlice);
+  equivalent = ValueBoundsConstraintSet::areEquivalentSlices(&context, slice,
+                                                             differentSlice);
   ASSERT_TRUE(succeeded(equivalent));
   EXPECT_FALSE(*equivalent);
 
   // Distinct dynamic values continue to use the ValueBounds fallback.
   HyperrectangularSlice unknownSlice({zero, otherDynamicValue},
                                      {four, dynamicValue}, {one, one});
-  equivalent = ValueBoundsConstraintSet::areEquivalentSlices(
-      &context, slice, unknownSlice);
+  equivalent = ValueBoundsConstraintSet::areEquivalentSlices(&context, slice,
+                                                             unknownSlice);
   EXPECT_TRUE(failed(equivalent));
 }
 

>From fd7fd5cce87e9168ccac4ff265d2d64d9e7b5797 Mon Sep 17 00:00:00 2001
From: Ziliang Zhang <zzl.coding at gmail.com>
Date: Tue, 29 Sep 2026 14:06:48 +0800
Subject: [PATCH 3/3] [mlir][ValueBounds] Address slice identity review
 feedback

---
 .../lib/Interfaces/ValueBoundsOpInterface.cpp | 12 ++--
 .../one-shot-module-bufferize-analysis.mlir   | 23 +++++++
 mlir/unittests/Interfaces/CMakeLists.txt      |  2 -
 .../Interfaces/ValueBoundsOpInterfaceTest.cpp | 61 -------------------
 4 files changed, 27 insertions(+), 71 deletions(-)
 delete mode 100644 mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp

diff --git a/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp b/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp
index 0d22b1d39c14e..d998104e99f4a 100644
--- a/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp
+++ b/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp
@@ -872,6 +872,10 @@ FailureOr<bool> ValueBoundsConstraintSet::strongCompare(const Variable &lhs,
 
 FailureOr<bool> ValueBoundsConstraintSet::areEqual(const Variable &var1,
                                                    const Variable &var2) {
+  // Avoid constructing a constraint set for variables that are already known
+  // to be identical.
+  if (var1.map == var2.map && var1.mapOperands == var2.mapOperands)
+    return true;
   return strongCompare(var1, ComparisonOperator::EQ, var2);
 }
 
@@ -955,10 +959,6 @@ FailureOr<bool> ValueBoundsConstraintSet::areEquivalentSlices(
   // "failure".
   for (auto [offset1, offset2] :
        llvm::zip_equal(slice1.getMixedOffsets(), slice2.getMixedOffsets())) {
-    // Avoid constructing a constraint set for values/attributes that are
-    // already known to be identical.
-    if (offset1 == offset2)
-      continue;
     FailureOr<bool> equal = areEqual(offset1, offset2);
     if (failed(equal))
       return failure();
@@ -967,8 +967,6 @@ FailureOr<bool> ValueBoundsConstraintSet::areEquivalentSlices(
   }
   for (auto [size1, size2] :
        llvm::zip_equal(slice1.getMixedSizes(), slice2.getMixedSizes())) {
-    if (size1 == size2)
-      continue;
     FailureOr<bool> equal = areEqual(size1, size2);
     if (failed(equal))
       return failure();
@@ -977,8 +975,6 @@ FailureOr<bool> ValueBoundsConstraintSet::areEquivalentSlices(
   }
   for (auto [stride1, stride2] :
        llvm::zip_equal(slice1.getMixedStrides(), slice2.getMixedStrides())) {
-    if (stride1 == stride2)
-      continue;
     FailureOr<bool> equal = areEqual(stride1, stride2);
     if (failed(equal))
       return failure();
diff --git a/mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize-analysis.mlir b/mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize-analysis.mlir
index c947407c63e74..4e64161aa8030 100644
--- a/mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize-analysis.mlir
+++ b/mlir/test/Dialect/Bufferization/Transforms/one-shot-module-bufferize-analysis.mlir
@@ -250,6 +250,29 @@ func.func @extract_slice_matching_insert_slice(
 
 // -----
 
+// CHECK-LABEL: func @extract_slice_matching_insert_slice_dynamic
+func.func @extract_slice_matching_insert_slice_dynamic(
+    %tensor: tensor<?xf32> {bufferization.writable = true},
+    %offset: index, %size: index) -> tensor<?xf32> {
+  // Identical dynamic offsets/sizes and static strides should be recognized
+  // without constructing a ValueBounds constraint set.
+  //      CHECK: tensor.extract_slice
+  // CHECK-SAME: {__inplace_operands_attr__ = ["true", "none", "none"]}
+  %slice = tensor.extract_slice %tensor[%offset][%size][1]
+      : tensor<?xf32> to tensor<?xf32>
+
+  //      CHECK: tensor.insert_slice
+  // CHECK-SAME: {__inplace_operands_attr__ = ["true", "true", "none", "none"]}
+  %result = tensor.insert_slice %slice into %tensor[%offset][%size][1]
+      : tensor<?xf32> into tensor<?xf32>
+
+  //      CHECK: return
+  // CHECK-SAME: __equivalent_func_args__ = [0]
+  return %result : tensor<?xf32>
+}
+
+// -----
+
 // CHECK-LABEL: @read_of_matching_insert_slice_source
 func.func @read_of_matching_insert_slice_source(
     %A : tensor<?xf32> {bufferization.writable = true},
diff --git a/mlir/unittests/Interfaces/CMakeLists.txt b/mlir/unittests/Interfaces/CMakeLists.txt
index 5cb2948c814cd..193edda6bf8d8 100644
--- a/mlir/unittests/Interfaces/CMakeLists.txt
+++ b/mlir/unittests/Interfaces/CMakeLists.txt
@@ -5,7 +5,6 @@ add_mlir_unittest(MLIRInterfacesTests
   MemorySlotUtilsTest.cpp
   SideEffectInterfacesTest.cpp
   InferTypeOpInterfaceTest.cpp
-  ValueBoundsOpInterfaceTest.cpp
 
   DEPENDS
   MLIRTestInterfaceIncGen
@@ -24,6 +23,5 @@ mlir_target_link_libraries(MLIRInterfacesTests
   MLIRMemorySlotUtils
   MLIRParser
   MLIRSideEffectInterfaces
-  MLIRValueBoundsOpInterface
 )
 target_link_libraries(MLIRInterfacesTests PRIVATE MLIRTestDialect)
diff --git a/mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp b/mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp
deleted file mode 100644
index 1571ab924bf6f..0000000000000
--- a/mlir/unittests/Interfaces/ValueBoundsOpInterfaceTest.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-//===- ValueBoundsOpInterfaceTest.cpp - ValueBounds unit tests -----------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "mlir/Interfaces/ValueBoundsOpInterface.h"
-#include "mlir/Dialect/Func/IR/FuncOps.h"
-
-#include <gtest/gtest.h>
-
-using namespace mlir;
-
-namespace {
-
-TEST(ValueBoundsOpInterfaceTest, EquivalentSlices) {
-  MLIRContext context;
-  context.loadDialect<func::FuncDialect>();
-  OpBuilder builder(&context);
-
-  auto function = func::FuncOp::create(
-      builder.getUnknownLoc(), "test",
-      builder.getFunctionType({builder.getIndexType(), builder.getIndexType()},
-                              {}));
-  Block *entryBlock = function.addEntryBlock();
-  Value dynamicValue = entryBlock->getArgument(0);
-  Value otherDynamicValue = entryBlock->getArgument(1);
-
-  OpFoldResult zero = builder.getIndexAttr(0);
-  OpFoldResult one = builder.getIndexAttr(1);
-  OpFoldResult four = builder.getIndexAttr(4);
-  OpFoldResult five = builder.getIndexAttr(5);
-
-  HyperrectangularSlice slice({zero, dynamicValue}, {four, dynamicValue},
-                              {one, one});
-  HyperrectangularSlice identicalSlice({zero, dynamicValue},
-                                       {four, dynamicValue}, {one, one});
-  FailureOr<bool> equivalent = ValueBoundsConstraintSet::areEquivalentSlices(
-      &context, slice, identicalSlice);
-  ASSERT_TRUE(succeeded(equivalent));
-  EXPECT_TRUE(*equivalent);
-
-  // Identical components must not hide a later non-equivalent component.
-  HyperrectangularSlice differentSlice({zero, dynamicValue},
-                                       {five, dynamicValue}, {one, one});
-  equivalent = ValueBoundsConstraintSet::areEquivalentSlices(&context, slice,
-                                                             differentSlice);
-  ASSERT_TRUE(succeeded(equivalent));
-  EXPECT_FALSE(*equivalent);
-
-  // Distinct dynamic values continue to use the ValueBounds fallback.
-  HyperrectangularSlice unknownSlice({zero, otherDynamicValue},
-                                     {four, dynamicValue}, {one, one});
-  equivalent = ValueBoundsConstraintSet::areEquivalentSlices(&context, slice,
-                                                             unknownSlice);
-  EXPECT_TRUE(failed(equivalent));
-}
-
-} // namespace



More information about the Mlir-commits mailing list