[Mlir-commits] [mlir] [MLIR][Affine] Refactor dependence testing to work on relations, not just on operations (PR #223188)

Uday Bondhugula llvmlistbot at llvm.org
Sat Sep 12 16:14:20 PDT 2026


https://github.com/bondhugula updated https://github.com/llvm/llvm-project/pull/223188

>From 9e779558c36c8b3e239afa4fe9fd01642bf112b5 Mon Sep 17 00:00:00 2001
From: Uday Bondhugula <uday at polymagelabs.com>
Date: Fri, 28 Aug 2026 10:02:15 +0530
Subject: [PATCH] [MLIR][Affine] Refactor dependence testing to work on
 relations, not just on operations

This change is effectively NFC for all users of affine dependence analysis
in the tree or outside. It performs a minor refactoring of the API to
allow more general usage.

`checkMemrefAccessDependence` takes two `MemRefAccess`es, each wrapping an
operation, so it can only be asked about accesses that already exist. A
transformation deciding whether to make a change has the opposite
question: whether the accesses it is about to create would depend on each
other. Loop fusion is one -- whether the stores of a slice would still be
one-to-one in the loops it is about to be placed under settles whether
their parallelism survives it, and it has to know before it fuses.

Nothing in the dependence analysis needs the operations. Everything from
the point where the two access relations are in hand -- inverting one,
composing, adding the ordering constraints, testing the result for
emptiness -- is already relation-only. Split that out as
`checkAccessDependence`, taking the relations and the loop depth, and
leave `checkMemrefAccessDependence` as the operation-based entry point:
it keeps what genuinely reads the IR, the memref and write-op checks,
the affine scope and common block ones, and the
source-precedes-destination one that a depth past the shared loops calls
for.

Split `MemRefAccess::getAccessRelation` the same way. The relation is
built out of an access map and an iteration domain, and only the two of
them being read off an operation ties it to one, so `getAccessRelation`
takes them directly and the method reads them off and calls it.

Assisted by Claude.
---
 .../Dialect/Affine/Analysis/AffineAnalysis.h  | 29 ++++++++++++
 .../Affine/Analysis/AffineAnalysis.cpp        | 44 ++++++++++++++-----
 2 files changed, 61 insertions(+), 12 deletions(-)

diff --git a/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h b/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h
index b06b7f552e6ec2..898dfdd288d6bf 100644
--- a/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h
+++ b/mlir/include/mlir/Dialect/Affine/Analysis/AffineAnalysis.h
@@ -176,6 +176,35 @@ DependenceResult checkMemrefAccessDependence(
     SmallVector<DependenceComponent, 2> *dependenceComponents = nullptr,
     bool allowRAR = false);
 
+/// Builds in `rel` the access relation of an access that reads or writes
+/// `accessValueMap` over the iteration `domain`, relating the iterations of
+/// the domain to the elements they touch. Unlike `MemRefAccess`'s method of
+/// the same name, it takes the access and its domain as they are, rather than
+/// reading them from an operation, so it can describe an access that is going
+/// to exist (for example, one a transformation is deciding whether to create)
+/// as well as one that already does.
+LogicalResult getAccessRelation(const AffineValueMap &accessValueMap,
+                                const FlatAffineValueConstraints &domain,
+                                presburger::IntegerRelation &rel);
+
+/// Checks whether the accesses two relations describe touch the same element,
+/// i.e. whether there is a dependence between them carried at `loopDepth`.
+/// This is `checkMemrefAccessDependence` from the point where it has nothing
+/// but the relations left to work on, so it serves an access built by hand out
+/// of an access map and a domain just as well as one read off an operation.
+///
+/// Returns 'NoDependence' if it can be determined conclusively that the two do
+/// not touch the same element. Note that a caller working from relations it
+/// built itself has to have established what the operation-based entry point
+/// checks on its own account: that the two access the same memref, that at
+/// least one of them writes, and, where `loopDepth` is past the loops they
+/// share, that the source precedes the destination.
+DependenceResult checkAccessDependence(
+    presburger::IntegerRelation srcRel, presburger::IntegerRelation dstRel,
+    unsigned loopDepth,
+    FlatAffineValueConstraints *dependenceConstraints = nullptr,
+    SmallVector<DependenceComponent, 2> *dependenceComponents = nullptr);
+
 /// Utility function that returns true if the provided DependenceResult
 /// corresponds to a dependence result.
 inline bool hasDependence(DependenceResult result) {
diff --git a/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp b/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp
index 4e496e0185579a..27a8c7b8ea8637 100644
--- a/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp
+++ b/mlir/lib/Dialect/Affine/Analysis/AffineAnalysis.cpp
@@ -456,15 +456,11 @@ static void computeDirectionVector(
   }
 }
 
-LogicalResult MemRefAccess::getAccessRelation(IntegerRelation &rel) const {
-  // Create set corresponding to domain of access.
-  FlatAffineValueConstraints domain;
-  if (failed(getOpIndexSet(opInst, &domain)))
-    return failure();
-
+LogicalResult
+mlir::affine::getAccessRelation(const AffineValueMap &accessValueMap,
+                                const FlatAffineValueConstraints &domain,
+                                IntegerRelation &rel) {
   // Get access relation from access map.
-  AffineValueMap accessValueMap;
-  getAccessMap(&accessValueMap);
   if (failed(getRelationFromMap(accessValueMap, rel)))
     return failure();
 
@@ -504,6 +500,17 @@ LogicalResult MemRefAccess::getAccessRelation(IntegerRelation &rel) const {
   return success();
 }
 
+LogicalResult MemRefAccess::getAccessRelation(IntegerRelation &rel) const {
+  // Create set corresponding to domain of access.
+  FlatAffineValueConstraints domain;
+  if (failed(getOpIndexSet(opInst, &domain)))
+    return failure();
+
+  AffineValueMap accessValueMap;
+  getAccessMap(&accessValueMap);
+  return mlir::affine::getAccessRelation(accessValueMap, domain, rel);
+}
+
 // Populates 'accessMap' with composition of AffineApplyOps reachable from
 // indices of MemRefAccess.
 void MemRefAccess::getAccessMap(AffineValueMap *accessMap) const {
@@ -651,16 +658,29 @@ DependenceResult mlir::affine::checkMemrefAccessDependence(
   // Note: this check is skipped if 'allowRAR' is true, because RAR deps
   // can exist irrespective of lexicographic ordering b/w src and dst.
   unsigned numCommonLoops = getNumCommonLoops(srcDomain, dstDomain);
-  assert(loopDepth <= numCommonLoops + 1);
+  assert(loopDepth <= numCommonLoops + 1 && "Invalid depth");
   if (!allowRAR && loopDepth > numCommonLoops &&
       !srcAppearsBeforeDstInAncestralBlock(srcAccess, dstAccess)) {
     return DependenceResult::NoDependence;
   }
 
+  return checkAccessDependence(srcRel, dstRel, loopDepth, dependenceConstraints,
+                               dependenceComponents);
+}
+
+DependenceResult mlir::affine::checkAccessDependence(
+    IntegerRelation srcRel, IntegerRelation dstRel, unsigned loopDepth,
+    FlatAffineValueConstraints *dependenceConstraints,
+    SmallVector<DependenceComponent, 2> *dependenceComponents) {
+  FlatAffineValueConstraints srcDomain(srcRel.getDomainSet());
+  FlatAffineValueConstraints dstDomain(dstRel.getDomainSet());
+  assert(loopDepth <= getNumCommonLoops(srcDomain, dstDomain) + 1 &&
+         "Invalid depth");
+
   // Compute the dependence relation by composing `srcRel` with the inverse of
-  // `dstRel`. Doing this builds a relation between iteration domain of
-  // `srcAccess` to the iteration domain of `dstAccess` which access the same
-  // memory locations.
+  // `dstRel`. Doing this builds a relation between the iteration domain of the
+  // source access to the iteration domain of the destination access which
+  // access the same memory locations.
   dstRel.inverse();
   // For 0-d spaces, there will be no IDs. Enable if that's the case.
   if (!dstRel.getSpace().isUsingIds())



More information about the Mlir-commits mailing list