CIRCT  19.0.0git
DependenceAnalysis.cpp
Go to the documentation of this file.
1 //===- DependenceAnalysis.cpp - memory dependence analyses ----------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements methods that perform analysis involving memory access
10 // dependences.
11 //
12 //===----------------------------------------------------------------------===//
13 
15 #include "mlir/Dialect/Affine/Analysis/AffineAnalysis.h"
16 #include "mlir/Dialect/Affine/Analysis/AffineStructures.h"
17 #include "mlir/Dialect/Affine/Analysis/Utils.h"
18 #include "mlir/Dialect/Affine/IR/AffineMemoryOpInterfaces.h"
19 #include "mlir/Dialect/Affine/IR/AffineOps.h"
20 #include "mlir/Dialect/Affine/LoopUtils.h"
21 #include "mlir/Dialect/Func/IR/FuncOps.h"
22 #include "mlir/IR/BuiltinOps.h"
23 
24 using namespace mlir;
25 using namespace mlir::affine;
26 using namespace circt::analysis;
27 
28 /// Helper to iterate through memory operation pairs and check for dependences
29 /// at a given loop nesting depth.
30 static void checkMemrefDependence(SmallVectorImpl<Operation *> &memoryOps,
31  unsigned depth,
32  MemoryDependenceResult &results) {
33  for (auto *source : memoryOps) {
34  for (auto *destination : memoryOps) {
35  if (source == destination)
36  continue;
37 
38  // Initialize the dependence list for this destination.
39  if (results.count(destination) == 0)
40  results[destination] = SmallVector<MemoryDependence>();
41 
42  // Look for inter-iteration dependences on the same memory location.
43  MemRefAccess src(source);
44  MemRefAccess dst(destination);
45  FlatAffineValueConstraints dependenceConstraints;
46  SmallVector<DependenceComponent, 2> depComps;
47 
48  // Requested depth might not be a valid comparison if they do not belong
49  // to the same loop nest
50  if (depth > getInnermostCommonLoopDepth({source, destination}))
51  continue;
52 
53  DependenceResult result = checkMemrefAccessDependence(
54  src, dst, depth, &dependenceConstraints, &depComps, true);
55 
56  results[destination].emplace_back(source, result.value, depComps);
57 
58  // Also consider intra-iteration dependences on the same memory location.
59  // This currently does not consider aliasing.
60  if (src != dst)
61  continue;
62 
63  // Collect surrounding loops to use in dependence components. Only proceed
64  // if we are in the innermost loop.
65  SmallVector<AffineForOp> enclosingLoops;
66  getAffineForIVs(*destination, &enclosingLoops);
67  if (enclosingLoops.size() != depth)
68  continue;
69 
70  // Look for the common parent that src and dst share. If there is none,
71  // there is nothing more to do.
72  SmallVector<Operation *> srcParents;
73  getEnclosingAffineOps(*source, &srcParents);
74  SmallVector<Operation *> dstParents;
75  getEnclosingAffineOps(*destination, &dstParents);
76 
77  Operation *commonParent = nullptr;
78  for (auto *srcParent : llvm::reverse(srcParents)) {
79  for (auto *dstParent : llvm::reverse(dstParents)) {
80  if (srcParent == dstParent)
81  commonParent = srcParent;
82  if (commonParent != nullptr)
83  break;
84  }
85  if (commonParent != nullptr)
86  break;
87  }
88 
89  if (commonParent == nullptr)
90  continue;
91 
92  // Check the common parent's regions.
93  for (auto &commonRegion : commonParent->getRegions()) {
94  if (commonRegion.empty())
95  continue;
96 
97  // Only support structured constructs with single-block regions for now.
98  assert(commonRegion.hasOneBlock() &&
99  "only single-block regions are supported");
100 
101  Block &commonBlock = commonRegion.front();
102 
103  // Find the src and dst ancestor in the common block, if any.
104  Operation *srcOrAncestor = commonBlock.findAncestorOpInBlock(*source);
105  Operation *dstOrAncestor =
106  commonBlock.findAncestorOpInBlock(*destination);
107  if (srcOrAncestor == nullptr || dstOrAncestor == nullptr)
108  continue;
109 
110  // Check if the src or its ancestor is before the dst or its ancestor.
111  if (srcOrAncestor->isBeforeInBlock(dstOrAncestor)) {
112  // Build dependence components for each loop depth.
113  SmallVector<DependenceComponent> intraDeps;
114  for (size_t i = 0; i < depth; ++i) {
115  DependenceComponent depComp;
116  depComp.op = enclosingLoops[i];
117  depComp.lb = 0;
118  depComp.ub = 0;
119  intraDeps.push_back(depComp);
120  }
121 
122  results[destination].emplace_back(
123  source, DependenceResult::HasDependence, intraDeps);
124  }
125  }
126  }
127  }
128 }
129 
130 /// MemoryDependenceAnalysis traverses any AffineForOps in the FuncOp body and
131 /// checks for memory access dependences. Results are captured in a
132 /// MemoryDependenceResult, which can by queried by Operation.
134  Operation *op) {
135  auto funcOp = cast<func::FuncOp>(op);
136 
137  // Collect affine loops grouped by nesting depth.
138  std::vector<SmallVector<AffineForOp, 2>> depthToLoops;
139  mlir::affine::gatherLoops(funcOp, depthToLoops);
140 
141  // Collect load and store operations to check.
142  SmallVector<Operation *> memoryOps;
143  funcOp.walk([&](Operation *op) {
144  if (isa<AffineReadOpInterface, AffineWriteOpInterface>(op))
145  memoryOps.push_back(op);
146  });
147 
148  // For each depth, check memref accesses.
149  for (unsigned depth = 1, e = depthToLoops.size(); depth <= e; ++depth)
150  checkMemrefDependence(memoryOps, depth, results);
151 }
152 
153 /// Returns the dependences, if any, that the given Operation depends on.
154 ArrayRef<MemoryDependence>
156  return results[op];
157 }
158 
159 /// Replaces the dependences, if any, from the oldOp to the newOp.
161  Operation *newOp) {
162  // If oldOp had any dependences.
163  auto it = results.find(oldOp);
164  if (it != results.end())
165  // Move the dependences to newOp.
166  it->first = newOp;
167 
168  // Find any dependences originating from oldOp and make newOp the source.
169  // TODO(mikeurbach): consider adding an inverted index to avoid this scan.
170  for (auto &it : results)
171  for (auto &dep : it.second)
172  if (dep.source == oldOp)
173  dep.source = newOp;
174 }
assert(baseType &&"element must be base type")
static void checkMemrefDependence(SmallVectorImpl< Operation * > &memoryOps, unsigned depth, MemoryDependenceResult &results)
Helper to iterate through memory operation pairs and check for dependences at a given loop nesting de...
DenseMap< Operation *, SmallVector< MemoryDependence > > MemoryDependenceResult
MemoryDependenceResult captures a set of memory dependences.
MemoryDependenceAnalysis(Operation *funcOp)
MemoryDependenceAnalysis traverses any AffineForOps in the FuncOp body and checks for memory access d...
void replaceOp(Operation *oldOp, Operation *newOp)
Replaces the dependences, if any, from the oldOp to the newOp.
ArrayRef< MemoryDependence > getDependences(Operation *)
Returns the dependences, if any, that the given Operation depends on.