[Mlir-commits] [mlir] [mlir][Affine] Handle memref.copy in loop fusion dependence checks (PR #203811)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Sun Jun 14 19:45:17 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Peter Chen J. (peter941221)

<details>
<summary>Changes</summary>

Fixes #<!-- -->203762

`mayDependence()` treated every non-affine memory op as if it had one memref operand. `memref.copy` lands in both the load and store effect buckets during loop-fusion dependence checks, so that path called `getMemRef(memref.copy)` and hit `llvm_unreachable("unexpected op")`.

This patch removes that single-memref assumption from the non-affine path. It checks whether each candidate op reads or writes the specific memref under analysis instead, so `memref.copy` works without changing the existing dependence walk for affine load/store pairs.

The regression covers the reported crash shape and keeps both memrefs carried by `memref.copy` in play: the source memref feeds a later `affine.load`, and the destination memref feeds a later `affine.store`.

Validation
build-mlir/bin/llvm-lit -sv -j1 mlir/test/Dialect/Affine/loop-fusion.mlir
build-mlir/bin/mlir-opt --affine-loop-fusion repro.mlir -o /dev/null

AI tool disclosure: I used OpenAI Codex to help inspect the failing code path, draft the patch, and draft the PR text. I reviewed and tested the final change myself before sending it for review.

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


2 Files Affected:

- (modified) mlir/lib/Dialect/Affine/Analysis/Utils.cpp (+6-16) 
- (modified) mlir/test/Dialect/Affine/loop-fusion.mlir (+27-1) 


``````````diff
diff --git a/mlir/lib/Dialect/Affine/Analysis/Utils.cpp b/mlir/lib/Dialect/Affine/Analysis/Utils.cpp
index ebe932a14694a..a63f19834832a 100644
--- a/mlir/lib/Dialect/Affine/Analysis/Utils.cpp
+++ b/mlir/lib/Dialect/Affine/Analysis/Utils.cpp
@@ -243,19 +243,6 @@ addNodeToMDG(Operation *nodeOp, MemRefDependenceGraph &mdg,
   return &node;
 }
 
-/// Returns the memref being read/written by a memref/affine load/store op.
-static Value getMemRef(Operation *memOp) {
-  if (auto memrefLoad = dyn_cast<memref::LoadOp>(memOp))
-    return memrefLoad.getMemRef();
-  if (auto affineLoad = dyn_cast<AffineReadOpInterface>(memOp))
-    return affineLoad.getMemRef();
-  if (auto memrefStore = dyn_cast<memref::StoreOp>(memOp))
-    return memrefStore.getMemRef();
-  if (auto affineStore = dyn_cast<AffineWriteOpInterface>(memOp))
-    return affineStore.getMemRef();
-  llvm_unreachable("unexpected op");
-}
-
 /// Returns true if there may be a dependence on `memref` from srcNode's
 /// memory ops to dstNode's memory ops, while using the affine memory
 /// dependence analysis checks. The method assumes that there is at least one
@@ -275,12 +262,15 @@ static bool mayDependence(const Node &srcNode, const Node &dstNode,
   // destination read/write one; all expected to be memref/affine load/store.
   auto hasNonAffineDep = [&](ArrayRef<Operation *> srcMemOps,
                              ArrayRef<Operation *> dstMemOps) {
+    auto accessesMemref = [&](Operation *op) {
+      return hasEffect<MemoryEffects::Read>(op, memref) ||
+             hasEffect<MemoryEffects::Write>(op, memref);
+    };
     return llvm::any_of(srcMemOps, [&](Operation *srcOp) {
-      Value srcMemref = getMemRef(srcOp);
-      if (srcMemref != memref)
+      if (!accessesMemref(srcOp))
         return false;
       return llvm::find_if(dstMemOps, [&](Operation *dstOp) {
-               return srcMemref == getMemRef(dstOp);
+               return accessesMemref(dstOp);
              }) != dstMemOps.end();
     });
   };
diff --git a/mlir/test/Dialect/Affine/loop-fusion.mlir b/mlir/test/Dialect/Affine/loop-fusion.mlir
index 1ea42517988c3..5f72bf79e0ac7 100644
--- a/mlir/test/Dialect/Affine/loop-fusion.mlir
+++ b/mlir/test/Dialect/Affine/loop-fusion.mlir
@@ -13,6 +13,33 @@
 
 // -----
 
+// CHECK-LABEL: func @memref_copy_in_loop_nest_does_not_crash() {
+func.func @memref_copy_in_loop_nest_does_not_crash() {
+  %mem = memref.alloca() : memref<10xi32>
+  %mem2 = memref.alloca() : memref<10xi32>
+  %c0 = arith.constant 0 : i32
+
+  affine.for %i = 0 to 10 {
+    memref.copy %mem, %mem2 : memref<10xi32> to memref<10xi32>
+    affine.store %c0, %mem[%i] : memref<10xi32>
+  }
+  affine.for %j = 0 to 10 {
+    %v = affine.load %mem[%j] : memref<10xi32>
+  }
+  affine.for %k = 0 to 10 {
+    affine.store %c0, %mem2[%k] : memref<10xi32>
+  }
+
+  // CHECK:      memref.copy
+  // CHECK:      affine.store
+  // CHECK:      affine.load
+  // CHECK:      affine.store
+  // CHECK:      return
+  return
+}
+
+// -----
+
 // CHECK-LABEL: func @should_fuse_raw_dep_for_locality() {
 func.func @should_fuse_raw_dep_for_locality() {
   %m = memref.alloc() : memref<10xf32>
@@ -1575,4 +1602,3 @@ func.func @producer_consumer_with_outmost_user(%arg0 : f16) {
 }
 
 // Add further tests in mlir/test/Transforms/loop-fusion-4.mlir
-

``````````

</details>


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


More information about the Mlir-commits mailing list