[Mlir-commits] [mlir] [mlir][linalg] Hoist transfer pairs through disjoint subview slices (PR #220244)

Matthias Springer llvmlistbot at llvm.org
Wed Sep 2 00:19:12 PDT 2026


================
@@ -195,6 +198,116 @@ static bool noAliasingUseInLoop(vector::TransferReadOp transferRead,
   return true;
 }
 
+namespace {
+/// Static footprint of a memref value resolved through a chain of
+/// rank-preserving, unit-stride `memref.subview` ops: the underlying root
+/// memref and, per root dimension, the `[offset, offset + size)` interval it
+/// covers.
+struct SubviewFootprint {
+  Value root;
+  SmallVector<int64_t> offsets;
+  SmallVector<int64_t> sizes;
+};
+} // namespace
+
+/// Resolve `base` through a `memref.subview` chain to its static footprint, or
+/// `std::nullopt` if it cannot be modelled statically.
+static std::optional<SubviewFootprint> resolveSubviewFootprint(Value base) {
+  SmallVector<memref::SubViewOp> chain;
+  Value source = base;
+  while (auto sv = source.getDefiningOp<memref::SubViewOp>()) {
+    chain.push_back(sv);
+    source = sv.getSource();
+  }
+  // The resolved root must be a genuine buffer, not another kind of view.
+  if (isa_and_nonnull<ViewLikeOpInterface>(source.getDefiningOp()))
+    return std::nullopt;
+
+  auto rootTy = dyn_cast<MemRefType>(source.getType());
+  if (!rootTy || !rootTy.hasStaticShape())
+    return std::nullopt;
+  SubviewFootprint fp;
+  fp.root = source;
+  // Seed with the whole-root footprint (offset 0, full extent per dimension):
+  // this is the result for a bare buffer, and each subview in the chain narrows
+  // it below.
+  fp.offsets.assign(rootTy.getRank(), 0);
+  fp.sizes.assign(rootTy.getShape().begin(), rootTy.getShape().end());
+  // Compose from the outermost subview (closest to the root) inward.
+  for (memref::SubViewOp sv : llvm::reverse(chain)) {
+    // Rank-reducing subviews would misalign the per-dimension bookkeeping.
+    if (sv.getSourceType().getRank() != sv.getType().getRank())
+      return std::nullopt;
+    ArrayRef<int64_t> offs = sv.getStaticOffsets();
+    ArrayRef<int64_t> szs = sv.getStaticSizes();
+    ArrayRef<int64_t> strides = sv.getStaticStrides();
+    if (llvm::any_of(offs, ShapedType::isDynamic) ||
+        llvm::any_of(szs, ShapedType::isDynamic) ||
+        llvm::any_of(strides, [](int64_t s) { return s != 1; }))
+      return std::nullopt;
+    for (unsigned d = 0, e = offs.size(); d < e; ++d) {
+      fp.offsets[d] += offs[d];
+      fp.sizes[d] = szs[d];
+    }
+  }
+  return fp;
+}
+
+/// True if two footprints provably cover disjoint memory: they must share the
+/// same root and be separated along at least one dimension.
+static bool footprintsDisjoint(const SubviewFootprint &a,
+                               const SubviewFootprint &b) {
+  if (a.root != b.root)
+    return false;
+  for (unsigned d = 0, e = a.offsets.size(); d < e; ++d) {
+    int64_t aHi = a.offsets[d] + a.sizes[d];
+    int64_t bHi = b.offsets[d] + b.sizes[d];
+    if (aHi <= b.offsets[d] || bHi <= a.offsets[d])
+      return true;
+  }
+  return false;
+}
+
+/// Subview-aware safety check for hoisting `read`/`write` out of `loop`.
+static bool footprintAliasingIsSafe(LoopLikeOpInterface loop,
----------------
matthias-springer wrote:

`SubsetOpInterface` is for ops with tensor semantics. The IR here seems to have memref semantics. (It could be generalized to support memrefs as well.)

https://github.com/llvm/llvm-project/pull/220244


More information about the Mlir-commits mailing list