[Mlir-commits] [mlir] [mlir][bufferization] Trace write-side subset provenance (PR #228995)

Ziliang Zhang llvmlistbot at llvm.org
Sat Oct 10 08:39:17 PDT 2026


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

>From 5c49e9514ab6cec6644ed4578e249af6813d7f4a Mon Sep 17 00:00:00 2001
From: Ziliang Zhang <zzl.coding at gmail.com>
Date: Fri, 2 Oct 2026 23:16:21 +0800
Subject: [PATCH 1/3] [mlir][bufferization] Trace write-side subset provenance

Trace write operands back to enclosing subset extractions to rule out conflicts with disjoint reads. Follow only equivalent alias edges and require every possible read/write origin pair to be disjoint.

Add coverage for fill and vector writes through extracted views, equivalent casts, and multiple write origins.
---
 .../Transforms/OneShotAnalysis.cpp            | 147 ++++++++++++------
 .../Dialect/Tensor/one-shot-bufferize.mlir    |  77 +++++++++
 .../Dialect/Vector/one-shot-bufferize.mlir    | 127 +++++++++++++++
 3 files changed, 304 insertions(+), 47 deletions(-)

diff --git a/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp b/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
index e8b3404843217..bb9e4417c00dc 100644
--- a/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
+++ b/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
@@ -719,6 +719,99 @@ static bool matchesInsertDestination(const AnalysisState &state,
   return llvm::all_of(backwardSlice, matchingSubset);
 }
 
+/// Find the nearest subset extractions from which `opOperand` may originate.
+/// Only equivalent alias edges may be crossed: an unknown alias does not
+/// guarantee that an access through the alias stays within the extracted
+/// subset. Return "false" if any origin is not a subset extraction.
+static bool
+findSubsetExtractionOrigins(const AnalysisState &state, OpOperand *opOperand,
+                            SmallVectorImpl<SubsetOpInterface> &subsetOrigins) {
+  auto isSubsetExtraction = [](Value value) {
+    return !!value.getDefiningOp<SubsetExtractionOpInterface>();
+  };
+  TraversalConfig config;
+  config.followEquivalentOnly = true;
+  SetVector<Value> origins = state.findValueInReverseUseDefChain(
+      opOperand, isSubsetExtraction, config);
+  if (origins.empty())
+    return false;
+
+  for (Value origin : origins) {
+    auto extraction = origin.getDefiningOp<SubsetExtractionOpInterface>();
+    if (!extraction)
+      return false;
+    subsetOrigins.push_back(cast<SubsetOpInterface>(extraction.getOperation()));
+  }
+  return true;
+}
+
+/// Return "true" if every read subset is disjoint from every written subset.
+static bool areAllDisjointSubsets(ArrayRef<SubsetOpInterface> readSubsets,
+                                  ArrayRef<SubsetOpInterface> writtenSubsets,
+                                  const AnalysisState &state) {
+  return llvm::all_of(readSubsets, [&](SubsetOpInterface readSubset) {
+    return llvm::all_of(writtenSubsets, [&](SubsetOpInterface writtenSubset) {
+      return readSubset.operatesOnDisjointSubset(
+          writtenSubset, [&](Value v1, Value v2) {
+            return state.areEquivalentBufferizedValues(v1, v2);
+          });
+    });
+  });
+}
+
+/// Return "true" if the read and write are confined to provably disjoint
+/// subsets. Prefer the exact subsets of extraction/insertion ops. If those
+/// cannot be compared, use enclosing subset extractions as conservative access
+/// bounds.
+static bool areDisjointSubsetAccesses(OpOperand *uRead,
+                                      OpOperand *uConflictingWrite,
+                                      const AnalysisState &state) {
+  Operation *readingOp = uRead->getOwner();
+  Operation *conflictingWritingOp = uConflictingWrite->getOwner();
+
+  SmallVector<SubsetOpInterface, 1> directReadSubsets;
+  if (auto extraction = dyn_cast<SubsetExtractionOpInterface>(readingOp))
+    if (uRead == &extraction.getSourceOperand())
+      directReadSubsets.push_back(cast<SubsetOpInterface>(readingOp));
+
+  SmallVector<SubsetOpInterface> readOrigins;
+  bool readOriginsInitialized = false;
+  bool hasValidReadOrigins = false;
+  auto readsAreDisjoint = [&](ArrayRef<SubsetOpInterface> writtenSubsets) {
+    if (!directReadSubsets.empty() &&
+        areAllDisjointSubsets(directReadSubsets, writtenSubsets, state))
+      return true;
+
+    if (!readOriginsInitialized) {
+      hasValidReadOrigins =
+          findSubsetExtractionOrigins(state, uRead, readOrigins);
+      readOriginsInitialized = true;
+    }
+    return hasValidReadOrigins &&
+           areAllDisjointSubsets(readOrigins, writtenSubsets, state);
+  };
+
+  // First try the exact subset written by an insertion op.
+  if (auto insertion =
+          dyn_cast<SubsetInsertionOpInterface>(conflictingWritingOp)) {
+    if (uConflictingWrite == &insertion.getDestinationOperand()) {
+      SmallVector<SubsetOpInterface, 1> writtenSubset = {
+          cast<SubsetOpInterface>(conflictingWritingOp)};
+      if (readsAreDisjoint(writtenSubset))
+        return true;
+    }
+  }
+
+  // The write may happen through a view that was produced by a subset
+  // extraction. The extraction is a safe (possibly imprecise) bound for every
+  // access through that view. There may be multiple possible origins, e.g.,
+  // after an arith.select; all of them must be disjoint from every read origin.
+  SmallVector<SubsetOpInterface> writtenOrigins;
+  if (!findSubsetExtractionOrigins(state, uConflictingWrite, writtenOrigins))
+    return false;
+  return readsAreDisjoint(writtenOrigins);
+}
+
 /// Return "true" if the given "read" and potentially conflicting "write" are
 /// not conflicting due to their subset relationship. The comments in this
 /// function are expressed in terms of tensor.extract_slice/tensor.insert_slice
@@ -770,56 +863,16 @@ static bool areNonConflictingSubsets(OpOperand *uRead,
       return true;
   }
 
+  // A read from a subset does not conflict with a write to a disjoint subset
+  // of an equivalent tensor. The actual read or write may be further down an
+  // aliasing use-def chain. Trace such accesses back to subset extractions,
+  // crossing only equivalent alias edges.
+  if (areDisjointSubsetAccesses(uRead, uConflictingWrite, state))
+    return true;
+
   // If uConflictingWrite is an InsertSliceOp...
   if (auto subsetOp =
           dyn_cast<SubsetInsertionOpInterface>(conflictingWritingOp)) {
-    if (uConflictingWrite == &subsetOp.getDestinationOperand()) {
-      auto writtenSubset = cast<SubsetOpInterface>(conflictingWritingOp);
-      auto isDisjointSubset = [&](SubsetOpInterface readSubset) {
-        return readSubset.operatesOnDisjointSubset(
-            writtenSubset, [&](Value v1, Value v2) {
-              return state.areEquivalentBufferizedValues(v1, v2);
-            });
-      };
-      auto isDisjointExtraction = [&](Value value) {
-        auto extraction = value.getDefiningOp<SubsetExtractionOpInterface>();
-        return extraction && isDisjointSubset(cast<SubsetOpInterface>(
-                                 extraction.getOperation()));
-      };
-
-      // Example:
-      //
-      // %0 = tensor.insert_slice %s into %t[0][4][1]
-      // %1 = vector.transfer_read %t[%c4], %cst
-      //
-      // A read from a subset does not conflict with a write to a disjoint
-      // subset of an equivalent tensor. Check the operand roles explicitly
-      // because not every subset extraction bufferizes to a memory read.
-      if (auto extraction = dyn_cast<SubsetExtractionOpInterface>(readingOp)) {
-        if (uRead == &extraction.getSourceOperand() &&
-            isDisjointSubset(cast<SubsetOpInterface>(readingOp)))
-          return true;
-      }
-
-      // Example:
-      //
-      // %0 = tensor.insert_slice %s into %t[0][4][1]
-      // %1 = tensor.extract_slice %t[4][4][1]
-      // return %0, %1
-      //
-      // The actual read may be further down the aliasing use-def chain. E.g.,
-      // tensor.extract_slice is an alias-only op and the read is attributed to
-      // a return or another consumer of its result. Trace such reads back to
-      // their subset extractions. Every origin must be a disjoint subset; a
-      // non-subset leaf or an extraction that may overlap keeps the analysis
-      // conservative.
-      SetVector<Value> readOrigins =
-          state.findValueInReverseUseDefChain(uRead, isDisjointExtraction);
-      if (!readOrigins.empty() &&
-          llvm::all_of(readOrigins, isDisjointExtraction))
-        return true;
-    }
-
     // As an example, consider the following IR.
     //
     // %0 = tensor.extract_slice %t[%a, %b][%c, %d][1, 1] {inplace = [true] }
diff --git a/mlir/test/Dialect/Tensor/one-shot-bufferize.mlir b/mlir/test/Dialect/Tensor/one-shot-bufferize.mlir
index 272e6d12e7e75..fa970d008c109 100644
--- a/mlir/test/Dialect/Tensor/one-shot-bufferize.mlir
+++ b/mlir/test/Dialect/Tensor/one-shot-bufferize.mlir
@@ -119,6 +119,83 @@ func.func @unknown_insert_extract(
 
 // -----
 
+// A write through an extracted view is confined to that subset. It does not
+// conflict with a read through a disjoint view of the same tensor.
+
+// CHECK-LABEL: func @disjoint_fill_through_extract(
+//  CHECK-SAME:     %[[T:.*]]: memref<8xf32, strided<[?], offset: ?>>
+//   CHECK-NOT:   memref.alloc
+//       CHECK:   %[[LHS:.*]] = memref.subview %[[T]][0] [4] [1]
+//       CHECK:   %[[RHS:.*]] = memref.subview %[[T]][4] [4] [1]
+//       CHECK:   linalg.fill {{.*}} outs(%[[LHS]]
+//       CHECK:   return %[[LHS]], %[[RHS]]
+
+// CHECK-ANALYSIS-LABEL: func @disjoint_fill_through_extract(
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+// CHECK-ANALYSIS: linalg.fill
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["none", "true"]
+func.func @disjoint_fill_through_extract(
+    %t: tensor<8xf32> {bufferization.writable = true}, %value: f32)
+    -> (tensor<4xf32>, tensor<4xf32>) {
+  %lhs = tensor.extract_slice %t[0][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %rhs = tensor.extract_slice %t[4][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %written = linalg.fill ins(%value : f32) outs(%lhs : tensor<4xf32>)
+      -> tensor<4xf32>
+  return %written, %rhs : tensor<4xf32>, tensor<4xf32>
+}
+
+// -----
+
+// A write through [0, 4) conflicts with a read through [2, 6).
+
+// CHECK-LABEL: func @overlapping_fill_through_extract(
+//       CHECK:   %[[ALLOC:.*]] = memref.alloc
+
+// CHECK-ANALYSIS-LABEL: func @overlapping_fill_through_extract(
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["false"]
+func.func @overlapping_fill_through_extract(
+    %t: tensor<8xf32> {bufferization.writable = true}, %value: f32)
+    -> (tensor<4xf32>, tensor<4xf32>) {
+  %lhs = tensor.extract_slice %t[0][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %rhs = tensor.extract_slice %t[2][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %written = linalg.fill ins(%value : f32) outs(%lhs : tensor<4xf32>)
+      -> tensor<4xf32>
+  return %written, %rhs : tensor<4xf32>, tensor<4xf32>
+}
+
+// -----
+
+// Dynamically positioned views remain conservative.
+
+// CHECK-LABEL: func @unknown_fill_through_extract(
+//       CHECK:   %[[ALLOC:.*]] = memref.alloc
+
+// CHECK-ANALYSIS-LABEL: func @unknown_fill_through_extract(
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["false", "none"]
+func.func @unknown_fill_through_extract(
+    %t: tensor<8xf32> {bufferization.writable = true}, %value: f32,
+    %write_idx: index, %read_idx: index)
+    -> (tensor<4xf32>, tensor<4xf32>) {
+  %lhs = tensor.extract_slice %t[%write_idx][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %rhs = tensor.extract_slice %t[%read_idx][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %written = linalg.fill ins(%value : f32) outs(%lhs : tensor<4xf32>)
+      -> tensor<4xf32>
+  return %written, %rhs : tensor<4xf32>, tensor<4xf32>
+}
+
+// -----
+
 // CHECK-LABEL: func private @insert_slice_fun
 //  CHECK-SAME:   %[[A0:[a-zA-Z0-9]*]]: memref<?xf32, strided<[?], offset: ?>>,
 //  CHECK-SAME:   %[[A1:[a-zA-Z0-9]*]]: memref<?xf32, strided<[?], offset: ?>>,
diff --git a/mlir/test/Dialect/Vector/one-shot-bufferize.mlir b/mlir/test/Dialect/Vector/one-shot-bufferize.mlir
index a190c94753008..971a2ae20dcd7 100644
--- a/mlir/test/Dialect/Vector/one-shot-bufferize.mlir
+++ b/mlir/test/Dialect/Vector/one-shot-bufferize.mlir
@@ -125,3 +125,130 @@ func.func @unknown_transfer_read_write(
       : tensor<8xf32>, vector<4xf32>
   return %written, %read : tensor<8xf32>, vector<4xf32>
 }
+
+// -----
+
+// A partial vector write through [0, 4) does not conflict with a read through
+// [4, 8). Both the allocation and the copy for the extracted write view can be
+// avoided.
+
+// CHECK-LABEL: func @disjoint_transfer_write_through_extract(
+//  CHECK-SAME:     %[[T:.*]]: memref<8xf32, strided<[?], offset: ?>>
+//   CHECK-NOT:   memref.alloc
+//   CHECK-NOT:   memref.copy
+//       CHECK:   %[[LHS:.*]] = memref.subview %[[T]][0] [4] [1]
+//       CHECK:   %[[RHS:.*]] = memref.subview %[[T]][4] [4] [1]
+//       CHECK:   vector.transfer_write %{{.*}}, %[[LHS]]
+//       CHECK:   return %[[LHS]], %[[RHS]]
+
+// CHECK-ANALYSIS-LABEL: func @disjoint_transfer_write_through_extract(
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+// CHECK-ANALYSIS: vector.transfer_write
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["none", "true", "none"]
+func.func @disjoint_transfer_write_through_extract(
+    %t: tensor<8xf32> {bufferization.writable = true},
+    %v: vector<2xf32>) -> (tensor<4xf32>, tensor<4xf32>) {
+  %c0 = arith.constant 0 : index
+  %lhs = tensor.extract_slice %t[0][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %rhs = tensor.extract_slice %t[4][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %written = vector.transfer_write %v, %lhs[%c0]
+      : vector<2xf32>, tensor<4xf32>
+  return %written, %rhs : tensor<4xf32>, tensor<4xf32>
+}
+
+// -----
+
+// Equivalent aliases between the extraction and the write preserve subset
+// containment.
+
+// CHECK-LABEL: func @disjoint_transfer_write_through_extract_and_cast(
+//   CHECK-NOT:   memref.alloc
+//   CHECK-NOT:   memref.copy
+//       CHECK:   vector.transfer_write
+
+// CHECK-ANALYSIS-LABEL: func @disjoint_transfer_write_through_extract_and_cast(
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+// CHECK-ANALYSIS: tensor.cast
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+// CHECK-ANALYSIS: tensor.cast
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+func.func @disjoint_transfer_write_through_extract_and_cast(
+    %t: tensor<8xf32> {bufferization.writable = true},
+    %v: vector<2xf32>) -> (tensor<4xf32>, tensor<4xf32>) {
+  %c0 = arith.constant 0 : index
+  %lhs = tensor.extract_slice %t[0][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %cast0 = tensor.cast %lhs : tensor<4xf32> to tensor<?xf32>
+  %cast1 = tensor.cast %cast0 : tensor<?xf32> to tensor<4xf32>
+  %rhs = tensor.extract_slice %t[4][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %written = vector.transfer_write %v, %cast1[%c0]
+      : vector<2xf32>, tensor<4xf32>
+  return %written, %rhs : tensor<4xf32>, tensor<4xf32>
+}
+
+// -----
+
+// Every possible write origin is disjoint from the read.
+
+// CHECK-LABEL: func @multiple_disjoint_write_origins(
+//   CHECK-NOT:   memref.alloc
+//   CHECK-NOT:   memref.copy
+//       CHECK:   vector.transfer_write
+
+// CHECK-ANALYSIS-LABEL: func @multiple_disjoint_write_origins(
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+func.func @multiple_disjoint_write_origins(
+    %t: tensor<12xf32> {bufferization.writable = true},
+    %v: vector<2xf32>, %cond: i1) -> (tensor<4xf32>, tensor<4xf32>) {
+  %c0 = arith.constant 0 : index
+  %lhs0 = tensor.extract_slice %t[0][4][1]
+      : tensor<12xf32> to tensor<4xf32>
+  %lhs1 = tensor.extract_slice %t[4][4][1]
+      : tensor<12xf32> to tensor<4xf32>
+  %rhs = tensor.extract_slice %t[8][4][1]
+      : tensor<12xf32> to tensor<4xf32>
+  %selected = arith.select %cond, %lhs0, %lhs1 : tensor<4xf32>
+  %written = vector.transfer_write %v, %selected[%c0]
+      : vector<2xf32>, tensor<4xf32>
+  return %written, %rhs : tensor<4xf32>, tensor<4xf32>
+}
+
+// -----
+
+// One possible write origin overlaps the read, so the proof must fail.
+
+// CHECK-LABEL: func @one_overlapping_write_origin(
+//       CHECK:   %[[ALLOC:.*]] = memref.alloc
+
+// CHECK-ANALYSIS-LABEL: func @one_overlapping_write_origin(
+// CHECK-ANALYSIS: tensor.extract_slice
+// CHECK-ANALYSIS-SAME: __inplace_operands_attr__ = ["false"]
+func.func @one_overlapping_write_origin(
+    %t: tensor<12xf32> {bufferization.writable = true},
+    %v: vector<2xf32>, %cond: i1) -> (tensor<4xf32>, tensor<4xf32>) {
+  %c0 = arith.constant 0 : index
+  %lhs0 = tensor.extract_slice %t[0][4][1]
+      : tensor<12xf32> to tensor<4xf32>
+  %lhs1 = tensor.extract_slice %t[8][4][1]
+      : tensor<12xf32> to tensor<4xf32>
+  %rhs = tensor.extract_slice %t[8][4][1]
+      : tensor<12xf32> to tensor<4xf32>
+  %selected = arith.select %cond, %lhs0, %lhs1 : tensor<4xf32>
+  %written = vector.transfer_write %v, %selected[%c0]
+      : vector<2xf32>, tensor<4xf32>
+  return %written, %rhs : tensor<4xf32>, tensor<4xf32>
+}

>From 758a23540698806c0f36ffbd7a979d312ceb7aa1 Mon Sep 17 00:00:00 2001
From: Ziliang Zhang <zzl.coding at gmail.com>
Date: Sat, 10 Oct 2026 23:02:32 +0800
Subject: [PATCH 2/3] [mlir][bufferization] Fix subset provenance across
 iterations

Guard disjoint subset proofs against cross-iteration interference and trace nested extractions to enclosing access bounds. Add SCF, CFG, and nested-subset regression tests.
---
 .../Transforms/OneShotAnalysis.cpp            | 131 +++++-----
 .../one-shot-bufferize-disjoint-subsets.mlir  | 238 ++++++++++++++++++
 2 files changed, 304 insertions(+), 65 deletions(-)
 create mode 100644 mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-disjoint-subsets.mlir

diff --git a/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp b/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
index bb9e4417c00dc..858fbc3bb2b99 100644
--- a/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
+++ b/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
@@ -719,46 +719,47 @@ static bool matchesInsertDestination(const AnalysisState &state,
   return llvm::all_of(backwardSlice, matchingSubset);
 }
 
-/// Find the nearest subset extractions from which `opOperand` may originate.
-/// Only equivalent alias edges may be crossed: an unknown alias does not
-/// guarantee that an access through the alias stays within the extracted
-/// subset. Return "false" if any origin is not a subset extraction.
+/// Return "true" if every reverse use-def path reaches an extraction satisfying
+/// `isDisjoint`. An extraction bounds all accesses through its result. If its
+/// subset cannot be compared, continue through its source to try a larger
+/// bound. Other alias edges must be equivalent: an unknown alias does not
+/// guarantee containment. Unproven leaves and cycles conservatively fail the
+/// proof.
 static bool
-findSubsetExtractionOrigins(const AnalysisState &state, OpOperand *opOperand,
-                            SmallVectorImpl<SubsetOpInterface> &subsetOrigins) {
-  auto isSubsetExtraction = [](Value value) {
-    return !!value.getDefiningOp<SubsetExtractionOpInterface>();
-  };
-  TraversalConfig config;
-  config.followEquivalentOnly = true;
-  SetVector<Value> origins = state.findValueInReverseUseDefChain(
-      opOperand, isSubsetExtraction, config);
-  if (origins.empty())
-    return false;
+hasDisjointSubsetOrigins(const AnalysisState &state, OpOperand *opOperand,
+                         function_ref<bool(SubsetOpInterface)> isDisjoint) {
+  SetVector<Value> worklist;
+  DenseSet<Value> visited;
+  worklist.insert(opOperand->get());
+  while (!worklist.empty()) {
+    Value value = worklist.pop_back_val();
+    auto extraction = value.getDefiningOp<SubsetExtractionOpInterface>();
+    if (extraction &&
+        isDisjoint(cast<SubsetOpInterface>(extraction.getOperation())))
+      continue;
+
+    if (!visited.insert(value).second)
+      return false;
 
-  for (Value origin : origins) {
-    auto extraction = origin.getDefiningOp<SubsetExtractionOpInterface>();
-    if (!extraction)
+    if (!state.getOptions().dynCastBufferizableOp(value))
       return false;
-    subsetOrigins.push_back(cast<SubsetOpInterface>(extraction.getOperation()));
+    if (extraction) {
+      worklist.insert(extraction.getSourceOperand().get());
+      continue;
+    }
+
+    AliasingOpOperandList aliases = state.getAliasingOpOperands(value);
+    if (aliases.getNumAliases() == 0)
+      return false;
+    for (AliasingOpOperand alias : aliases) {
+      if (alias.relation != BufferRelation::Equivalent)
+        return false;
+      worklist.insert(alias.opOperand->get());
+    }
   }
   return true;
 }
 
-/// Return "true" if every read subset is disjoint from every written subset.
-static bool areAllDisjointSubsets(ArrayRef<SubsetOpInterface> readSubsets,
-                                  ArrayRef<SubsetOpInterface> writtenSubsets,
-                                  const AnalysisState &state) {
-  return llvm::all_of(readSubsets, [&](SubsetOpInterface readSubset) {
-    return llvm::all_of(writtenSubsets, [&](SubsetOpInterface writtenSubset) {
-      return readSubset.operatesOnDisjointSubset(
-          writtenSubset, [&](Value v1, Value v2) {
-            return state.areEquivalentBufferizedValues(v1, v2);
-          });
-    });
-  });
-}
-
 /// Return "true" if the read and write are confined to provably disjoint
 /// subsets. Prefer the exact subsets of extraction/insertion ops. If those
 /// cannot be compared, use enclosing subset extractions as conservative access
@@ -769,35 +770,25 @@ static bool areDisjointSubsetAccesses(OpOperand *uRead,
   Operation *readingOp = uRead->getOwner();
   Operation *conflictingWritingOp = uConflictingWrite->getOwner();
 
-  SmallVector<SubsetOpInterface, 1> directReadSubsets;
-  if (auto extraction = dyn_cast<SubsetExtractionOpInterface>(readingOp))
-    if (uRead == &extraction.getSourceOperand())
-      directReadSubsets.push_back(cast<SubsetOpInterface>(readingOp));
-
-  SmallVector<SubsetOpInterface> readOrigins;
-  bool readOriginsInitialized = false;
-  bool hasValidReadOrigins = false;
-  auto readsAreDisjoint = [&](ArrayRef<SubsetOpInterface> writtenSubsets) {
-    if (!directReadSubsets.empty() &&
-        areAllDisjointSubsets(directReadSubsets, writtenSubsets, state))
-      return true;
-
-    if (!readOriginsInitialized) {
-      hasValidReadOrigins =
-          findSubsetExtractionOrigins(state, uRead, readOrigins);
-      readOriginsInitialized = true;
-    }
-    return hasValidReadOrigins &&
-           areAllDisjointSubsets(readOrigins, writtenSubsets, state);
+  auto readsAreDisjoint = [&](SubsetOpInterface writtenSubset) {
+    auto isDisjoint = [&](SubsetOpInterface readSubset) {
+      return readSubset.operatesOnDisjointSubset(
+          writtenSubset, [&](Value v1, Value v2) {
+            return state.areEquivalentBufferizedValues(v1, v2);
+          });
+    };
+    if (auto extraction = dyn_cast<SubsetExtractionOpInterface>(readingOp))
+      if (uRead == &extraction.getSourceOperand() &&
+          isDisjoint(cast<SubsetOpInterface>(readingOp)))
+        return true;
+    return hasDisjointSubsetOrigins(state, uRead, isDisjoint);
   };
 
   // First try the exact subset written by an insertion op.
   if (auto insertion =
           dyn_cast<SubsetInsertionOpInterface>(conflictingWritingOp)) {
     if (uConflictingWrite == &insertion.getDestinationOperand()) {
-      SmallVector<SubsetOpInterface, 1> writtenSubset = {
-          cast<SubsetOpInterface>(conflictingWritingOp)};
-      if (readsAreDisjoint(writtenSubset))
+      if (readsAreDisjoint(cast<SubsetOpInterface>(conflictingWritingOp)))
         return true;
     }
   }
@@ -806,20 +797,16 @@ static bool areDisjointSubsetAccesses(OpOperand *uRead,
   // extraction. The extraction is a safe (possibly imprecise) bound for every
   // access through that view. There may be multiple possible origins, e.g.,
   // after an arith.select; all of them must be disjoint from every read origin.
-  SmallVector<SubsetOpInterface> writtenOrigins;
-  if (!findSubsetExtractionOrigins(state, uConflictingWrite, writtenOrigins))
-    return false;
-  return readsAreDisjoint(writtenOrigins);
+  return hasDisjointSubsetOrigins(state, uConflictingWrite, readsAreDisjoint);
 }
 
 /// Return "true" if the given "read" and potentially conflicting "write" are
 /// not conflicting due to their subset relationship. The comments in this
 /// function are expressed in terms of tensor.extract_slice/tensor.insert_slice
-/// pairs, but apply to any subset ops that implement the
-/// `SubsetInsertionOpInterface`.
+/// pairs, but apply to any ops implementing the subset interfaces.
 static bool areNonConflictingSubsets(OpOperand *uRead,
                                      OpOperand *uConflictingWrite,
-                                     const AnalysisState &state) {
+                                     OneShotAnalysisState &state) {
   Operation *readingOp = uRead->getOwner();
   Operation *conflictingWritingOp = uConflictingWrite->getOwner();
 
@@ -866,8 +853,22 @@ static bool areNonConflictingSubsets(OpOperand *uRead,
   // A read from a subset does not conflict with a write to a disjoint subset
   // of an equivalent tensor. The actual read or write may be further down an
   // aliasing use-def chain. Trace such accesses back to subset extractions,
-  // crossing only equivalent alias edges.
-  if (areDisjointSubsetAccesses(uRead, uConflictingWrite, state))
+  // crossing only equivalent alias edges or extraction source edges.
+  //
+  // %lhs = tensor.extract_slice %t[0][4][1]
+  // %rhs = tensor.extract_slice %t[4][4][1]
+  // %written = linalg.fill ins(%cst) outs(%lhs)
+  // return %written, %rhs
+  //
+  // Disjointness describes a single execution of the subset ops. If a read
+  // can observe writes from another iteration, the subset coordinates may
+  // differ across executions. Reuse the dominance checks to conservatively
+  // exclude such repetitive-region and CFG cases. Matching subset rules above
+  // and below do not rely on a disjointness proof and remain applicable.
+  const SetVector<Value> &definitions = state.findDefinitionsCached(uRead);
+  if ((definitions.empty() ||
+       canUseOpDominance(uRead, uConflictingWrite, definitions, state)) &&
+      areDisjointSubsetAccesses(uRead, uConflictingWrite, state))
     return true;
 
   // If uConflictingWrite is an InsertSliceOp...
diff --git a/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-disjoint-subsets.mlir b/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-disjoint-subsets.mlir
new file mode 100644
index 0000000000000..2cece8b4ffe89
--- /dev/null
+++ b/mlir/test/Dialect/Bufferization/Transforms/one-shot-bufferize-disjoint-subsets.mlir
@@ -0,0 +1,238 @@
+// RUN: mlir-opt %s -one-shot-bufferize="bufferize-function-boundaries" -split-input-file | FileCheck %s
+// RUN: mlir-opt %s -one-shot-bufferize="test-analysis-only bufferize-function-boundaries" -split-input-file | FileCheck %s --check-prefix=ANALYSIS
+// RUN: mlir-opt %s -one-shot-bufferize="test-analysis-only analysis-heuristic=fuzzer analysis-fuzzer-seed=23 bufferize-function-boundaries" -split-input-file -o /dev/null
+// RUN: mlir-opt %s -one-shot-bufferize="test-analysis-only analysis-heuristic=fuzzer analysis-fuzzer-seed=59 bufferize-function-boundaries" -split-input-file -o /dev/null
+// RUN: mlir-opt %s -one-shot-bufferize="test-analysis-only analysis-heuristic=fuzzer analysis-fuzzer-seed=91 bufferize-function-boundaries" -split-input-file -o /dev/null
+
+// The inner extraction cannot be compared directly with the insertion. The
+// outer extraction still bounds the read and is disjoint from the insertion.
+
+// CHECK-LABEL: func @nested_read(
+// CHECK-NOT: memref.alloc
+// CHECK: memref.copy
+// CHECK: memref.subview
+// CHECK: memref.subview
+// CHECK-NOT: memref.alloc
+// CHECK: return
+// ANALYSIS-LABEL: func @nested_read(
+// ANALYSIS: tensor.insert_slice
+// ANALYSIS-SAME: __inplace_operands_attr__ = ["true", "true"]
+func.func @nested_read(
+    %t: tensor<8xf32> {bufferization.writable = true}, %s: tensor<4xf32>)
+    -> (tensor<8xf32>, tensor<2xf32>) {
+  %written = tensor.insert_slice %s into %t[0][4][1]
+      : tensor<4xf32> into tensor<8xf32>
+  %outer = tensor.extract_slice %t[4][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %inner = tensor.extract_slice %outer[0][2][1]
+      : tensor<4xf32> to tensor<2xf32>
+  return %written, %inner : tensor<8xf32>, tensor<2xf32>
+}
+
+// -----
+
+// The same containment reasoning applies on the write side.
+
+// CHECK-LABEL: func @nested_write(
+// CHECK-NOT: memref.alloc
+// CHECK: linalg.fill
+// CHECK-NOT: memref.alloc
+// CHECK: return
+// ANALYSIS-LABEL: func @nested_write(
+// ANALYSIS: linalg.fill
+// ANALYSIS-SAME: __inplace_operands_attr__ = ["none", "true"]
+func.func @nested_write(
+    %t: tensor<8xf32> {bufferization.writable = true}, %value: f32)
+    -> (tensor<2xf32>, tensor<4xf32>) {
+  %outer = tensor.extract_slice %t[0][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %inner = tensor.extract_slice %outer[0][2][1]
+      : tensor<4xf32> to tensor<2xf32>
+  %rhs = tensor.extract_slice %t[4][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %written = linalg.fill ins(%value : f32) outs(%inner : tensor<2xf32>)
+      -> tensor<2xf32>
+  return %written, %rhs : tensor<2xf32>, tensor<4xf32>
+}
+
+// -----
+
+// Neither the inner nor outer bound proves disjointness in this case.
+
+// CHECK-LABEL: func @nested_overlap(
+// CHECK: memref.alloc
+// CHECK: linalg.fill
+// ANALYSIS-LABEL: func @nested_overlap(
+// ANALYSIS: tensor.extract_slice
+// ANALYSIS-SAME: __inplace_operands_attr__ = ["false"]
+func.func @nested_overlap(
+    %t: tensor<8xf32> {bufferization.writable = true}, %value: f32)
+    -> (tensor<2xf32>, tensor<4xf32>) {
+  %outer = tensor.extract_slice %t[0][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %inner = tensor.extract_slice %outer[0][2][1]
+      : tensor<4xf32> to tensor<2xf32>
+  %rhs = tensor.extract_slice %t[1][4][1]
+      : tensor<8xf32> to tensor<4xf32>
+  %written = linalg.fill ins(%value : f32) outs(%inner : tensor<2xf32>)
+      -> tensor<2xf32>
+  return %written, %rhs : tensor<2xf32>, tensor<4xf32>
+}
+
+// -----
+
+// In iteration i the write to t[i] and read from t[i-1] are disjoint, but the
+// read must not observe the write from iteration i-1. The fill needs a private
+// buffer; tensor SSA semantics keep the original t unchanged across iterations.
+
+// CHECK-LABEL: func @cross_iteration_fill(
+// CHECK-SAME: %[[T:.*]]: memref<4xf32,
+// CHECK: scf.for
+// CHECK: %[[ALLOC:.*]] = memref.alloc
+// CHECK: %[[RHS:.*]] = memref.subview %[[T]]
+// CHECK: linalg.fill {{.*}} outs(%[[ALLOC]]
+// CHECK: memref.load %[[RHS]]
+// ANALYSIS-LABEL: func @cross_iteration_fill(
+// ANALYSIS: tensor.extract_slice
+// ANALYSIS-SAME: __inplace_operands_attr__ = ["false", "none"]
+func.func @cross_iteration_fill(
+    %t: tensor<4xf32> {bufferization.writable = true}) -> f32 {
+  %c0 = arith.constant 0 : index
+  %c1 = arith.constant 1 : index
+  %c4 = arith.constant 4 : index
+  %zero = arith.constant 0.0 : f32
+  %value = arith.constant 100.0 : f32
+  %result = scf.for %i = %c1 to %c4 step %c1
+      iter_args(%sum = %zero) -> f32 {
+    %prev = arith.subi %i, %c1 : index
+    %lhs = tensor.extract_slice %t[%i][1][1]
+        : tensor<4xf32> to tensor<1xf32>
+    %rhs = tensor.extract_slice %t[%prev][1][1]
+        : tensor<4xf32> to tensor<1xf32>
+    %written = linalg.fill ins(%value : f32) outs(%lhs : tensor<1xf32>)
+        -> tensor<1xf32>
+    %new = tensor.extract %written[%c0] : tensor<1xf32>
+    %old = tensor.extract %rhs[%c0] : tensor<1xf32>
+    %pair = arith.addf %new, %old : f32
+    %next = arith.addf %sum, %pair : f32
+    scf.yield %next : f32
+  }
+  return %result : f32
+}
+
+// -----
+
+// Exact insertion subsets must obey the same cross-iteration restriction.
+
+// CHECK-LABEL: func @cross_iteration_insert(
+// CHECK-SAME: %[[T:.*]]: memref<4xf32,
+// CHECK: scf.for
+// CHECK: %[[ALLOC:.*]] = memref.alloc
+// CHECK: memref.copy %[[T]], %[[ALLOC]]
+// ANALYSIS-LABEL: func @cross_iteration_insert(
+// ANALYSIS: tensor.insert_slice
+// ANALYSIS-SAME: __inplace_operands_attr__ = ["true", "false", "none"]
+func.func @cross_iteration_insert(
+    %t: tensor<4xf32> {bufferization.writable = true}, %s: tensor<1xf32>)
+    -> f32 {
+  %c0 = arith.constant 0 : index
+  %c1 = arith.constant 1 : index
+  %c4 = arith.constant 4 : index
+  %zero = arith.constant 0.0 : f32
+  %result = scf.for %i = %c1 to %c4 step %c1
+      iter_args(%sum = %zero) -> f32 {
+    %prev = arith.subi %i, %c1 : index
+    %written = tensor.insert_slice %s into %t[%i][1][1]
+        : tensor<1xf32> into tensor<4xf32>
+    %rhs = tensor.extract_slice %t[%prev][1][1]
+        : tensor<4xf32> to tensor<1xf32>
+    %new = tensor.extract %written[%i] : tensor<4xf32>
+    %old = tensor.extract %rhs[%c0] : tensor<1xf32>
+    %pair = arith.addf %new, %old : f32
+    %next = arith.addf %sum, %pair : f32
+    scf.yield %next : f32
+  }
+  return %result : f32
+}
+
+// -----
+
+// Unstructured loops have the same cross-iteration hazard.
+
+// CHECK-LABEL: func @cross_iteration_cfg(
+// CHECK-SAME: %[[T:.*]]: memref<4xf32,
+// CHECK: %[[ALLOC:.*]] = memref.alloc
+// CHECK: %[[RHS:.*]] = memref.subview %[[T]]
+// CHECK: linalg.fill {{.*}} outs(%[[ALLOC]]
+// CHECK: memref.load %[[RHS]]
+// ANALYSIS-LABEL: func @cross_iteration_cfg(
+// ANALYSIS: tensor.extract_slice
+// ANALYSIS-SAME: __inplace_operands_attr__ = ["false", "none"]
+func.func @cross_iteration_cfg(
+    %t: tensor<4xf32> {bufferization.writable = true}) -> f32 {
+  %c0 = arith.constant 0 : index
+  %c1 = arith.constant 1 : index
+  %c4 = arith.constant 4 : index
+  %zero = arith.constant 0.0 : f32
+  %value = arith.constant 100.0 : f32
+  cf.br ^loop(%c1, %zero : index, f32)
+^loop(%i: index, %sum: f32):
+  %prev = arith.subi %i, %c1 : index
+  %lhs = tensor.extract_slice %t[%i][1][1]
+      : tensor<4xf32> to tensor<1xf32>
+  %rhs = tensor.extract_slice %t[%prev][1][1]
+      : tensor<4xf32> to tensor<1xf32>
+  %written = linalg.fill ins(%value : f32) outs(%lhs : tensor<1xf32>)
+      -> tensor<1xf32>
+  %new = tensor.extract %written[%c0] : tensor<1xf32>
+  %old = tensor.extract %rhs[%c0] : tensor<1xf32>
+  %pair = arith.addf %new, %old : f32
+  %next = arith.addf %sum, %pair : f32
+  %next_i = arith.addi %i, %c1 : index
+  %continue = arith.cmpi ult, %next_i, %c4 : index
+  cf.cond_br %continue, ^loop(%next_i, %next : index, f32), ^exit(%next : f32)
+^exit(%result: f32):
+  return %result : f32
+}
+
+// -----
+
+// A fresh definition in each iteration resets the read/write sequence. Merely
+// being inside a loop must not disable the disjoint-subset optimization.
+
+// CHECK-LABEL: func @fresh_definition_in_loop(
+// CHECK: scf.for
+// CHECK: memref.alloc
+// CHECK-NOT: memref.alloc
+// CHECK: linalg.fill
+// CHECK-NOT: memref.alloc
+// CHECK: scf.yield
+// ANALYSIS-LABEL: func @fresh_definition_in_loop(
+// ANALYSIS: tensor.extract_slice
+// ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+// ANALYSIS: tensor.extract_slice
+// ANALYSIS-SAME: __inplace_operands_attr__ = ["true"]
+// ANALYSIS: linalg.fill
+// ANALYSIS-SAME: __inplace_operands_attr__ = ["none", "true"]
+func.func @fresh_definition_in_loop(%value: f32) -> f32 {
+  %c0 = arith.constant 0 : index
+  %c1 = arith.constant 1 : index
+  %c4 = arith.constant 4 : index
+  %zero = arith.constant 0.0 : f32
+  %result = scf.for %i = %c1 to %c4 step %c1
+      iter_args(%sum = %zero) -> f32 {
+    %t = tensor.from_elements %value, %value, %value, %value : tensor<4xf32>
+    %lhs = tensor.extract_slice %t[0][2][1]
+        : tensor<4xf32> to tensor<2xf32>
+    %rhs = tensor.extract_slice %t[2][2][1]
+        : tensor<4xf32> to tensor<2xf32>
+    %written = linalg.fill ins(%zero : f32) outs(%lhs : tensor<2xf32>)
+        -> tensor<2xf32>
+    %new = tensor.extract %written[%c0] : tensor<2xf32>
+    %old = tensor.extract %rhs[%c0] : tensor<2xf32>
+    %pair = arith.addf %new, %old : f32
+    %next = arith.addf %sum, %pair : f32
+    scf.yield %next : f32
+  }
+  return %result : f32
+}

>From 4eaa406b20553367aec9f9a7217789da168c315e Mon Sep 17 00:00:00 2001
From: Ziliang Zhang <zzl.coding at gmail.com>
Date: Sat, 10 Oct 2026 23:39:03 +0800
Subject: [PATCH 3/3] [mlir][bufferization] Avoid redundant subset disjointness
 proofs

Reject identical subset bounds before invoking ValueBounds and stop origin traversal at known overlapping bounds. Try matching-subset rules before the disjointness fallback.
---
 .../Transforms/OneShotAnalysis.cpp            | 59 +++++++++++--------
 1 file changed, 34 insertions(+), 25 deletions(-)

diff --git a/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp b/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
index 858fbc3bb2b99..3ab4771d6ced7 100644
--- a/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
+++ b/mlir/lib/Dialect/Bufferization/Transforms/OneShotAnalysis.cpp
@@ -724,16 +724,23 @@ static bool matchesInsertDestination(const AnalysisState &state,
 /// subset cannot be compared, continue through its source to try a larger
 /// bound. Other alias edges must be equivalent: an unknown alias does not
 /// guarantee containment. Unproven leaves and cycles conservatively fail the
-/// proof.
+/// proof. An origin equal to `overlappingSubset` or its container fails
+/// immediately: a larger enclosing bound cannot prove disjointness either.
 static bool
 hasDisjointSubsetOrigins(const AnalysisState &state, OpOperand *opOperand,
-                         function_ref<bool(SubsetOpInterface)> isDisjoint) {
+                         function_ref<bool(SubsetOpInterface)> isDisjoint,
+                         SubsetOpInterface overlappingSubset = {}) {
   SetVector<Value> worklist;
   DenseSet<Value> visited;
   worklist.insert(opOperand->get());
   while (!worklist.empty()) {
     Value value = worklist.pop_back_val();
+    if (overlappingSubset && value == overlappingSubset.getTensorContainer())
+      return false;
     auto extraction = value.getDefiningOp<SubsetExtractionOpInterface>();
+    if (extraction && overlappingSubset &&
+        extraction.getOperation() == overlappingSubset.getOperation())
+      return false;
     if (extraction &&
         isDisjoint(cast<SubsetOpInterface>(extraction.getOperation())))
       continue;
@@ -772,6 +779,10 @@ static bool areDisjointSubsetAccesses(OpOperand *uRead,
 
   auto readsAreDisjoint = [&](SubsetOpInterface writtenSubset) {
     auto isDisjoint = [&](SubsetOpInterface readSubset) {
+      // Conservatively treat identical access bounds as overlapping. In
+      // particular, do not invoke ValueBounds on a dynamic slice with itself.
+      if (readSubset == writtenSubset)
+        return false;
       return readSubset.operatesOnDisjointSubset(
           writtenSubset, [&](Value v1, Value v2) {
             return state.areEquivalentBufferizedValues(v1, v2);
@@ -781,7 +792,7 @@ static bool areDisjointSubsetAccesses(OpOperand *uRead,
       if (uRead == &extraction.getSourceOperand() &&
           isDisjoint(cast<SubsetOpInterface>(readingOp)))
         return true;
-    return hasDisjointSubsetOrigins(state, uRead, isDisjoint);
+    return hasDisjointSubsetOrigins(state, uRead, isDisjoint, writtenSubset);
   };
 
   // First try the exact subset written by an insertion op.
@@ -850,27 +861,6 @@ static bool areNonConflictingSubsets(OpOperand *uRead,
       return true;
   }
 
-  // A read from a subset does not conflict with a write to a disjoint subset
-  // of an equivalent tensor. The actual read or write may be further down an
-  // aliasing use-def chain. Trace such accesses back to subset extractions,
-  // crossing only equivalent alias edges or extraction source edges.
-  //
-  // %lhs = tensor.extract_slice %t[0][4][1]
-  // %rhs = tensor.extract_slice %t[4][4][1]
-  // %written = linalg.fill ins(%cst) outs(%lhs)
-  // return %written, %rhs
-  //
-  // Disjointness describes a single execution of the subset ops. If a read
-  // can observe writes from another iteration, the subset coordinates may
-  // differ across executions. Reuse the dominance checks to conservatively
-  // exclude such repetitive-region and CFG cases. Matching subset rules above
-  // and below do not rely on a disjointness proof and remain applicable.
-  const SetVector<Value> &definitions = state.findDefinitionsCached(uRead);
-  if ((definitions.empty() ||
-       canUseOpDominance(uRead, uConflictingWrite, definitions, state)) &&
-      areDisjointSubsetAccesses(uRead, uConflictingWrite, state))
-    return true;
-
   // If uConflictingWrite is an InsertSliceOp...
   if (auto subsetOp =
           dyn_cast<SubsetInsertionOpInterface>(conflictingWritingOp)) {
@@ -897,7 +887,26 @@ static bool areNonConflictingSubsets(OpOperand *uRead,
       return true;
   }
 
-  return false;
+  // A read from a subset does not conflict with a write to a disjoint subset
+  // of an equivalent tensor. The actual read or write may be further down an
+  // aliasing use-def chain. Trace such accesses back to subset extractions,
+  // crossing only equivalent alias edges or extraction source edges. Try the
+  // matching-subset rules above first to avoid unnecessary disjointness proofs.
+  //
+  // %lhs = tensor.extract_slice %t[0][4][1]
+  // %rhs = tensor.extract_slice %t[4][4][1]
+  // %written = linalg.fill ins(%cst) outs(%lhs)
+  // return %written, %rhs
+  //
+  // Disjointness describes a single execution of the subset ops. If a read
+  // can observe writes from another iteration, the subset coordinates may
+  // differ across executions. Reuse the dominance checks to conservatively
+  // exclude such repetitive-region and CFG cases. Matching subset rules above
+  // do not rely on a disjointness proof and remain applicable.
+  const SetVector<Value> &definitions = state.findDefinitionsCached(uRead);
+  return (definitions.empty() ||
+          canUseOpDominance(uRead, uConflictingWrite, definitions, state)) &&
+         areDisjointSubsetAccesses(uRead, uConflictingWrite, state);
 }
 
 /// Given sets of uses and writes, return true if there is a RaW conflict under



More information about the Mlir-commits mailing list