[Mlir-commits] [mlir] [mlir][affine] Treat failed dependence checks conservatively (PR #211014)
Takayuki Todokoro
llvmlistbot at llvm.org
Sat Jul 25 19:26:31 PDT 2026
https://github.com/takatodo updated https://github.com/llvm/llvm-project/pull/211014
>From 58adfdcb1eb0542803ada861317124ecbf9fbaa8 Mon Sep 17 00:00:00 2001
From: takatodo <takatodo1227 at gmail.com>
Date: Sun, 19 Jul 2026 17:57:36 +0900
Subject: [PATCH 1/2] [mlir][affine] Treat failed dependence checks
conservatively
Reject tiling and loop permutation when Affine dependence analysis cannot construct a complete relation, and preserve the original loop nest when sequential-loop sinking encounters the same failure. Add regressions for a semi-affine same-memref dependence.
Fixes #210585
Assisted-by: OpenAI Codex
---
.../Dialect/Affine/Analysis/AffineAnalysis.h | 4 +--
.../Affine/Analysis/AffineAnalysis.cpp | 5 +++-
.../Dialect/Affine/Analysis/LoopAnalysis.cpp | 5 +++-
mlir/lib/Dialect/Affine/Utils/LoopUtils.cpp | 6 +++--
.../Affine/loop-transformation-validity.mlir | 25 +++++++++++++++++++
5 files changed, 39 insertions(+), 6 deletions(-)
create mode 100644 mlir/test/Dialect/Affine/loop-transformation-validity.mlir
diff --git a/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h b/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h
index 3e4b8648061ff..6bfa887e95fd1 100644
--- a/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h
+++ b/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h
@@ -190,8 +190,8 @@ inline bool noDependence(DependenceResult result) {
/// Returns in 'depCompsVec', dependence components for dependences between all
/// load and store ops in loop nest rooted at 'forOp', at loop depths in range
-/// [1, maxLoopDepth].
-void getDependenceComponents(
+/// [1, maxLoopDepth]. Returns failure if any dependence cannot be analyzed.
+LogicalResult getDependenceComponents(
AffineForOp forOp, unsigned maxLoopDepth,
std::vector<SmallVector<DependenceComponent, 2>> *depCompsVec);
diff --git a/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp b/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp
index 3d1a73417d1ea..aef5ec85858a7 100644
--- a/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp
+++ b/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp
@@ -694,7 +694,7 @@ DependenceResult mlir::affine::checkMemrefAccessDependence(
/// Gathers dependence components for dependences between all ops in loop nest
/// rooted at 'forOp' at loop depths in range [1, maxLoopDepth].
-void mlir::affine::getDependenceComponents(
+LogicalResult mlir::affine::getDependenceComponents(
AffineForOp forOp, unsigned maxLoopDepth,
std::vector<SmallVector<DependenceComponent, 2>> *depCompsVec) {
// Collect all load and store ops in loop nest rooted at 'forOp'.
@@ -719,9 +719,12 @@ void mlir::affine::getDependenceComponents(
DependenceResult result = checkMemrefAccessDependence(
srcAccess, dstAccess, d, /*dependenceConstraints=*/nullptr,
&depComps);
+ if (result.value == DependenceResult::Failure)
+ return failure();
if (hasDependence(result))
depCompsVec->push_back(depComps);
}
}
}
+ return success();
}
diff --git a/mlir/lib/Dialect/Affine/Analysis/LoopAnalysis.cpp b/mlir/lib/Dialect/Affine/Analysis/LoopAnalysis.cpp
index 40802cc6e85e5..2dbc1baa02236 100644
--- a/mlir/lib/Dialect/Affine/Analysis/LoopAnalysis.cpp
+++ b/mlir/lib/Dialect/Affine/Analysis/LoopAnalysis.cpp
@@ -524,8 +524,11 @@ bool mlir::affine::isTilingValid(ArrayRef<AffineForOp> loops) {
srcAccess, dstAccess, d, /*dependenceConstraints=*/nullptr,
&depComps);
+ if (result.value == DependenceResult::Failure)
+ return false;
+
// Skip if there is no dependence in this case.
- if (!hasDependence(result))
+ if (noDependence(result))
continue;
// Check whether there is any negative direction vector in the
diff --git a/mlir/lib/Dialect/Affine/Utils/LoopUtils.cpp b/mlir/lib/Dialect/Affine/Utils/LoopUtils.cpp
index 90bc57e950cf1..bb002229d7b50 100644
--- a/mlir/lib/Dialect/Affine/Utils/LoopUtils.cpp
+++ b/mlir/lib/Dialect/Affine/Utils/LoopUtils.cpp
@@ -1357,7 +1357,8 @@ bool mlir::affine::isValidLoopInterchangePermutation(
// Gather dependence components for dependences between all ops in loop nest
// rooted at 'loops[0]', at loop depths in range [1, maxLoopDepth].
std::vector<SmallVector<DependenceComponent, 2>> depCompsVec;
- getDependenceComponents(loops[0], maxLoopDepth, &depCompsVec);
+ if (failed(getDependenceComponents(loops[0], maxLoopDepth, &depCompsVec)))
+ return false;
return checkLoopInterchangeDependences(depCompsVec, loops, loopPermMap);
}
@@ -1466,7 +1467,8 @@ AffineForOp mlir::affine::sinkSequentialLoops(AffineForOp forOp) {
// rooted at 'loops[0]', at loop depths in range [1, maxLoopDepth].
unsigned maxLoopDepth = loops.size();
std::vector<SmallVector<DependenceComponent, 2>> depCompsVec;
- getDependenceComponents(loops[0], maxLoopDepth, &depCompsVec);
+ if (failed(getDependenceComponents(loops[0], maxLoopDepth, &depCompsVec)))
+ return forOp;
// Mark loops as either parallel or sequential.
SmallVector<bool, 8> isParallelLoop(maxLoopDepth, true);
diff --git a/mlir/test/Dialect/Affine/loop-transformation-validity.mlir b/mlir/test/Dialect/Affine/loop-transformation-validity.mlir
new file mode 100644
index 0000000000000..b57e9ddcc357b
--- /dev/null
+++ b/mlir/test/Dialect/Affine/loop-transformation-validity.mlir
@@ -0,0 +1,25 @@
+// RUN: mlir-opt %s -test-loop-permutation="permutation-map=1,0 check-validity=1" | FileCheck %s
+// RUN: mlir-opt %s -affine-loop-tile="tile-size=4" | FileCheck %s
+
+#dynamic_index = affine_map<()[s0, s1] -> (s0 * s1)>
+
+// Dependence analysis cannot represent the common semi-affine index. The
+// remaining indices carry a (1, -1) dependence, so both transforms must fail.
+// CHECK-LABEL: func.func @unknown_dependence
+func.func @unknown_dependence(
+ %A: memref<?x9x9xi32>, %B: memref<9x9xi32>,
+ %p: index, %q: index, %value: i32) {
+ // CHECK: affine.for %[[I:.*]] = 1 to 8 {
+ // CHECK-NEXT: affine.for %[[J:.*]] = 1 to 8 {
+ affine.for %i = 1 to 8 {
+ affine.for %j = 1 to 8 {
+ %z = affine.apply #dynamic_index()[%p, %q]
+ // CHECK: affine.store %{{.*}}, %{{.*}}[%{{.*}}, %[[I]], %[[J]]]
+ affine.store %value, %A[%z, %i, %j] : memref<?x9x9xi32>
+ %loaded = affine.load %A[%z, %i - 1, %j + 1]
+ : memref<?x9x9xi32>
+ affine.store %loaded, %B[%i, %j] : memref<9x9xi32>
+ }
+ }
+ return
+}
>From 5789f7fdc90c045761a5d96d643633394bc1e6e3 Mon Sep 17 00:00:00 2001
From: takatodo <takatodo1227 at gmail.com>
Date: Sun, 26 Jul 2026 11:26:07 +0900
Subject: [PATCH 2/2] [mlir][affine] Distinguish possible from proven
dependences
---
.../mlir/Dialect/Affine/Analysis/AffineAnalysis.h | 12 +++++++++---
mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp | 2 +-
mlir/lib/Dialect/Affine/Utils/LoopFusionUtils.cpp | 2 +-
mlir/lib/Dialect/Affine/Utils/Utils.cpp | 2 +-
mlir/test/lib/Analysis/TestMemRefDependenceCheck.cpp | 2 +-
5 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h b/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h
index 6bfa887e95fd1..a001be9c42346 100644
--- a/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h
+++ b/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h
@@ -176,12 +176,18 @@ DependenceResult checkMemrefAccessDependence(
SmallVector<DependenceComponent, 2> *dependenceComponents = nullptr,
bool allowRAR = false);
-/// Utility function that returns true if the provided DependenceResult
-/// corresponds to a dependence result.
-inline bool hasDependence(DependenceResult result) {
+/// Returns true if the provided DependenceResult proves that a dependence
+/// exists.
+inline bool mustHaveDependence(DependenceResult result) {
return result.value == DependenceResult::HasDependence;
}
+/// Returns true unless the provided DependenceResult proves that no dependence
+/// exists.
+inline bool mayHaveDependence(DependenceResult result) {
+ return result.value != DependenceResult::NoDependence;
+}
+
/// Returns true if the provided DependenceResult corresponds to the absence of
/// a dependence.
inline bool noDependence(DependenceResult result) {
diff --git a/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp b/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp
index aef5ec85858a7..f86c5e0e8421e 100644
--- a/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp
+++ b/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp
@@ -721,7 +721,7 @@ LogicalResult mlir::affine::getDependenceComponents(
&depComps);
if (result.value == DependenceResult::Failure)
return failure();
- if (hasDependence(result))
+ if (mustHaveDependence(result))
depCompsVec->push_back(depComps);
}
}
diff --git a/mlir/lib/Dialect/Affine/Utils/LoopFusionUtils.cpp b/mlir/lib/Dialect/Affine/Utils/LoopFusionUtils.cpp
index 68296ea3368a1..984015943251e 100644
--- a/mlir/lib/Dialect/Affine/Utils/LoopFusionUtils.cpp
+++ b/mlir/lib/Dialect/Affine/Utils/LoopFusionUtils.cpp
@@ -229,7 +229,7 @@ static unsigned getMaxLoopDepth(ArrayRef<Operation *> srcOps,
// TODO: Cache dependence analysis results, check cache here.
DependenceResult result =
checkMemrefAccessDependence(srcAccess, dstAccess, d);
- if (hasDependence(result)) {
+ if (mayHaveDependence(result)) {
// Store minimum loop depth and break because we want the min 'd' at
// which there is a dependence.
loopDepth = std::min(loopDepth, d - 1);
diff --git a/mlir/lib/Dialect/Affine/Utils/Utils.cpp b/mlir/lib/Dialect/Affine/Utils/Utils.cpp
index 7043083298615..ec69aba8619f5 100644
--- a/mlir/lib/Dialect/Affine/Utils/Utils.cpp
+++ b/mlir/lib/Dialect/Affine/Utils/Utils.cpp
@@ -646,7 +646,7 @@ static bool mustReachAtInnermost(const MemRefAccess &srcAccess,
getNumCommonSurroundingLoops(*srcAccess.opInst, *destAccess.opInst);
DependenceResult result =
checkMemrefAccessDependence(srcAccess, destAccess, nsLoops + 1);
- return hasDependence(result);
+ return mustHaveDependence(result);
}
/// Returns true if `srcMemOp` may have an effect on `destMemOp` within the
diff --git a/mlir/test/lib/Analysis/TestMemRefDependenceCheck.cpp b/mlir/test/lib/Analysis/TestMemRefDependenceCheck.cpp
index b3b9a590773b0..b08e1e7cd1df0 100644
--- a/mlir/test/lib/Analysis/TestMemRefDependenceCheck.cpp
+++ b/mlir/test/lib/Analysis/TestMemRefDependenceCheck.cpp
@@ -89,7 +89,7 @@ static void checkDependences(ArrayRef<Operation *> loadsAndStores) {
if (result.value == DependenceResult::Failure) {
srcOpInst->emitError("dependence check failed");
} else {
- bool ret = hasDependence(result);
+ bool ret = mustHaveDependence(result);
// TODO: Print dependence type (i.e. RAW, etc) and print
// distance vectors as: ([2, 3], [0, 10]). Also, shorten distance
// vectors from ([1, 1], [3, 3]) to (1, 3).
More information about the Mlir-commits
mailing list