Dominance.cpp
11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
//===- Dominance.cpp - Dominator analysis for CFGs ------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Implementation of dominance related classes and instantiations of extern
// templates.
//
//===----------------------------------------------------------------------===//
#include "mlir/IR/Dominance.h"
#include "mlir/IR/Operation.h"
#include "mlir/IR/RegionKindInterface.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/GenericDomTreeConstruction.h"
using namespace mlir;
using namespace mlir::detail;
template class llvm::DominatorTreeBase<Block, /*IsPostDom=*/false>;
template class llvm::DominatorTreeBase<Block, /*IsPostDom=*/true>;
template class llvm::DomTreeNodeBase<Block>;
/// Return true if the region with the given index inside the operation
/// has SSA dominance.
static bool hasSSADominance(Operation *op, unsigned index) {
auto kindInterface = dyn_cast<RegionKindInterface>(op);
return op->isRegistered() &&
(!kindInterface || kindInterface.hasSSADominance(index));
}
//===----------------------------------------------------------------------===//
// DominanceInfoBase
//===----------------------------------------------------------------------===//
template <bool IsPostDom>
void DominanceInfoBase<IsPostDom>::recalculate(Operation *op) {
dominanceInfos.clear();
// Build the dominance for each of the operation regions.
op->walk([&](Operation *op) {
auto kindInterface = dyn_cast<RegionKindInterface>(op);
unsigned numRegions = op->getNumRegions();
for (unsigned i = 0; i < numRegions; i++) {
Region ®ion = op->getRegion(i);
// Don't compute dominance if the region is empty.
if (region.empty())
continue;
// Dominance changes based on the region type. Avoid the helper
// function here so we don't do the region cast repeatedly.
bool hasSSADominance =
op->isRegistered() &&
(!kindInterface || kindInterface.hasSSADominance(i));
// If a region has SSADominance, then compute detailed dominance
// info. Otherwise, all values in the region are live anywhere
// in the region, which is represented as an empty entry in the
// dominanceInfos map.
if (hasSSADominance) {
auto opDominance = std::make_unique<base>();
opDominance->recalculate(region);
dominanceInfos.try_emplace(®ion, std::move(opDominance));
}
}
});
}
/// Walks up the list of containers of the given block and calls the
/// user-defined traversal function for every pair of a region and block that
/// could be found during traversal. If the user-defined function returns true
/// for a given pair, traverseAncestors will return the current block. Nullptr
/// otherwise.
template <typename FuncT>
Block *traverseAncestors(Block *block, const FuncT &func) {
// Invoke the user-defined traversal function in the beginning for the current
// block.
if (func(block))
return block;
Region *region = block->getParent();
while (region) {
Operation *ancestor = region->getParentOp();
// If we have reached to top... return.
if (!ancestor || !(block = ancestor->getBlock()))
break;
// Update the nested region using the new ancestor block.
region = block->getParent();
// Invoke the user-defined traversal function and check whether we can
// already return.
if (func(block))
return block;
}
return nullptr;
}
/// Tries to update the given block references to live in the same region by
/// exploring the relationship of both blocks with respect to their regions.
static bool tryGetBlocksInSameRegion(Block *&a, Block *&b) {
// If both block do not live in the same region, we will have to check their
// parent operations.
if (a->getParent() == b->getParent())
return true;
// Iterate over all ancestors of a and insert them into the map. This allows
// for efficient lookups to find a commonly shared region.
llvm::SmallDenseMap<Region *, Block *, 4> ancestors;
traverseAncestors(a, [&](Block *block) {
ancestors[block->getParent()] = block;
return false;
});
// Try to find a common ancestor starting with regionB.
b = traverseAncestors(
b, [&](Block *block) { return ancestors.count(block->getParent()) > 0; });
// If there is no match, we will not be able to find a common dominator since
// both regions do not share a common parent region.
if (!b)
return false;
// We have found a common parent region. Update block a to refer to this
// region.
auto it = ancestors.find(b->getParent());
assert(it != ancestors.end());
a = it->second;
return true;
}
template <bool IsPostDom>
Block *
DominanceInfoBase<IsPostDom>::findNearestCommonDominator(Block *a,
Block *b) const {
// If either a or b are null, then conservatively return nullptr.
if (!a || !b)
return nullptr;
// Try to find blocks that are in the same region.
if (!tryGetBlocksInSameRegion(a, b))
return nullptr;
// Get and verify dominance information of the common parent region.
Region *parentRegion = a->getParent();
auto infoAIt = dominanceInfos.find(parentRegion);
if (infoAIt == dominanceInfos.end())
return nullptr;
// Since the blocks live in the same region, we can rely on already
// existing dominance functionality.
return infoAIt->second->findNearestCommonDominator(a, b);
}
template <bool IsPostDom>
DominanceInfoNode *DominanceInfoBase<IsPostDom>::getNode(Block *a) {
Region *region = a->getParent();
assert(dominanceInfos.count(region) != 0);
return dominanceInfos[region]->getNode(a);
}
/// Return true if the specified block A properly dominates block B.
template <bool IsPostDom>
bool DominanceInfoBase<IsPostDom>::properlyDominates(Block *a, Block *b) const {
// A block dominates itself but does not properly dominate itself.
if (a == b)
return false;
// If either a or b are null, then conservatively return false.
if (!a || !b)
return false;
// If both blocks are not in the same region, 'a' properly dominates 'b' if
// 'b' is defined in an operation region that (recursively) ends up being
// dominated by 'a'. Walk up the list of containers enclosing B.
Region *regionA = a->getParent();
if (regionA != b->getParent()) {
b = traverseAncestors(
b, [&](Block *block) { return block->getParent() == regionA; });
// If we could not find a valid block b then it is either a not a dominator
// or a post dominator.
if (!b)
return IsPostDom;
// Check to see if the ancestor of 'b' is the same block as 'a'.
if (a == b)
return true;
}
// Otherwise, use the standard dominance functionality.
// If we don't have a dominance information for this region, assume that b is
// dominated by anything.
auto baseInfoIt = dominanceInfos.find(regionA);
if (baseInfoIt == dominanceInfos.end())
return true;
return baseInfoIt->second->properlyDominates(a, b);
}
/// Return true if the specified block is reachable from the entry block of its
/// region.
template <bool IsPostDom>
bool DominanceInfoBase<IsPostDom>::isReachableFromEntry(Block *a) const {
Region *regionA = a->getParent();
auto baseInfoIt = dominanceInfos.find(regionA);
if (baseInfoIt == dominanceInfos.end())
return true;
return baseInfoIt->second->isReachableFromEntry(a);
}
template class detail::DominanceInfoBase</*IsPostDom=*/true>;
template class detail::DominanceInfoBase</*IsPostDom=*/false>;
//===----------------------------------------------------------------------===//
// DominanceInfo
//===----------------------------------------------------------------------===//
/// Return true if operation A properly dominates operation B.
bool DominanceInfo::properlyDominates(Operation *a, Operation *b) const {
Block *aBlock = a->getBlock(), *bBlock = b->getBlock();
Region *aRegion = a->getParentRegion();
unsigned aRegionNum = aRegion->getRegionNumber();
Operation *ancestor = aRegion->getParentOp();
// If a or b are not within a block, then a does not dominate b.
if (!aBlock || !bBlock)
return false;
if (aBlock == bBlock) {
// Dominance changes based on the region type. In a region with SSA
// dominance, uses inside the same block must follow defs. In other
// regions kinds, uses and defs can come in any order inside a block.
if (hasSSADominance(ancestor, aRegionNum)) {
// If the blocks are the same, then check if b is before a in the block.
return a->isBeforeInBlock(b);
}
return true;
}
// Traverse up b's hierarchy to check if b's block is contained in a's.
if (auto *bAncestor = aBlock->findAncestorOpInBlock(*b)) {
// Since we already know that aBlock != bBlock, here bAncestor != b.
// a and bAncestor are in the same block; check if 'a' dominates
// bAncestor.
return dominates(a, bAncestor);
}
// If the blocks are different, check if a's block dominates b's.
return properlyDominates(aBlock, bBlock);
}
/// Return true if value A properly dominates operation B.
bool DominanceInfo::properlyDominates(Value a, Operation *b) const {
if (auto *aOp = a.getDefiningOp()) {
// Dominance changes based on the region type.
auto *aRegion = aOp->getParentRegion();
unsigned aRegionNum = aRegion->getRegionNumber();
Operation *ancestor = aRegion->getParentOp();
// Dominance changes based on the region type. In a region with SSA
// dominance, values defined by an operation cannot be used by the
// operation. In other regions kinds they can be used the operation.
if (hasSSADominance(ancestor, aRegionNum)) {
// The values defined by an operation do *not* dominate any nested
// operations.
if (aOp->getParentRegion() != b->getParentRegion() && aOp->isAncestor(b))
return false;
}
return properlyDominates(aOp, b);
}
// block arguments properly dominate all operations in their own block, so
// we use a dominates check here, not a properlyDominates check.
return dominates(a.cast<BlockArgument>().getOwner(), b->getBlock());
}
void DominanceInfo::updateDFSNumbers() {
for (auto &iter : dominanceInfos)
iter.second->updateDFSNumbers();
}
//===----------------------------------------------------------------------===//
// PostDominanceInfo
//===----------------------------------------------------------------------===//
/// Returns true if statement 'a' properly postdominates statement b.
bool PostDominanceInfo::properlyPostDominates(Operation *a, Operation *b) {
auto *aBlock = a->getBlock(), *bBlock = b->getBlock();
auto *aRegion = a->getParentRegion();
unsigned aRegionNum = aRegion->getRegionNumber();
Operation *ancestor = aRegion->getParentOp();
// If a or b are not within a block, then a does not post dominate b.
if (!aBlock || !bBlock)
return false;
// If the blocks are the same, check if b is before a in the block.
if (aBlock == bBlock) {
// Dominance changes based on the region type.
if (hasSSADominance(ancestor, aRegionNum)) {
// If the blocks are the same, then check if b is before a in the block.
return b->isBeforeInBlock(a);
}
return true;
}
// Traverse up b's hierarchy to check if b's block is contained in a's.
if (auto *bAncestor = a->getBlock()->findAncestorOpInBlock(*b))
// Since we already know that aBlock != bBlock, here bAncestor != b.
// a and bAncestor are in the same block; check if 'a' postdominates
// bAncestor.
return postDominates(a, bAncestor);
// If the blocks are different, check if a's block post dominates b's.
return properlyDominates(aBlock, bBlock);
}