CIRCT 20.0.0git
Loading...
Searching...
No Matches
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
24using namespace mlir;
25using namespace mlir::affine;
26using namespace circt::analysis;
27
28/// Helper to iterate through memory operation pairs and check for dependences
29/// at a given loop nesting depth.
30static 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.
154ArrayRef<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.