Adds support for returning the direction of the dependence between memref accesses...
authorMLIR Team <no-reply@google.com>
Fri, 9 Nov 2018 17:42:24 +0000 (09:42 -0800)
committerjpienaar <jpienaar@google.com>
Fri, 29 Mar 2019 20:53:28 +0000 (13:53 -0700)
Updates MemRefDependenceCheck to check and report on all memref access pairs at all loop nest depths.
Updates old and adds new memref dependence check tests.
Resolves multiple TODOs.

PiperOrigin-RevId: 220816515

mlir/include/mlir/Analysis/AffineAnalysis.h
mlir/lib/Analysis/AffineAnalysis.cpp
mlir/lib/Analysis/MemRefDependenceCheck.cpp
mlir/lib/Transforms/LoopFusion.cpp
mlir/test/Transforms/memref-dependence-check.mlir

index a3fe813c5486ba0c4c6553f72a577e882edf9634..64859f7a63b65bf088926d186d0fad0c086b6a91 100644 (file)
@@ -72,11 +72,6 @@ bool getFlattenedAffineExpr(AffineExpr expr, unsigned numDims,
 bool addIndexSet(llvm::ArrayRef<const MLValue *> indices,
                  FlatAffineConstraints *domain);
 
-/// Checks whether two accesses to the same memref access the same element.
-/// Each access is specified using the MemRefAccess structure, which contains
-/// the operation statement, indices and memref associated with the access.
-/// Returns 'false' if it can be determined conclusively that the accesses do
-/// not access the same memref element. Returns 'true' otherwise.
 struct MemRefAccess {
   const MLValue *memref;
   const OperationStmt *opStmt;
@@ -85,9 +80,30 @@ struct MemRefAccess {
   // 'indices'.
   void getAccessMap(AffineValueMap *accessMap) const;
 };
-bool checkMemrefAccessDependence(const MemRefAccess &srcAccess,
-                                 const MemRefAccess &dstAccess);
 
+// DependenceComponent contains state about the direction of a dependence as an
+// interval [lb, ub].
+// Distance vectors components are represented by the interval [lb, ub] with
+// lb == ub.
+// Direction vectors components are represented by the interval [lb, ub] with
+// lb < ub. Note that ub/lb == None means unbounded.
+struct DependenceComponent {
+  // The lower bound of the dependence distance.
+  llvm::Optional<int64_t> lb;
+  // The upper bound of the dependence distance (inclusive).
+  llvm::Optional<int64_t> ub;
+  DependenceComponent() : lb(llvm::None), ub(llvm::None) {}
+};
+
+/// Checks whether two accesses to the same memref access the same element.
+/// Each access is specified using the MemRefAccess structure, which contains
+/// the operation statement, indices and memref associated with the access.
+/// Returns 'false' if it can be determined conclusively that the accesses do
+/// not access the same memref element. Returns 'true' otherwise.
+bool checkMemrefAccessDependence(
+    const MemRefAccess &srcAccess, const MemRefAccess &dstAccess,
+    unsigned loopDepth,
+    llvm::SmallVector<DependenceComponent, 2> *dependenceComponents);
 } // end namespace mlir
 
 #endif // MLIR_ANALYSIS_AFFINE_ANALYSIS_H
index a668b8eb2e6f14b4f1ba4abfb90bcc8ff93f20a5..5539cbc2c87b7f289c74cdee7974ea3a0d45ec0e 100644 (file)
 
 #include "mlir/Analysis/AffineAnalysis.h"
 #include "mlir/Analysis/AffineStructures.h"
+#include "mlir/Analysis/Utils.h"
 #include "mlir/IR/AffineExprVisitor.h"
 #include "mlir/IR/BuiltinOps.h"
 #include "mlir/IR/Statements.h"
 #include "mlir/StandardOps/StandardOps.h"
+#include "mlir/Support/MathExtras.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -441,6 +443,9 @@ bool mlir::addIndexSet(ArrayRef<const MLValue *> indices,
 
 // IterationDomainContext encapsulates the state required to represent
 // the iteration domain of an OperationStmt.
+// TODO(andydavis) Move this into FlatAffineConstraints when we have shared
+// code to manage the operand values and positions to use FlatAffineConstraints
+// and AffineValueMap.
 struct IterationDomainContext {
   // Set of inequality constraint pairs, where each pair represents the
   // upper/lower bounds of a ForStmt in the iteration domain.
@@ -452,8 +457,8 @@ struct IterationDomainContext {
   // [numDims, values.size()) representing symbol identifiers.
   SmallVector<const MLValue *, 4> values;
   IterationDomainContext() : numDims(0) {}
-  unsigned getNumDims() { return numDims; }
-  unsigned getNumSymbols() { return values.size() - numDims; }
+  unsigned getNumDims() const { return numDims; }
+  unsigned getNumSymbols() const { return values.size() - numDims; }
 };
 
 // Computes the iteration domain for 'opStmt' and populates 'ctx', which
@@ -465,11 +470,8 @@ struct IterationDomainContext {
 // TODO(andydavis) Handle non-constant loop bounds by composing affine maps
 // for each ForStmt loop bound and adding de-duped ids/symbols to iteration
 // domain context.
-// TODO(andydavis) Capture the context of the symbols. For example, check
-// if a symbol is the result of a constant operation, and set the symbol to
-// that value in FlatAffineConstraints (using setIdToConstant).
-bool getIterationDomainContext(const Statement *stmt,
-                               IterationDomainContext *ctx) {
+static bool getIterationDomainContext(const Statement *stmt,
+                                      IterationDomainContext *ctx) {
   // Walk up tree storing parent statements in 'loops'.
   // TODO(andydavis) Extend this to gather enclosing IfStmts and consider
   // factoring it out into a utility function.
@@ -505,73 +507,121 @@ bool getIterationDomainContext(const Statement *stmt,
   return addIndexSet(ctx->values, &ctx->domain);
 }
 
+// ValuePositionMap manages the mapping from MLValues which represent dimension
+// and symbol identifiers from 'src' and 'dst' access functions to positions
+// in new space where some MLValues are kept separate (using addSrc/DstValue)
+// and some MLValues are merged (addSymbolValue).
+// Position lookups return the absolute position in the new space which
+// has the following format:
+//
+//   [src-dim-identifiers] [dst-dim-identifiers] [symbol-identifers]
+//
+// Note: access function non-IV dimension identifiers (that have 'dimension'
+// positions in the access function position space) are assigned as symbols
+// in the output position space. Convienience access functions which lookup
+// an MLValue in multiple maps are provided (i.e. getSrcDimOrSymPos) to handle
+// the common case of resolving positions for all access function operands.
+//
+// TODO(andydavis) Generalize this: could take a template parameter for
+// the number of maps (3 in the current case), and lookups could take indices
+// of maps to check. So getSrcDimOrSymPos would be "getPos(value, {0, 2})".
+class ValuePositionMap {
+public:
+  void addSrcValue(const MLValue *value) {
+    if (addValueAt(value, &srcDimPosMap, numSrcDims))
+      ++numSrcDims;
+  }
+  void addDstValue(const MLValue *value) {
+    if (addValueAt(value, &dstDimPosMap, numDstDims))
+      ++numDstDims;
+  }
+  void addSymbolValue(const MLValue *value) {
+    if (addValueAt(value, &symbolPosMap, numSymbols))
+      ++numSymbols;
+  }
+  unsigned getSrcDimOrSymPos(const MLValue *value) const {
+    return getDimOrSymPos(value, srcDimPosMap, 0);
+  }
+  unsigned getDstDimOrSymPos(const MLValue *value) const {
+    return getDimOrSymPos(value, dstDimPosMap, numSrcDims);
+  }
+  unsigned getSymPos(const MLValue *value) const {
+    auto it = symbolPosMap.find(value);
+    assert(it != symbolPosMap.end());
+    return numSrcDims + numDstDims + it->second;
+  }
+
+  unsigned getNumSrcDims() const { return numSrcDims; }
+  unsigned getNumDstDims() const { return numDstDims; }
+  unsigned getNumDims() const { return numSrcDims + numDstDims; }
+  unsigned getNumSymbols() const { return numSymbols; }
+
+private:
+  bool addValueAt(const MLValue *value,
+                  DenseMap<const MLValue *, unsigned> *posMap,
+                  unsigned position) {
+    auto it = posMap->find(value);
+    if (it == posMap->end()) {
+      (*posMap)[value] = position;
+      return true;
+    }
+    return false;
+  }
+  unsigned getDimOrSymPos(const MLValue *value,
+                          const DenseMap<const MLValue *, unsigned> &dimPosMap,
+                          unsigned dimPosOffset) const {
+    auto it = dimPosMap.find(value);
+    if (it != dimPosMap.end()) {
+      return dimPosOffset + it->second;
+    }
+    it = symbolPosMap.find(value);
+    assert(it != symbolPosMap.end());
+    return numSrcDims + numDstDims + it->second;
+  }
+
+  unsigned numSrcDims = 0;
+  unsigned numDstDims = 0;
+  unsigned numSymbols = 0;
+  DenseMap<const MLValue *, unsigned> srcDimPosMap;
+  DenseMap<const MLValue *, unsigned> dstDimPosMap;
+  DenseMap<const MLValue *, unsigned> symbolPosMap;
+};
+
 // Builds a map from MLValue to identifier position in a new merged identifier
 // list, which is the result of merging dim/symbol lists from src/dst
 // iteration domains. The format of the new merged list is as follows:
 //
 //   [src-dim-identifiers, dst-dim-identifiers, symbol-identifiers]
 //
-// This method populates 'srcDimPosMap' and 'dstDimPosMap' with mappings from
-// operand MLValues in 'srcAccessMap'/'dstAccessMap' to the position of these
-// values in the merged list.
-// In addition, this method populates 'symbolPosMap' with mappings from
-// operand MLValues in both 'srcIterationDomainContext' and
-// 'dstIterationDomainContext' to position of these values in the merged list.
+// This method populates 'valuePosMap' with mappings from operand MLValues in
+// 'srcAccessMap'/'dstAccessMap' (as well as those in
+// 'srcIterationDomainContext'/'dstIterationDomainContext') to the position of
+// these values in the merged list.
 static void buildDimAndSymbolPositionMaps(
     const IterationDomainContext &srcIterationDomainContext,
     const IterationDomainContext &dstIterationDomainContext,
     const AffineValueMap &srcAccessMap, const AffineValueMap &dstAccessMap,
-    DenseMap<const MLValue *, unsigned> *srcDimPosMap,
-    DenseMap<const MLValue *, unsigned> *dstDimPosMap,
-    DenseMap<const MLValue *, unsigned> *symbolPosMap) {
-  unsigned pos = 0;
-
-  auto updatePosMap = [&](DenseMap<const MLValue *, unsigned> *posMap,
-                          ArrayRef<const MLValue *> values, unsigned start,
-                          unsigned limit) {
-    for (unsigned i = start; i < limit; ++i) {
+    ValuePositionMap *valuePosMap) {
+  auto updateValuePosMap = [&](ArrayRef<const MLValue *> values, bool isSrc) {
+    for (unsigned i = 0, e = values.size(); i < e; ++i) {
       auto *value = values[i];
-      auto it = posMap->find(value);
-      if (it == posMap->end()) {
-        (*posMap)[value] = pos++;
-      }
+      if (!isa<ForStmt>(values[i]))
+        valuePosMap->addSymbolValue(value);
+      else if (isSrc)
+        valuePosMap->addSrcValue(value);
+      else
+        valuePosMap->addDstValue(value);
     }
   };
 
-  AffineMap srcMap = srcAccessMap.getAffineMap();
-  AffineMap dstMap = dstAccessMap.getAffineMap();
-
-  // Update position map with src dimension identifiers from iteration domain
-  // and access function.
-  updatePosMap(srcDimPosMap, srcIterationDomainContext.values, 0,
-               srcIterationDomainContext.numDims);
-  // Update position map with 'srcAccessMap' operands not in iteration domain.
-  updatePosMap(srcDimPosMap, srcAccessMap.getOperands(), 0,
-               srcMap.getNumDims());
-
-  // Update position map with dst dimension identifiers from iteration domain
-  // and access function.
-  updatePosMap(dstDimPosMap, dstIterationDomainContext.values, 0,
-               dstIterationDomainContext.numDims);
-  // Update position map with 'dstAccessMap' operands not in iteration domain.
-  updatePosMap(dstDimPosMap, dstAccessMap.getOperands(), 0,
-               dstMap.getNumDims());
-
-  // Update position map with src symbol identifiers from iteration domain
-  // and access function.
-  updatePosMap(symbolPosMap, srcIterationDomainContext.values,
-               dstIterationDomainContext.numDims,
-               srcIterationDomainContext.values.size());
-  updatePosMap(symbolPosMap, srcAccessMap.getOperands(), srcMap.getNumDims(),
-               srcMap.getNumDims() + srcMap.getNumSymbols());
-
-  // Update position map with dst symbol identifiers from iteration domain
-  // and access function.
-  updatePosMap(symbolPosMap, dstIterationDomainContext.values,
-               dstIterationDomainContext.numDims,
-               dstIterationDomainContext.values.size());
-  updatePosMap(symbolPosMap, dstAccessMap.getOperands(), dstMap.getNumDims(),
-               dstMap.getNumDims() + dstMap.getNumSymbols());
+  // Update value position map with identifiers from src iteration domain.
+  updateValuePosMap(srcIterationDomainContext.values, /*isSrc=*/true);
+  // Update value position map with identifiers from dst iteration domain.
+  updateValuePosMap(dstIterationDomainContext.values, /*isSrc=*/false);
+  // Update value position map with identifiers from src access function.
+  updateValuePosMap(srcAccessMap.getOperands(), /*isSrc=*/true);
+  // Update value position map with identifiers from dst access function.
+  updateValuePosMap(dstAccessMap.getOperands(), /*isSrc=*/false);
 }
 
 static unsigned getPos(const DenseMap<const MLValue *, unsigned> &posMap,
@@ -581,41 +631,55 @@ static unsigned getPos(const DenseMap<const MLValue *, unsigned> &posMap,
   return it->second;
 }
 
-// Adds iteration domain constraints from 'ctx.domain' into 'dependenceDomain'.
-// Uses 'dimPosMap' to map from dim operand value in 'ctx.values', to dim
-// position in 'dependenceDomain'.
-// Uses 'symbolPosMap' to map from symbol operand value in 'ctx.values', to
-// symbol position in 'dependenceDomain'.
-static void
-addDomainConstraints(const IterationDomainContext &ctx,
-                     const DenseMap<const MLValue *, unsigned> &dimPosMap,
-                     const DenseMap<const MLValue *, unsigned> &symbolPosMap,
-                     FlatAffineConstraints *dependenceDomain) {
-  unsigned inputNumIneq = ctx.domain.getNumInequalities();
-  unsigned inputNumDims = ctx.domain.getNumDimIds();
-  unsigned inputNumSymbols = ctx.domain.getNumSymbolIds();
-  unsigned inputNumIds = inputNumDims + inputNumSymbols;
+// Adds iteration domain constraints from 'srcCtx' and 'dstCtx' into
+// 'dependenceDomain'.
+// Uses 'valuePosMap' to map from operand values in 'ctx.values' to position in
+// 'dependenceDomain'.
+static void addDomainConstraints(const IterationDomainContext &srcCtx,
+                                 const IterationDomainContext &dstCtx,
+                                 const ValuePositionMap &valuePosMap,
+                                 FlatAffineConstraints *dependenceDomain) {
+  unsigned srcNumIneq = srcCtx.domain.getNumInequalities();
+  unsigned srcNumDims = srcCtx.domain.getNumDimIds();
+  unsigned srcNumSymbols = srcCtx.domain.getNumSymbolIds();
+  unsigned srcNumIds = srcNumDims + srcNumSymbols;
+
+  unsigned dstNumIneq = dstCtx.domain.getNumInequalities();
+  unsigned dstNumDims = dstCtx.domain.getNumDimIds();
+  unsigned dstNumSymbols = dstCtx.domain.getNumSymbolIds();
+  unsigned dstNumIds = dstNumDims + dstNumSymbols;
 
   unsigned outputNumDims = dependenceDomain->getNumDimIds();
   unsigned outputNumSymbols = dependenceDomain->getNumSymbolIds();
   unsigned outputNumIds = outputNumDims + outputNumSymbols;
 
-  SmallVector<int64_t, 4> eq;
-  eq.resize(outputNumIds + 1);
-  for (unsigned i = 0; i < inputNumIneq; ++i) {
+  SmallVector<int64_t, 4> ineq;
+  ineq.resize(outputNumIds + 1);
+  // Add inequalities from src domain.
+  for (unsigned i = 0; i < srcNumIneq; ++i) {
     // Zero fill.
-    std::fill(eq.begin(), eq.end(), 0);
-    // Add dim identifiers.
-    for (unsigned j = 0; j < inputNumDims; ++j)
-      eq[getPos(dimPosMap, ctx.values[j])] = ctx.domain.atIneq(i, j);
-    // Add symbol identifiers.
-    for (unsigned j = inputNumDims; j < inputNumIds; ++j) {
-      eq[getPos(symbolPosMap, ctx.values[j])] = ctx.domain.atIneq(i, j);
-    }
-    // Add constant term.
-    eq[outputNumIds] = ctx.domain.atIneq(i, inputNumIds);
+    std::fill(ineq.begin(), ineq.end(), 0);
+    // Set coefficients for identifiers corresponding to src domain.
+    for (unsigned j = 0; j < srcNumIds; ++j)
+      ineq[valuePosMap.getSrcDimOrSymPos(srcCtx.values[j])] =
+          srcCtx.domain.atIneq(i, j);
+    // Set constant term.
+    ineq[outputNumIds] = srcCtx.domain.atIneq(i, srcNumIds);
+    // Add inequality constraint.
+    dependenceDomain->addInequality(ineq);
+  }
+  // Add inequalities from dst domain.
+  for (unsigned i = 0; i < dstNumIneq; ++i) {
+    // Zero fill.
+    std::fill(ineq.begin(), ineq.end(), 0);
+    // Set coefficients for identifiers corresponding to dst domain.
+    for (unsigned j = 0; j < dstNumIds; ++j)
+      ineq[valuePosMap.getDstDimOrSymPos(dstCtx.values[j])] =
+          dstCtx.domain.atIneq(i, j);
+    // Set constant term.
+    ineq[outputNumIds] = dstCtx.domain.atIneq(i, dstNumIds);
     // Add inequality constraint.
-    dependenceDomain->addInequality(eq);
+    dependenceDomain->addInequality(ineq);
   }
 }
 
@@ -640,12 +704,13 @@ addDomainConstraints(const IterationDomainContext &ctx,
 //   a0     -c0      (a1 - c1)  (a1 - c2) = 0
 //   b0     -f0      (b1 - f1)  (b1 - f2) = 0
 //
-bool addMemRefAccessConstraints(
-    const AffineValueMap &srcAccessMap, const AffineValueMap &dstAccessMap,
-    const DenseMap<const MLValue *, unsigned> &srcDimPosMap,
-    const DenseMap<const MLValue *, unsigned> &dstDimPosMap,
-    const DenseMap<const MLValue *, unsigned> &symbolPosMap,
-    FlatAffineConstraints *dependenceDomain) {
+// Returns false if any AffineExpr cannot be flattened (which will be removed
+// when mod/floor/ceil support is added). Returns true otherwise.
+static bool
+addMemRefAccessConstraints(const AffineValueMap &srcAccessMap,
+                           const AffineValueMap &dstAccessMap,
+                           const ValuePositionMap &valuePosMap,
+                           FlatAffineConstraints *dependenceDomain) {
   AffineMap srcMap = srcAccessMap.getAffineMap();
   AffineMap dstMap = dstAccessMap.getAffineMap();
   assert(srcMap.getNumResults() == dstMap.getNumResults());
@@ -665,8 +730,7 @@ bool addMemRefAccessConstraints(
   unsigned outputNumSymbols = dependenceDomain->getNumSymbolIds();
   unsigned outputNumIds = outputNumDims + outputNumSymbols;
 
-  SmallVector<int64_t, 4> eq;
-  eq.resize(outputNumIds + 1);
+  SmallVector<int64_t, 4> eq(outputNumIds + 1);
   SmallVector<int64_t, 4> flattenedExpr;
   for (unsigned i = 0; i < numResults; ++i) {
     // Zero fill.
@@ -677,13 +741,10 @@ bool addMemRefAccessConstraints(
     if (!getFlattenedAffineExpr(srcExpr, srcNumDims, srcNumSymbols,
                                 &flattenedExpr))
       return false;
-    // Add dim identifier coefficients from src access function.
-    for (unsigned j = 0, e = srcNumDims; j < e; ++j)
-      eq[getPos(srcDimPosMap, srcOperands[j])] = flattenedExpr[j];
-    // Add symbol identifiers from src access function.
-    for (unsigned j = srcNumDims; j < srcNumIds; ++j)
-      eq[getPos(symbolPosMap, srcOperands[j])] = flattenedExpr[j];
-    // Add constant term.
+    // Set identifier coefficients from src access function.
+    for (unsigned j = 0, e = srcOperands.size(); j < e; ++j)
+      eq[valuePosMap.getSrcDimOrSymPos(srcOperands[j])] = flattenedExpr[j];
+    // Set constant term.
     eq[outputNumIds] = flattenedExpr[srcNumIds];
 
     // Get flattened AffineExpr for result 'i' from dst access function.
@@ -692,49 +753,218 @@ bool addMemRefAccessConstraints(
     if (!getFlattenedAffineExpr(dstExpr, dstNumDims, dstNumSymbols,
                                 &flattenedExpr))
       return false;
-    // Add dim identifier coefficients from dst access function.
-    for (unsigned j = 0, e = dstNumDims; j < e; ++j)
-      eq[getPos(dstDimPosMap, dstOperands[j])] = -flattenedExpr[j];
-    // Add symbol identifiers from dst access function.
-    for (unsigned j = dstNumDims; j < dstNumIds; ++j)
-      eq[getPos(symbolPosMap, dstOperands[j])] -= flattenedExpr[j];
-    // Add constant term.
+    // Set identifier coefficients from dst access function.
+    for (unsigned j = 0, e = dstOperands.size(); j < e; ++j)
+      eq[valuePosMap.getDstDimOrSymPos(dstOperands[j])] -= flattenedExpr[j];
+    // Set constant term.
     eq[outputNumIds] -= flattenedExpr[dstNumIds];
     // Add equality constraint.
     dependenceDomain->addEquality(eq);
   }
 
   // Add equality constraints for any operands that are defined by constant ops.
-  auto addEqForConstOperands =
-      [&](const DenseMap<const MLValue *, unsigned> &posMap,
-          ArrayRef<const MLValue *> operands, unsigned start, unsigned limit) {
-        for (unsigned i = start; i < limit; ++i) {
-          if (isa<ForStmt>(operands[i]))
-            continue;
-          auto *symbol = operands[i];
-          assert(symbol->isValidSymbol());
-          // Check if the symbols is a constant.
-          if (auto *opStmt = symbol->getDefiningStmt()) {
-            if (auto constOp = opStmt->dyn_cast<ConstantIndexOp>()) {
-              dependenceDomain->setIdToConstant(getPos(posMap, symbol),
-                                                constOp->getValue());
-            }
-          }
+  auto addEqForConstOperands = [&](ArrayRef<const MLValue *> operands) {
+    for (unsigned i = 0, e = operands.size(); i < e; ++i) {
+      if (isa<ForStmt>(operands[i]))
+        continue;
+      auto *symbol = operands[i];
+      assert(symbol->isValidSymbol());
+      // Check if the symbol is a constant.
+      if (auto *opStmt = symbol->getDefiningStmt()) {
+        if (auto constOp = opStmt->dyn_cast<ConstantIndexOp>()) {
+          dependenceDomain->setIdToConstant(valuePosMap.getSymPos(symbol),
+                                            constOp->getValue());
         }
-      };
+      }
+    }
+  };
 
-  // Add equality constraints for any src dims defined by constant ops.
-  addEqForConstOperands(srcDimPosMap, srcOperands, 0, srcNumDims);
   // Add equality constraints for any src symbols defined by constant ops.
-  addEqForConstOperands(symbolPosMap, srcOperands, srcNumDims, srcNumIds);
-  // Add equality constraints for any dst dims defined by constant ops.
-  addEqForConstOperands(dstDimPosMap, dstOperands, 0, dstNumDims);
+  addEqForConstOperands(srcOperands);
   // Add equality constraints for any dst symbols defined by constant ops.
-  addEqForConstOperands(symbolPosMap, dstOperands, dstNumDims, dstNumIds);
+  addEqForConstOperands(dstOperands);
+  return true;
+}
+
+// Returns the number of outer loop common to 'src/dstIterationDomainContext'.
+static unsigned
+getNumCommonLoops(const IterationDomainContext &srcIterationDomainContext,
+                  const IterationDomainContext &dstIterationDomainContext) {
+  // Find the number of common loops shared by src and dst accesses.
+  unsigned minNumLoops = std::min(srcIterationDomainContext.getNumDims(),
+                                  dstIterationDomainContext.getNumDims());
+  unsigned numCommonLoops = 0;
+  for (unsigned i = 0; i < minNumLoops; ++i) {
+    if (!isa<ForStmt>(srcIterationDomainContext.values[i]) ||
+        !isa<ForStmt>(dstIterationDomainContext.values[i]) ||
+        srcIterationDomainContext.values[i] !=
+            dstIterationDomainContext.values[i])
+      break;
+    ++numCommonLoops;
+  }
+  return numCommonLoops;
+}
+
+// Returns true if the operation statement in 'srcAccess' properly dominates
+// the operation statement in 'dstAccess'. Returns false otherwise.
+// Note that 'numCommonLoops' is the number of contiguous surrounding outer
+// loops.
+static bool
+srcHappensBeforeDst(const MemRefAccess &srcAccess,
+                    const MemRefAccess &dstAccess,
+                    const IterationDomainContext &srcIterationDomainContext,
+                    unsigned numCommonLoops) {
+  if (numCommonLoops == 0) {
+    return mlir::properlyDominates(*srcAccess.opStmt, *dstAccess.opStmt);
+  }
+  auto *commonForValue = srcIterationDomainContext.values[numCommonLoops - 1];
+  assert(isa<ForStmt>(commonForValue));
+  auto *commonForStmt = dyn_cast<ForStmt>(commonForValue);
+  // Check the dominance relationship between the respective ancestors of the
+  // src and dst in the StmtBlock of the innermost among the common loops.
+  auto *srcStmt = commonForStmt->findAncestorStmtInBlock(*srcAccess.opStmt);
+  assert(srcStmt != nullptr);
+  auto *dstStmt = commonForStmt->findAncestorStmtInBlock(*dstAccess.opStmt);
+  assert(dstStmt != nullptr);
+  return mlir::properlyDominates(*srcStmt, *dstStmt);
+}
+
+// Adds ordering constraints to 'dependenceDomain' based on number of loops
+// common to 'src/dstIterationDomainContext' and requested 'loopDepth'.
+// Note that 'loopDepth' cannot exceed the number of common loops plus one.
+// EX: Given a loop nest of depth 2 with IVs 'i' and 'j':
+// *) If 'loopDepth == 1' then one constraint is added: i' >= i + 1
+// *) If 'loopDepth == 2' then two constraints are added: i == i' and j' > j + 1
+// *) If 'loopDepth == 3' then two constraints are added: i == i' and j == j'
+static void
+addOrderingConstraints(const IterationDomainContext &srcIterationDomainContext,
+                       const IterationDomainContext &dstIterationDomainContext,
+                       const ValuePositionMap &valuePosMap, unsigned loopDepth,
+                       FlatAffineConstraints *dependenceDomain) {
+  unsigned numCols = dependenceDomain->getNumCols();
+  SmallVector<int64_t, 4> eq(numCols);
+  unsigned numSrcDims = valuePosMap.getNumSrcDims();
+  unsigned numCommonLoops =
+      getNumCommonLoops(srcIterationDomainContext, dstIterationDomainContext);
+  unsigned numCommonLoopConstraints = std::min(numCommonLoops, loopDepth);
+  for (unsigned i = 0; i < numCommonLoopConstraints; ++i) {
+    std::fill(eq.begin(), eq.end(), 0);
+    eq[i] = -1;
+    eq[i + numSrcDims] = 1;
+    if (i == loopDepth - 1) {
+      eq[numCols - 1] = -1;
+      dependenceDomain->addInequality(eq);
+    } else {
+      dependenceDomain->addEquality(eq);
+    }
+  }
+}
 
+// Returns true if 'isEq' constraint in 'dependenceDomain' has a single
+// non-zero coefficient at (rowIdx, idPos). Returns false otherwise.
+// TODO(andydavis) Move this function to FlatAffineConstraints.
+static bool hasSingleNonZeroAt(unsigned idPos, unsigned rowIdx, bool isEq,
+                               FlatAffineConstraints *dependenceDomain) {
+  unsigned numCols = dependenceDomain->getNumCols();
+  for (unsigned j = 0; j < numCols - 1; ++j) {
+    int64_t v = isEq ? dependenceDomain->atEq(rowIdx, j)
+                     : dependenceDomain->atIneq(rowIdx, j);
+    if ((j == idPos && v == 0) || (j != idPos && v != 0))
+      return false;
+  }
   return true;
 }
 
+// Computes distance and direction vectors in 'dependences', by adding
+// variables to 'dependenceDomain' which represent the difference of the IVs,
+// eliminating all other variables, and reading off distance vectors from
+// equality constraints (if possible), and direction vectors from inequalities.
+static void computeDirectionVector(
+    const IterationDomainContext &srcIterationDomainContext,
+    const IterationDomainContext &dstIterationDomainContext, unsigned loopDepth,
+    FlatAffineConstraints *dependenceDomain,
+    llvm::SmallVector<DependenceComponent, 2> *dependenceComponents) {
+  // Find the number of common loops shared by src and dst accesses.
+  unsigned numCommonLoops =
+      getNumCommonLoops(srcIterationDomainContext, dstIterationDomainContext);
+  if (numCommonLoops == 0)
+    return;
+  // Compute direction vectors for requested loop depth.
+  unsigned numIdsToEliminate = dependenceDomain->getNumIds();
+  // Add new variables to 'dependenceDomain' to represent the direction
+  // constraints for each shared loop.
+  for (unsigned j = 0; j < numCommonLoops; ++j) {
+    dependenceDomain->addDimId(j);
+  }
+
+  // Add equality contraints for each common loop, setting newly instroduced
+  // variable at column 'j' to the 'dst' IV minus the 'src IV.
+  SmallVector<int64_t, 4> eq;
+  eq.resize(dependenceDomain->getNumCols());
+  for (unsigned j = 0; j < numCommonLoops; ++j) {
+    std::fill(eq.begin(), eq.end(), 0);
+    eq[j] = 1;
+    eq[j + numCommonLoops] = 1;
+    eq[j + 2 * numCommonLoops] = -1;
+    dependenceDomain->addEquality(eq);
+  }
+
+  // Eliminate all variables other than the direction variables just added.
+  dependenceDomain->projectOut(numCommonLoops, numIdsToEliminate);
+
+  // Scan each common loop variable column and add direction vectors based
+  // on eliminated constraint system.
+  unsigned numCols = dependenceDomain->getNumCols();
+  dependenceComponents->reserve(numCommonLoops);
+  for (unsigned j = 0; j < numCommonLoops; ++j) {
+    DependenceComponent depComp;
+    for (unsigned i = 0, e = dependenceDomain->getNumEqualities(); i < e; ++i) {
+      // Check for equality constraint with single non-zero in column 'j'.
+      if (!hasSingleNonZeroAt(j, i, /*isEq=*/true, dependenceDomain))
+        continue;
+      // Get direction variable coefficient at (i, j).
+      int64_t d = dependenceDomain->atEq(i, j);
+      // Get constant coefficient at (i, numCols - 1).
+      int64_t c = -dependenceDomain->atEq(i, numCols - 1);
+      assert(c % d == 0 && "No dependence should have existed");
+      depComp.lb = depComp.ub = c / d;
+      dependenceComponents->push_back(depComp);
+      break;
+    }
+    // Skip checking inequalities if we set 'depComp' based on equalities.
+    if (depComp.lb.hasValue() || depComp.ub.hasValue())
+      continue;
+    // TODO(andydavis) Call FlatAffineConstraints::getConstantLower/UpperBound
+    // Check inequalities to track direction range for each 'j'.
+    for (unsigned i = 0, e = dependenceDomain->getNumInequalities(); i < e;
+         ++i) {
+      // Check for inequality constraint with single non-zero in column 'j'.
+      if (!hasSingleNonZeroAt(j, i, /*isEq=*/false, dependenceDomain))
+        continue;
+      // Get direction variable coefficient at (i, j).
+      int64_t d = dependenceDomain->atIneq(i, j);
+      // Get constant coefficient at (i, numCols - 1).
+      int64_t c = dependenceDomain->atIneq(i, numCols - 1);
+      if (d < 0) {
+        // Upper bound: add tightest upper bound.
+        auto ub = mlir::floorDiv(c, -d);
+        if (!depComp.ub.hasValue() || ub < depComp.ub.getValue())
+          depComp.ub = ub;
+      } else {
+        // Lower bound: add tightest lower bound.
+        auto lb = mlir::ceilDiv(-c, d);
+        if (!depComp.lb.hasValue() || lb > depComp.lb.getValue())
+          depComp.lb = lb;
+      }
+    }
+    if (depComp.lb.hasValue() || depComp.ub.hasValue()) {
+      if (depComp.lb.hasValue() && depComp.ub.hasValue())
+        assert(depComp.lb.getValue() <= depComp.ub.getValue());
+      dependenceComponents->push_back(depComp);
+    }
+  }
+}
+
 // Populates 'accessMap' with composition of AffineApplyOps reachable from
 // indices of MemRefAccess.
 void MemRefAccess::getAccessMap(AffineValueMap *accessMap) const {
@@ -752,6 +982,9 @@ void MemRefAccess::getAccessMap(AffineValueMap *accessMap) const {
 // between memref accesses 'srcAccess' and 'dstAccess'.
 // Returns 'false' if the accesses can be definitively shown not to access the
 // same element. Returns 'true' otherwise.
+// If a dependence exists, returns in 'dependenceComponents' a direction
+// vector for the dependence, with a component for each loop IV in loops
+// common to both accesses (see Dependence in AffineAnalysis.h for details).
 //
 // The memref access dependence check is comprised of the following steps:
 // *) Compute access functions for each access. Access functions are computed
@@ -830,13 +1063,10 @@ void MemRefAccess::getAccessMap(AffineValueMap *accessMap) const {
 //
 //
 // TODO(andydavis) Support AffineExprs mod/floordiv/ceildiv.
-// TODO(andydavis) Add precedence order constraints for accesses that
-// share a common loop.
-// TODO(andydavis) Add support for returning the direction of the dependence.
-// For example, this function may return that there is a dependence between
-// 'srcAccess' and 'dstAccess' but the dependence may be from dst to src.
-bool mlir::checkMemrefAccessDependence(const MemRefAccess &srcAccess,
-                                       const MemRefAccess &dstAccess) {
+bool mlir::checkMemrefAccessDependence(
+    const MemRefAccess &srcAccess, const MemRefAccess &dstAccess,
+    unsigned loopDepth,
+    llvm::SmallVector<DependenceComponent, 2> *dependenceComponents) {
   // Return 'false' if these accesses do not acces the same memref.
   if (srcAccess.memref != dstAccess.memref)
     return false;
@@ -862,23 +1092,32 @@ bool mlir::checkMemrefAccessDependence(const MemRefAccess &srcAccess,
   if (!getIterationDomainContext(dstAccess.opStmt, &dstIterationDomainContext))
     return false;
 
+  // Return if loopDepth > numCommonLoops and 'srcAccess' does not properly
+  // dominate 'dstAccess' (i.e. no execution path from src to dst access).
+  unsigned numCommonLoops =
+      getNumCommonLoops(srcIterationDomainContext, dstIterationDomainContext);
+  assert(loopDepth <= numCommonLoops + 1);
+  if (loopDepth > numCommonLoops &&
+      !srcHappensBeforeDst(srcAccess, dstAccess, srcIterationDomainContext,
+                           numCommonLoops)) {
+    return false;
+  }
   // Build dim and symbol position maps for each access from access operand
   // MLValue to position in merged contstraint system.
-  DenseMap<const MLValue *, unsigned> srcDimPosMap;
-  DenseMap<const MLValue *, unsigned> dstDimPosMap;
-  DenseMap<const MLValue *, unsigned> symbolPosMap;
-  buildDimAndSymbolPositionMaps(
-      srcIterationDomainContext, dstIterationDomainContext, srcAccessMap,
-      dstAccessMap, &srcDimPosMap, &dstDimPosMap, &symbolPosMap);
+  ValuePositionMap valuePosMap;
+  buildDimAndSymbolPositionMaps(srcIterationDomainContext,
+                                dstIterationDomainContext, srcAccessMap,
+                                dstAccessMap, &valuePosMap);
 
-  // TODO(andydavis) Add documentation.
+  // Calculate number of equalities/inequalities and columns required to
+  // initialize FlatAffineConstraints for 'dependenceDomain'.
   unsigned numIneq = srcIterationDomainContext.domain.getNumInequalities() +
                      dstIterationDomainContext.domain.getNumInequalities();
   AffineMap srcMap = srcAccessMap.getAffineMap();
   assert(srcMap.getNumResults() == dstAccessMap.getAffineMap().getNumResults());
   unsigned numEq = srcMap.getNumResults();
-  unsigned numDims = srcDimPosMap.size() + dstDimPosMap.size();
-  unsigned numSymbols = symbolPosMap.size();
+  unsigned numDims = valuePosMap.getNumDims();
+  unsigned numSymbols = valuePosMap.getNumSymbols();
   unsigned numIds = numDims + numSymbols;
   unsigned numCols = numIds + 1;
 
@@ -888,18 +1127,23 @@ bool mlir::checkMemrefAccessDependence(const MemRefAccess &srcAccess,
   // Create memref access constraint by equating src/dst access functions.
   // Note that this check is conservative, and will failure in the future
   // when local variables for mod/div exprs are supported.
-  if (!addMemRefAccessConstraints(srcAccessMap, dstAccessMap, srcDimPosMap,
-                                  dstDimPosMap, symbolPosMap,
+  if (!addMemRefAccessConstraints(srcAccessMap, dstAccessMap, valuePosMap,
                                   &dependenceDomain))
     return true;
 
-  // Add domain constraints for src access function.
-  addDomainConstraints(srcIterationDomainContext, srcDimPosMap, symbolPosMap,
-                       &dependenceDomain);
-  // Add equality constraints from 'dstConstraints'.
-  addDomainConstraints(dstIterationDomainContext, dstDimPosMap, symbolPosMap,
-                       &dependenceDomain);
-  bool isEmpty = dependenceDomain.isEmpty();
-  // Return false if the solution space is empty.
-  return !isEmpty;
+  // Add 'src' happens before 'dst' ordering constraints.
+  addOrderingConstraints(srcIterationDomainContext, dstIterationDomainContext,
+                         valuePosMap, loopDepth, &dependenceDomain);
+  // Add src and dst domain constraints.
+  addDomainConstraints(srcIterationDomainContext, dstIterationDomainContext,
+                       valuePosMap, &dependenceDomain);
+
+  // Return false if the solution space is empty: no dependence.
+  if (dependenceDomain.isEmpty()) {
+    return false;
+  }
+  // Compute dependence direction vector and return true.
+  computeDirectionVector(srcIterationDomainContext, dstIterationDomainContext,
+                         loopDepth, &dependenceDomain, dependenceComponents);
+  return true;
 }
index 28a80762b949916b5c1b22fd1051bdc4e78ed1a3..fc63a41c8488949da288907fb93ac0d828101e90 100644 (file)
@@ -90,40 +90,87 @@ static void getMemRefAccess(const OperationStmt *loadOrStoreOpStmt,
   }
 }
 
+// Populates 'loops' with the loop nest surrounding 'stmt', ordered from
+// outer-most ForStmt to inner-most.
+static void getLoopNest(Statement *stmt,
+                        SmallVector<const ForStmt *, 4> *loops) {
+  const auto *currStmt = stmt->getParentStmt();
+  while (currStmt != nullptr && isa<ForStmt>(currStmt)) {
+    loops->push_back(dyn_cast<ForStmt>(currStmt));
+    currStmt = currStmt->getParentStmt();
+  }
+  std::reverse(loops->begin(), loops->end());
+}
+
+// Returns the number of surrounding loops common to 'loopsA' and 'loopsB',
+// where each lists loops from outer-most to inner-most in loop nest.
+static unsigned getNumCommonSurroundingLoops(ArrayRef<const ForStmt *> loopsA,
+                                             ArrayRef<const ForStmt *> loopsB) {
+  unsigned minNumLoops = std::min(loopsA.size(), loopsB.size());
+  unsigned numCommonLoops = 0;
+  for (unsigned i = 0; i < minNumLoops; ++i) {
+    if (loopsA[i] != loopsB[i])
+      break;
+    ++numCommonLoops;
+  }
+  return numCommonLoops;
+}
+
+// Returns a result string which represents the direction vector (if there was
+// a dependence), returns the string "false" otherwise.
+static string
+getDirectionVectorStr(bool ret, unsigned numCommonLoops, unsigned loopNestDepth,
+                      ArrayRef<DependenceComponent> dependenceComponents) {
+  if (!ret)
+    return "false";
+  if (dependenceComponents.empty() || loopNestDepth > numCommonLoops)
+    return "true";
+  string result;
+  for (unsigned i = 0, e = dependenceComponents.size(); i < e; ++i) {
+    string lbStr = dependenceComponents[i].lb.hasValue()
+                       ? std::to_string(dependenceComponents[i].lb.getValue())
+                       : "-inf";
+    string ubStr = dependenceComponents[i].ub.hasValue()
+                       ? std::to_string(dependenceComponents[i].ub.getValue())
+                       : "+inf";
+    result += "[" + lbStr + ", " + ubStr + "]";
+  }
+  return result;
+}
+
 // For each access in 'loadsAndStores', runs a depence check between this
 // "source" access and all subsequent "destination" accesses in
 // 'loadsAndStores'. Emits the result of the dependence check as a note with
 // the source access.
-// TODO(andydavis) Clarify expected-note logs. In particular we may want to
-// drop the 'i' from the note string, tag dependence destination accesses
-// with a note with their 'j' index. In addition, we may want a schedme that
-// first assigned unique ids to each access, then emits a note for each access
-// with its id, and emits a note for each dependence check with a pair of ids.
-// For example, given this code:
-//
-//   memref_access0
-//   // emit note: "this op is memref access 0'
-//   // emit note: "dependence from memref access 0 to access 1 = false"
-//   // emit note: "dependence from memref access 0 to access 2 = true"
-//   memref_access1
-//   // emit note: "this op is memref access 1'
-//   // emit note: "dependence from memref access 1 to access 2 = false"
-//   memref_access2
-//   // emit note: "this op is memref access 2'
-//
 static void checkDependences(ArrayRef<OperationStmt *> loadsAndStores) {
   for (unsigned i = 0, e = loadsAndStores.size(); i < e; ++i) {
     auto *srcOpStmt = loadsAndStores[i];
     MemRefAccess srcAccess;
     getMemRefAccess(srcOpStmt, &srcAccess);
-    for (unsigned j = i + 1; j < e; ++j) {
+    SmallVector<const ForStmt *, 4> srcLoops;
+    getLoopNest(srcOpStmt, &srcLoops);
+    for (unsigned j = 0; j < e; ++j) {
       auto *dstOpStmt = loadsAndStores[j];
       MemRefAccess dstAccess;
       getMemRefAccess(dstOpStmt, &dstAccess);
-      bool ret = checkMemrefAccessDependence(srcAccess, dstAccess);
-      srcOpStmt->emitNote("dependence from memref access " + Twine(i) +
-                          " to access " + Twine(j) + " = " +
-                          (ret ? "true" : "false"));
+
+      SmallVector<const ForStmt *, 4> dstLoops;
+      getLoopNest(dstOpStmt, &dstLoops);
+      unsigned numCommonLoops =
+          getNumCommonSurroundingLoops(srcLoops, dstLoops);
+      for (unsigned d = 1; d <= numCommonLoops + 1; ++d) {
+        llvm::SmallVector<DependenceComponent, 2> dependenceComponents;
+        bool ret = checkMemrefAccessDependence(srcAccess, dstAccess, d,
+                                               &dependenceComponents);
+        // TODO(andydavis) 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).
+        srcOpStmt->emitNote(
+            "dependence from " + Twine(i) + " to " + Twine(j) + " at depth " +
+            Twine(d) + " = " +
+            getDirectionVectorStr(ret, numCommonLoops, d, dependenceComponents)
+                .c_str());
+      }
     }
   }
 }
index 87657aeb3594a950b4ebb4985c749391f13ee6f6..3db290fac038bead42581438a64989e8058d7f1c 100644 (file)
@@ -233,11 +233,15 @@ PassResult LoopFusion::runOnMLFunction(MLFunction *f) {
 
   // TODO(andydavis) Add checks for fusion-preventing dependences and ordering
   // constraints which would prevent fusion.
-  // TODO(andydavis) This check if overly conservative for now. Support fusing
+  // TODO(andydavis) This check is overly conservative for now. Support fusing
   // statements with compatible dependences (i.e. statements where the
   // dependence between the statements does not reverse direction when the
   // statements are fused into the same loop).
-  if (!checkMemrefAccessDependence(candidate.accessA, candidate.accessB)) {
+  llvm::SmallVector<DependenceComponent, 2> dependenceComponents;
+  // TODO(andydavis) Check dependences at differnt loop nest depths.
+  if (!checkMemrefAccessDependence(candidate.accessA, candidate.accessB,
+                                   /*loopNestDepth=*/0,
+                                   &dependenceComponents)) {
     // Current conservatinve test policy: No dependence exists between accesses
     // in different loop nests -> fuse loops.
     fuseLoops(candidate);
index ca6d9fb4143f8aa96810b0a98b7d491caac307a3..26f2738c9940ad629ea5a6c55ec0d7ecfbb8780b 100644 (file)
@@ -1,7 +1,5 @@
 // RUN: mlir-opt %s -memref-dependence-check  -split-input-file -verify | FileCheck %s
 
-// TODO(andydavis) Add test cases for self-edges and a dependence cycle.
-
 // -----
 // CHECK-LABEL: mlfunc @different_memrefs() {
 mlfunc @different_memrefs() {
@@ -10,8 +8,11 @@ mlfunc @different_memrefs() {
   %c0 = constant 0 : index
   %c1 = constant 1.0 : f32
   store %c1, %m.a[%c0] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = false}}
+  // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 0 to 1 at depth 1 = false}}
   %v0 = load %m.b[%c0] : memref<100xf32>
+  // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 1 to 1 at depth 1 = false}}
   return
 }
 
@@ -23,8 +24,11 @@ mlfunc @store_load_different_elements() {
   %c1 = constant 1 : index
   %c7 = constant 7.0 : f32
   store %c7, %m[%c0] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = false}}
+  // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 0 to 1 at depth 1 = false}}
   %v0 = load %m[%c1] : memref<100xf32>
+  // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 1 to 1 at depth 1 = false}}
   return
 }
 
@@ -36,8 +40,11 @@ mlfunc @load_store_different_elements() {
   %c1 = constant 1 : index
   %c7 = constant 7.0 : f32
   %v0 = load %m[%c1] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = false}}
+  // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 0 to 1 at depth 1 = false}}
   store %c7, %m[%c0] : memref<100xf32>
+  // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 1 to 1 at depth 1 = false}}
   return
 }
 
@@ -48,20 +55,11 @@ mlfunc @store_load_same_element() {
   %c11 = constant 11 : index
   %c7 = constant 7.0 : f32
   store %c7, %m[%c11] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
+  // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 0 to 1 at depth 1 = true}}
   %v0 = load %m[%c11] : memref<100xf32>
-  return
-}
-
-// -----
-// CHECK-LABEL: mlfunc @load_store_same_element() {
-mlfunc @load_store_same_element() {
-  %m = alloc() : memref<100xf32>
-  %c11 = constant 11 : index
-  %c7 = constant 7.0 : f32
-  %v0 = load %m[%c11] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
-  store %c7, %m[%c11] : memref<100xf32>
+  // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 1 to 1 at depth 1 = false}}
   return
 }
 
@@ -72,8 +70,11 @@ mlfunc @load_load_same_element() {
   %c11 = constant 11 : index
   %c7 = constant 7.0 : f32
   %v0 = load %m[%c11] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = false}}
+  // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 0 to 1 at depth 1 = false}}
   %v1 = load %m[%c11] : memref<100xf32>
+  // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 1 to 1 at depth 1 = false}}
   return
 }
 
@@ -83,8 +84,11 @@ mlfunc @store_load_same_symbol(%arg0 : index) {
   %m = alloc() : memref<100xf32>
   %c7 = constant 7.0 : f32
   store %c7, %m[%arg0] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
+  // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 0 to 1 at depth 1 = true}}
   %v0 = load %m[%arg0] : memref<100xf32>
+  // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 1 to 1 at depth 1 = false}}
   return
 }
 
@@ -94,8 +98,11 @@ mlfunc @store_load_different_symbols(%arg0 : index, %arg1 : index) {
   %m = alloc() : memref<100xf32>
   %c7 = constant 7.0 : f32
   store %c7, %m[%arg0] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
+  // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 0 to 1 at depth 1 = true}}
   %v0 = load %m[%arg1] : memref<100xf32>
+  // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 1 to 1 at depth 1 = false}}
   return
 }
 
@@ -107,9 +114,12 @@ mlfunc @store_load_diff_element_affine_apply_const() {
   %c7 = constant 7.0 : f32
   %a0 = affine_apply (d0) -> (d0) (%c1)
   store %c7, %m[%a0] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = false}}
+  // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 0 to 1 at depth 1 = false}}
   %a1 = affine_apply (d0) -> (d0 + 1) (%c1)
   %v0 = load %m[%a1] : memref<100xf32>
+  // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 1 to 1 at depth 1 = false}}
   return
 }
 
@@ -122,9 +132,12 @@ mlfunc @store_load_same_element_affine_apply_const() {
   %c11 = constant 11 : index  
   %a0 = affine_apply (d0) -> (d0 + 1) (%c9)
   store %c7, %m[%a0] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
+  // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 0 to 1 at depth 1 = true}} 
   %a1 = affine_apply (d0) -> (d0 - 1) (%c11)
   %v0 = load %m[%a1] : memref<100xf32>
+  // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 1 to 1 at depth 1 = false}}
   return
 }
 
@@ -135,23 +148,28 @@ mlfunc @store_load_affine_apply_symbol(%arg0 : index) {
   %c7 = constant 7.0 : f32
   %a0 = affine_apply (d0) -> (d0) (%arg0)
   store %c7, %m[%a0] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
+  // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 0 to 1 at depth 1 = true}}
   %a1 = affine_apply (d0) -> (d0) (%arg0)
   %v0 = load %m[%a1] : memref<100xf32>
+  // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 1 to 1 at depth 1 = false}}
   return
 }
 
 // -----
-// Note: has single equality x - y - 1 = 0, which has solns for (1, 0) (0, -1)
 // CHECK-LABEL: mlfunc @store_load_affine_apply_symbol_offset(%arg0 : index) {
 mlfunc @store_load_affine_apply_symbol_offset(%arg0 : index) {
   %m = alloc() : memref<100xf32>
   %c7 = constant 7.0 : f32
   %a0 = affine_apply (d0) -> (d0) (%arg0)
   store %c7, %m[%a0] : memref<100xf32>
-  // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
+  // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 0 to 1 at depth 1 = false}}
   %a1 = affine_apply (d0) -> (d0 + 1) (%arg0)
   %v0 = load %m[%a1] : memref<100xf32>
+  // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+  // expected-note@-2 {{dependence from 1 to 1 at depth 1 = false}}
   return
 }
 
@@ -164,9 +182,39 @@ mlfunc @store_range_load_after_range() {
   for %i0 = 0 to 10 {
     %a0 = affine_apply (d0) -> (d0) (%i0)
     store %c7, %m[%a0] : memref<100xf32>
-    // expected-note@-1 {{dependence from memref access 0 to access 1 = false}}
+    // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 0 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 0 to 1 at depth 2 = false}}
     %a1 = affine_apply (d0) -> (d0) (%c10)
     %v0 = load %m[%a1] : memref<100xf32>
+    // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 1 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 1 to 1 at depth 2 = false}}
+  }
+  return
+}
+
+// -----
+// CHECK-LABEL: mlfunc @store_load_func_symbol(%arg0 : index) {
+mlfunc @store_load_func_symbol(%arg0 : index) {
+  %m = alloc() : memref<100xf32>
+  %c7 = constant 7.0 : f32
+  %c10 = constant 10 : index
+  for %i0 = 0 to 10 {
+    %a0 = affine_apply (d0) -> (d0) (%arg0)
+    store %c7, %m[%a0] : memref<100xf32>
+    // expected-note@-1 {{dependence from 0 to 0 at depth 1 = [1, 9]}}
+    // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 0 to 1 at depth 1 = [1, 9]}}
+    // expected-note@-4 {{dependence from 0 to 1 at depth 2 = true}}
+    %a1 = affine_apply (d0) -> (d0) (%arg0)
+    %v0 = load %m[%a1] : memref<100xf32>
+    // expected-note@-1 {{dependence from 1 to 0 at depth 1 = [1, 9]}}
+    // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 1 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 1 to 1 at depth 2 = false}}
   }
   return
 }
@@ -179,10 +227,22 @@ mlfunc @store_range_load_last_in_range() {
   %c10 = constant 10 : index
   for %i0 = 0 to 10 {
     %a0 = affine_apply (d0) -> (d0) (%i0)
+    // For dependence from 0 to 1, we do not have a loop carried dependence
+    // because only the final write in the loop accesses the same element as the
+    // load, so this dependence appears only at depth 2 (loop independent).
     store %c7, %m[%a0] : memref<100xf32>
-    // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
+    // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 0 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 0 to 1 at depth 2 = true}}
     %a1 = affine_apply (d0) -> (d0 - 1) (%c10)
+    // For dependence from 1 to 0, we have write-after-read (WAR) dependences
+    // for all loads in the loop to the store on the last iteration.
     %v0 = load %m[%a1] : memref<100xf32>
+    // expected-note@-1 {{dependence from 1 to 0 at depth 1 = [1, 9]}}
+    // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 1 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 1 to 1 at depth 2 = false}}
   }
   return
 }
@@ -196,9 +256,16 @@ mlfunc @store_range_load_before_range() {
   for %i0 = 1 to 11 {
     %a0 = affine_apply (d0) -> (d0) (%i0)
     store %c7, %m[%a0] : memref<100xf32>
-    // expected-note@-1 {{dependence from memref access 0 to access 1 = false}}
+    // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 0 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 0 to 1 at depth 2 = false}}
     %a1 = affine_apply (d0) -> (d0) (%c0)
     %v0 = load %m[%a1] : memref<100xf32>
+    // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 1 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 1 to 1 at depth 2 = false}}
   }
   return
 }
@@ -211,127 +278,289 @@ mlfunc @store_range_load_first_in_range() {
   %c0 = constant 0 : index
   for %i0 = 1 to 11 {
     %a0 = affine_apply (d0) -> (d0) (%i0)
+    // Dependence from 0 to 1 at depth 1 is a range because all loads at
+    // constant index zero are reads after first store at index zero during
+    // first iteration of the loop.
     store %c7, %m[%a0] : memref<100xf32>
-    // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
+    // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 0 to 1 at depth 1 = [1, 9]}}
+    // expected-note@-4 {{dependence from 0 to 1 at depth 2 = true}}
     %a1 = affine_apply (d0) -> (d0 + 1) (%c0)
     %v0 = load %m[%a1] : memref<100xf32>
+    // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 1 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 1 to 1 at depth 2 = false}}
   }
   return
 }
 
 // -----
-// CHECK-LABEL: mlfunc @store_load_diff_ranges_diff_1d_loop_nests() {
-mlfunc @store_load_diff_ranges_diff_1d_loop_nests() {
+// CHECK-LABEL: mlfunc @store_plus_3() {
+mlfunc @store_plus_3() {
   %m = alloc() : memref<100xf32>
   %c7 = constant 7.0 : f32
-  for %i0 = 0 to 5 {
-    %a0 = affine_apply (d0) -> (d0) (%i0)
+  for %i0 = 1 to 11 {
+    %a0 = affine_apply (d0) -> (d0 + 3) (%i0)
     store %c7, %m[%a0] : memref<100xf32>
-    // expected-note@-1 {{dependence from memref access 0 to access 1 = false}}
-  }
-  for %i1 = 5 to 11 {
-    %a1 = affine_apply (d0) -> (d0) (%i1)
+    // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 0 to 1 at depth 1 = [3, 3]}}
+    // expected-note@-4 {{dependence from 0 to 1 at depth 2 = false}}
+    %a1 = affine_apply (d0) -> (d0) (%i0)
     %v0 = load %m[%a1] : memref<100xf32>
+    // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 1 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 1 to 1 at depth 2 = false}}
   }
   return
 }
 
 // -----
-// CHECK-LABEL: mlfunc @store_load_overlapping_ranges_diff_1d_loop_nests() {
-mlfunc @store_load_overlapping_ranges_diff_1d_loop_nests() {
+// CHECK-LABEL: mlfunc @load_minus_2() {
+mlfunc @load_minus_2() {
   %m = alloc() : memref<100xf32>
   %c7 = constant 7.0 : f32
-  for %i0 = 0 to 5 {
+  for %i0 = 2 to 11 {
     %a0 = affine_apply (d0) -> (d0) (%i0)
     store %c7, %m[%a0] : memref<100xf32>
-    // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
-  }
-  for %i1 = 5 to 11 {
-    %a1 = affine_apply (d0) -> (d0 - 1) (%i1)
+    // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 0 to 1 at depth 1 = [2, 2]}}
+    // expected-note@-4 {{dependence from 0 to 1 at depth 2 = false}}
+    %a1 = affine_apply (d0) -> (d0 - 2) (%i0)
     %v0 = load %m[%a1] : memref<100xf32>
+    // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 1 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 1 to 1 at depth 2 = false}}
   }
   return
 }
 
 // -----
-// CHECK-LABEL: mlfunc @store_load_diff_inner_ranges_diff_2d_loop_nests() {
-mlfunc @store_load_diff_inner_ranges_diff_2d_loop_nests() {
+// CHECK-LABEL: mlfunc @perfectly_nested_loops_loop_independent() {
+mlfunc @perfectly_nested_loops_loop_independent() {
   %m = alloc() : memref<10x10xf32>
   %c7 = constant 7.0 : f32
-  for %i0 = 0 to 5 {
-    for %i1 = 0 to 5 {
+  for %i0 = 0 to 11 {
+    for %i1 = 0 to 11 {
+      // Dependence from access 0 to 1 is loop independent at depth = 3.
       %a0 = affine_apply (d0, d1) -> (d0, d1) (%i0, %i1)
-       store %c7, %m[%a0#0, %a0#1] : memref<10x10xf32>
-      // expected-note@-1 {{dependence from memref access 0 to access 1 = false}}
-    }
-  }
-  for %i2 = 0 to 5 {
-    for %i3 = 5 to 7 {
-      %a1 = affine_apply (d0, d1) -> (d0, d1) (%i2, %i3)
+      store %c7, %m[%a0#0, %a0#1] : memref<10x10xf32>
+      // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+      // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 0 to 0 at depth 3 = false}}
+      // expected-note@-4 {{dependence from 0 to 1 at depth 1 = false}}
+      // expected-note@-5 {{dependence from 0 to 1 at depth 2 = false}}
+      // expected-note@-6 {{dependence from 0 to 1 at depth 3 = true}}
+      %a1 = affine_apply (d0, d1) -> (d0, d1) (%i0, %i1)
       %v0 = load %m[%a1#0, %a1#1] : memref<10x10xf32>
+      // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+      // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 1 to 0 at depth 3 = false}}
+      // expected-note@-4 {{dependence from 1 to 1 at depth 1 = false}}
+      // expected-note@-5 {{dependence from 1 to 1 at depth 2 = false}}
+      // expected-note@-6 {{dependence from 1 to 1 at depth 3 = false}}
     }
   }
   return
 }
 
 // -----
-// CHECK-LABEL: mlfunc @store_load_overlapping_inner_ranges_diff_2d_loop_nests() {
-mlfunc @store_load_overlapping_inner_ranges_diff_2d_loop_nests() {
+// CHECK-LABEL: mlfunc @perfectly_nested_loops_loop_carried_at_depth1() {
+mlfunc @perfectly_nested_loops_loop_carried_at_depth1() {
   %m = alloc() : memref<10x10xf32>
   %c7 = constant 7.0 : f32
-  for %i0 = 0 to 5 {
-    for %i1 = 0 to 5 {
-      %a0 = affine_apply (d0, d1) -> (d0, d1 + 1) (%i0, %i1)
-       store %c7, %m[%a0#0, %a0#1] : memref<10x10xf32>
-      // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
+  for %i0 = 0 to 9 {
+    for %i1 = 0 to 9 {
+      // Dependence from access 0 to 1 is loop carried at depth 1.
+      %a0 = affine_apply (d0, d1) -> (d0, d1) (%i0, %i1)
+      store %c7, %m[%a0#0, %a0#1] : memref<10x10xf32>
+      // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+      // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 0 to 0 at depth 3 = false}}
+      // expected-note@-4 {{dependence from 0 to 1 at depth 1 = [2, 2][0, 0]}}
+      // expected-note@-5 {{dependence from 0 to 1 at depth 2 = false}}
+      // expected-note@-6 {{dependence from 0 to 1 at depth 3 = false}}
+      %a1 = affine_apply (d0, d1) -> (d0 - 2, d1) (%i0, %i1)
+      %v0 = load %m[%a1#0, %a1#1] : memref<10x10xf32>
+      // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+      // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 1 to 0 at depth 3 = false}}
+      // expected-note@-4 {{dependence from 1 to 1 at depth 1 = false}}
+      // expected-note@-5 {{dependence from 1 to 1 at depth 2 = false}}
+      // expected-note@-6 {{dependence from 1 to 1 at depth 3 = false}}
     }
   }
-  for %i2 = 0 to 5 {
-    for %i3 = 5 to 7 {
-      %a1 = affine_apply (d0, d1) -> (d0, d1) (%i2, %i3)
+  return
+}
+
+// -----
+// CHECK-LABEL: mlfunc @perfectly_nested_loops_loop_carried_at_depth2() {
+mlfunc @perfectly_nested_loops_loop_carried_at_depth2() {
+  %m = alloc() : memref<10x10xf32>
+  %c7 = constant 7.0 : f32
+  for %i0 = 0 to 10 {
+    for %i1 = 0 to 10 {
+      // Dependence from access 0 to 1 is loop carried at depth 2.
+      %a0 = affine_apply (d0, d1) -> (d0, d1) (%i0, %i1)
+      store %c7, %m[%a0#0, %a0#1] : memref<10x10xf32>
+      // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+      // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 0 to 0 at depth 3 = false}}
+      // expected-note@-4 {{dependence from 0 to 1 at depth 1 = false}}
+      // expected-note@-5 {{dependence from 0 to 1 at depth 2 = [0, 0][3, 3]}}
+      // expected-note@-6 {{dependence from 0 to 1 at depth 3 = false}}
+      %a1 = affine_apply (d0, d1) -> (d0, d1 - 3) (%i0, %i1)
       %v0 = load %m[%a1#0, %a1#1] : memref<10x10xf32>
+      // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+      // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 1 to 0 at depth 3 = false}}
+      // expected-note@-4 {{dependence from 1 to 1 at depth 1 = false}}
+      // expected-note@-5 {{dependence from 1 to 1 at depth 2 = false}}
+      // expected-note@-6 {{dependence from 1 to 1 at depth 3 = false}}
     }
   }
   return
 }
 
 // -----
-// CHECK-LABEL: mlfunc @store_load_diff_outer_ranges_diff_2d_loop_nests() {
-mlfunc @store_load_diff_outer_ranges_diff_2d_loop_nests() {
+// CHECK-LABEL: mlfunc @one_common_loop() {
+mlfunc @one_common_loop() {
   %m = alloc() : memref<10x10xf32>
   %c7 = constant 7.0 : f32
-  for %i0 = 0 to 5 {
-    for %i1 = 0 to 5 {
+  // There is a loop-independent dependence from access 0 to 1 at depth 2.
+  for %i0 = 0 to 10 {
+    for %i1 = 0 to 10 {
       %a0 = affine_apply (d0, d1) -> (d0, d1) (%i0, %i1)
-       store %c7, %m[%a0#0, %a0#1] : memref<10x10xf32>
-      // expected-note@-1 {{dependence from memref access 0 to access 1 = false}}
+      store %c7, %m[%a0#0, %a0#1] : memref<10x10xf32>
+      // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+      // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 0 to 0 at depth 3 = false}}
+      // expected-note@-4 {{dependence from 0 to 1 at depth 1 = false}}
+      // expected-note@-5 {{dependence from 0 to 1 at depth 2 = true}}
     }
-  }
-  for %i2 = 5 to 8 {
-    for %i3 = 0 to 5 {
-      %a1 = affine_apply (d0, d1) -> (d0, d1) (%i2, %i3)
+    for %i2 = 0 to 9 {
+      %a1 = affine_apply (d0, d1) -> (d0, d1) (%i0, %i2)
       %v0 = load %m[%a1#0, %a1#1] : memref<10x10xf32>
+      // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+      // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 1 to 1 at depth 1 = false}}
+      // expected-note@-4 {{dependence from 1 to 1 at depth 2 = false}}
+      // expected-note@-5 {{dependence from 1 to 1 at depth 3 = false}}
     }
   }
   return
 }
 
 // -----
-// CHECK-LABEL: mlfunc @store_load_overlapping_outer_ranges_diff_2d_loop_nests() {
-mlfunc @store_load_overlapping_outer_ranges_diff_2d_loop_nests() {
+// CHECK-LABEL: mlfunc @dependence_cycle() {
+mlfunc @dependence_cycle() {
+  %m.a = alloc() : memref<100xf32>
+  %m.b = alloc() : memref<100xf32>
+
+  // Dependences:
+  // *) loop-independent dependence from access 1 to 2 at depth 2.
+  // *) loop-carried dependence from access 3 to 0 at depth 1.
+  for %i0 = 0 to 9 {
+    %a0 = affine_apply (d0) -> (d0) (%i0)
+    %v0 = load %m.a[%a0] : memref<100xf32>
+    // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 0 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 0 to 1 at depth 2 = false}}
+    // expected-note@-5 {{dependence from 0 to 2 at depth 1 = false}}
+    // expected-note@-6 {{dependence from 0 to 2 at depth 2 = false}}
+    // expected-note@-7 {{dependence from 0 to 3 at depth 1 = false}}
+    // expected-note@-8 {{dependence from 0 to 3 at depth 2 = false}}
+    %a1 = affine_apply (d0) -> (d0) (%i0)
+    store %v0, %m.b[%a1] : memref<100xf32>
+    // expected-note@-1 {{dependence from 1 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 1 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 1 to 1 at depth 2 = false}}
+    // expected-note@-5 {{dependence from 1 to 2 at depth 1 = false}}
+    // expected-note@-6 {{dependence from 1 to 2 at depth 2 = true}}
+    // expected-note@-7 {{dependence from 1 to 3 at depth 1 = false}}
+    // expected-note@-8 {{dependence from 1 to 3 at depth 2 = false}}
+    %a2 = affine_apply (d0) -> (d0) (%i0)
+    %v1 = load %m.b[%a2] : memref<100xf32>
+    // expected-note@-1 {{dependence from 2 to 0 at depth 1 = false}}
+    // expected-note@-2 {{dependence from 2 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 2 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 2 to 1 at depth 2 = false}}
+    // expected-note@-5 {{dependence from 2 to 2 at depth 1 = false}}
+    // expected-note@-6 {{dependence from 2 to 2 at depth 2 = false}}
+    // expected-note@-7 {{dependence from 2 to 3 at depth 1 = false}}
+    // expected-note@-8 {{dependence from 2 to 3 at depth 2 = false}}
+    %a3 = affine_apply (d0) -> (d0 + 1) (%i0)
+    store %v1, %m.a[%a3] : memref<100xf32>
+    // expected-note@-1 {{dependence from 3 to 0 at depth 1 = [1, 1]}}
+    // expected-note@-2 {{dependence from 3 to 0 at depth 2 = false}}
+    // expected-note@-3 {{dependence from 3 to 1 at depth 1 = false}}
+    // expected-note@-4 {{dependence from 3 to 1 at depth 2 = false}}
+    // expected-note@-5 {{dependence from 3 to 2 at depth 1 = false}}
+    // expected-note@-6 {{dependence from 3 to 2 at depth 2 = false}}
+    // expected-note@-7 {{dependence from 3 to 3 at depth 1 = false}}
+    // expected-note@-8 {{dependence from 3 to 3 at depth 2 = false}}
+  }
+  return
+}
+
+// -----
+// CHECK-LABEL: mlfunc @negative_and_positive_direction_vectors() {
+mlfunc @negative_and_positive_direction_vectors() {
   %m = alloc() : memref<10x10xf32>
   %c7 = constant 7.0 : f32
-  for %i0 = 0 to 5 {
-    for %i1 = 0 to 5 {
-      %a0 = affine_apply (d0, d1) -> (d0 + 1, d1) (%i0, %i1)
-       store %c7, %m[%a0#0, %a0#1] : memref<10x10xf32>
-      // expected-note@-1 {{dependence from memref access 0 to access 1 = true}}
+  for %i0 = 0 to 10 {
+    for %i1 = 0 to 10 {
+      %a0 = affine_apply (d0, d1) -> (d0 - 1, d1 + 1) (%i0, %i1)
+      %v0 = load %m[%a0#0, %a0#1] : memref<10x10xf32>
+      // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+      // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 0 to 0 at depth 3 = false}}
+      // expected-note@-4 {{dependence from 0 to 1 at depth 1 = false}}
+      // expected-note@-5 {{dependence from 0 to 1 at depth 2 = false}}
+      // expected-note@-6 {{dependence from 0 to 1 at depth 3 = false}}
+      %a1 = affine_apply (d0, d1) -> (d0, d1) (%i0, %i1)
+      store %c7, %m[%a1#0, %a1#1] : memref<10x10xf32>
+      // expected-note@-1 {{dependence from 1 to 0 at depth 1 = [1, 1][-1, -1]}}
+      // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 1 to 0 at depth 3 = false}}
+      // expected-note@-4 {{dependence from 1 to 1 at depth 1 = false}}
+      // expected-note@-5 {{dependence from 1 to 1 at depth 2 = false}}
+      // expected-note@-6 {{dependence from 1 to 1 at depth 3 = false}}
     }
   }
-  for %i2 = 5 to 8 {
-    for %i3 = 0 to 5 {
-      %a1 = affine_apply (d0, d1) -> (d0, d1) (%i2, %i3)
-      %v0 = load %m[%a1#0, %a1#1] : memref<10x10xf32>
+  return
+}
+
+// -----
+// CHECK-LABEL: mlfunc @war_raw_waw_deps() {
+mlfunc @war_raw_waw_deps() {
+  %m = alloc() : memref<100xf32>
+  %c7 = constant 7.0 : f32
+  for %i0 = 0 to 10 {
+    for %i1 = 0 to 10 {
+      %a0 = affine_apply (d0) -> (d0 + 1) (%i1)
+      %v0 = load %m[%a0] : memref<100xf32>
+      // expected-note@-1 {{dependence from 0 to 0 at depth 1 = false}}
+      // expected-note@-2 {{dependence from 0 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 0 to 0 at depth 3 = false}}
+      // expected-note@-4 {{dependence from 0 to 1 at depth 1 = [1, 9][1, 1]}}
+      // expected-note@-5 {{dependence from 0 to 1 at depth 2 = [0, 0][1, 1]}}
+      // expected-note@-6 {{dependence from 0 to 1 at depth 3 = false}}
+      %a1 = affine_apply (d0) -> (d0) (%i1)
+      store %c7, %m[%a1] : memref<100xf32>
+      // expected-note@-1 {{dependence from 1 to 0 at depth 1 = [1, 9][-1, -1]}}
+      // expected-note@-2 {{dependence from 1 to 0 at depth 2 = false}}
+      // expected-note@-3 {{dependence from 1 to 0 at depth 3 = false}}
+      // expected-note@-4 {{dependence from 1 to 1 at depth 1 = [1, 9][0, 0]}}
+      // expected-note@-5 {{dependence from 1 to 1 at depth 2 = false}}
+      // expected-note@-6 {{dependence from 1 to 1 at depth 3 = false}}
     }
   }
   return