1 //===- RegionUtils.cpp - Region-related transformation utilities ----------===//
2 //
3 // Copyright 2019 The MLIR Authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //   http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 // =============================================================================
17 
18 #include "mlir/Transforms/RegionUtils.h"
19 #include "mlir/IR/Block.h"
20 #include "mlir/IR/Operation.h"
21 #include "mlir/IR/RegionGraphTraits.h"
22 #include "mlir/IR/Value.h"
23 
24 #include "llvm/ADT/DepthFirstIterator.h"
25 #include "llvm/ADT/PostOrderIterator.h"
26 #include "llvm/ADT/SmallSet.h"
27 
28 using namespace mlir;
29 
30 void mlir::replaceAllUsesInRegionWith(Value *orig, Value *replacement,
31                                       Region &region) {
32   for (IROperand &use : llvm::make_early_inc_range(orig->getUses())) {
33     if (region.isAncestor(use.getOwner()->getParentRegion()))
34       use.set(replacement);
35   }
36 }
37 
38 void mlir::visitUsedValuesDefinedAbove(
39     Region &region, Region &limit, function_ref<void(OpOperand *)> callback) {
40   assert(limit.isAncestor(&region) &&
41          "expected isolation limit to be an ancestor of the given region");
42 
43   // Collect proper ancestors of `limit` upfront to avoid traversing the region
44   // tree for every value.
45   SmallPtrSet<Region *, 4> properAncestors;
46   for (auto *reg = limit.getParentRegion(); reg != nullptr;
47        reg = reg->getParentRegion()) {
48     properAncestors.insert(reg);
49   }
50 
51   region.walk([callback, &properAncestors](Operation *op) {
52     for (OpOperand &operand : op->getOpOperands())
53       // Callback on values defined in a proper ancestor of region.
54       if (properAncestors.count(operand.get()->getParentRegion()))
55         callback(&operand);
56   });
57 }
58 
59 void mlir::visitUsedValuesDefinedAbove(
60     MutableArrayRef<Region> regions, function_ref<void(OpOperand *)> callback) {
61   for (Region &region : regions)
62     visitUsedValuesDefinedAbove(region, region, callback);
63 }
64 
65 void mlir::getUsedValuesDefinedAbove(Region &region, Region &limit,
66                                      llvm::SetVector<Value *> &values) {
67   visitUsedValuesDefinedAbove(region, limit, [&](OpOperand *operand) {
68     values.insert(operand->get());
69   });
70 }
71 
72 void mlir::getUsedValuesDefinedAbove(MutableArrayRef<Region> regions,
73                                      llvm::SetVector<Value *> &values) {
74   for (Region &region : regions)
75     getUsedValuesDefinedAbove(region, region, values);
76 }
77 
78 //===----------------------------------------------------------------------===//
79 // Unreachable Block Elimination
80 //===----------------------------------------------------------------------===//
81 
82 /// Erase the unreachable blocks within the provided regions. Returns success
83 /// if any blocks were erased, failure otherwise.
84 // TODO: We could likely merge this with the DCE algorithm below.
85 static LogicalResult eraseUnreachableBlocks(MutableArrayRef<Region> regions) {
86   // Set of blocks found to be reachable within a given region.
87   llvm::df_iterator_default_set<Block *, 16> reachable;
88   // If any blocks were found to be dead.
89   bool erasedDeadBlocks = false;
90 
91   SmallVector<Region *, 1> worklist;
92   worklist.reserve(regions.size());
93   for (Region &region : regions)
94     worklist.push_back(&region);
95   while (!worklist.empty()) {
96     Region *region = worklist.pop_back_val();
97     if (region->empty())
98       continue;
99 
100     // If this is a single block region, just collect the nested regions.
101     if (std::next(region->begin()) == region->end()) {
102       for (Operation &op : region->front())
103         for (Region &region : op.getRegions())
104           worklist.push_back(&region);
105       continue;
106     }
107 
108     // Mark all reachable blocks.
109     reachable.clear();
110     for (Block *block : depth_first_ext(&region->front(), reachable))
111       (void)block /* Mark all reachable blocks */;
112 
113     // Collect all of the dead blocks and push the live regions onto the
114     // worklist.
115     for (Block &block : llvm::make_early_inc_range(*region)) {
116       if (!reachable.count(&block)) {
117         block.dropAllDefinedValueUses();
118         block.erase();
119         erasedDeadBlocks = true;
120         continue;
121       }
122 
123       // Walk any regions within this block.
124       for (Operation &op : block)
125         for (Region &region : op.getRegions())
126           worklist.push_back(&region);
127     }
128   }
129 
130   return success(erasedDeadBlocks);
131 }
132 
133 //===----------------------------------------------------------------------===//
134 // Dead Code Elimination
135 //===----------------------------------------------------------------------===//
136 
137 namespace {
138 /// Data structure used to track which values have already been proved live.
139 ///
140 /// Because Operation's can have multiple results, this data structure tracks
141 /// liveness for both Value's and Operation's to avoid having to look through
142 /// all Operation results when analyzing a use.
143 ///
144 /// This data structure essentially tracks the dataflow lattice.
145 /// The set of values/ops proved live increases monotonically to a fixed-point.
146 class LiveMap {
147 public:
148   /// Value methods.
149   bool wasProvenLive(Value *value) { return liveValues.count(value); }
150   void setProvedLive(Value *value) {
151     changed |= liveValues.insert(value).second;
152   }
153 
154   /// Operation methods.
155   bool wasProvenLive(Operation *op) { return liveOps.count(op); }
156   void setProvedLive(Operation *op) { changed |= liveOps.insert(op).second; }
157 
158   /// Methods for tracking if we have reached a fixed-point.
159   void resetChanged() { changed = false; }
160   bool hasChanged() { return changed; }
161 
162 private:
163   bool changed = false;
164   DenseSet<Value *> liveValues;
165   DenseSet<Operation *> liveOps;
166 };
167 } // namespace
168 
169 static bool isUseSpeciallyKnownDead(OpOperand &use, LiveMap &liveMap) {
170   Operation *owner = use.getOwner();
171   unsigned operandIndex = use.getOperandNumber();
172   // This pass generally treats all uses of an op as live if the op itself is
173   // considered live. However, for successor operands to terminators we need a
174   // finer-grained notion where we deduce liveness for operands individually.
175   // The reason for this is easiest to think about in terms of a classical phi
176   // node based SSA IR, where each successor operand is really an operand to a
177   // *separate* phi node, rather than all operands to the branch itself as with
178   // the block argument representation that MLIR uses.
179   //
180   // And similarly, because each successor operand is really an operand to a phi
181   // node, rather than to the terminator op itself, a terminator op can't e.g.
182   // "print" the value of a successor operand.
183   if (owner->isKnownTerminator()) {
184     if (auto arg = owner->getSuccessorBlockArgument(operandIndex))
185       return !liveMap.wasProvenLive(*arg);
186     return false;
187   }
188   return false;
189 }
190 
191 static void processValue(Value *value, LiveMap &liveMap) {
192   bool provedLive = llvm::any_of(value->getUses(), [&](OpOperand &use) {
193     if (isUseSpeciallyKnownDead(use, liveMap))
194       return false;
195     return liveMap.wasProvenLive(use.getOwner());
196   });
197   if (provedLive)
198     liveMap.setProvedLive(value);
199 }
200 
201 static bool isOpIntrinsicallyLive(Operation *op) {
202   // This pass doesn't modify the CFG, so terminators are never deleted.
203   if (!op->isKnownNonTerminator())
204     return true;
205   // If the op has a side effect, we treat it as live.
206   if (!op->hasNoSideEffect())
207     return true;
208   return false;
209 }
210 
211 static void propagateLiveness(Region &region, LiveMap &liveMap);
212 static void propagateLiveness(Operation *op, LiveMap &liveMap) {
213   // All Value's are either a block argument or an op result.
214   // We call processValue on those cases.
215 
216   // Recurse on any regions the op has.
217   for (Region &region : op->getRegions())
218     propagateLiveness(region, liveMap);
219 
220   // Process the op itself.
221   if (isOpIntrinsicallyLive(op)) {
222     liveMap.setProvedLive(op);
223     return;
224   }
225   for (Value *value : op->getResults())
226     processValue(value, liveMap);
227   bool provedLive = llvm::any_of(op->getResults(), [&](Value *value) {
228     return liveMap.wasProvenLive(value);
229   });
230   if (provedLive)
231     liveMap.setProvedLive(op);
232 }
233 
234 static void propagateLiveness(Region &region, LiveMap &liveMap) {
235   if (region.empty())
236     return;
237 
238   for (Block *block : llvm::post_order(&region.front())) {
239     // We process block arguments after the ops in the block, to promote
240     // faster convergence to a fixed point (we try to visit uses before defs).
241     for (Operation &op : llvm::reverse(block->getOperations()))
242       propagateLiveness(&op, liveMap);
243     for (Value *value : block->getArguments())
244       processValue(value, liveMap);
245   }
246 }
247 
248 static void eraseTerminatorSuccessorOperands(Operation *terminator,
249                                              LiveMap &liveMap) {
250   for (unsigned succI = 0, succE = terminator->getNumSuccessors();
251        succI < succE; succI++) {
252     // Iterating successors in reverse is not strictly needed, since we
253     // aren't erasing any successors. But it is slightly more efficient
254     // since it will promote later operands of the terminator being erased
255     // first, reducing the quadratic-ness.
256     unsigned succ = succE - succI - 1;
257     for (unsigned argI = 0, argE = terminator->getNumSuccessorOperands(succ);
258          argI < argE; argI++) {
259       // Iterating args in reverse is needed for correctness, to avoid
260       // shifting later args when earlier args are erased.
261       unsigned arg = argE - argI - 1;
262       Value *value = terminator->getSuccessor(succ)->getArgument(arg);
263       if (!liveMap.wasProvenLive(value)) {
264         terminator->eraseSuccessorOperand(succ, arg);
265       }
266     }
267   }
268 }
269 
270 static LogicalResult deleteDeadness(MutableArrayRef<Region> regions,
271                                     LiveMap &liveMap) {
272   bool erasedAnything = false;
273   for (Region &region : regions) {
274     if (region.empty())
275       continue;
276 
277     // We do the deletion in an order that deletes all uses before deleting
278     // defs.
279     // MLIR's SSA structural invariants guarantee that except for block
280     // arguments, the use-def graph is acyclic, so this is possible with a
281     // single walk of ops and then a final pass to clean up block arguments.
282     //
283     // To do this, we visit ops in an order that visits domtree children
284     // before domtree parents. A CFG post-order (with reverse iteration with a
285     // block) satisfies that without needing an explicit domtree calculation.
286     for (Block *block : llvm::post_order(&region.front())) {
287       eraseTerminatorSuccessorOperands(block->getTerminator(), liveMap);
288       for (Operation &childOp :
289            llvm::make_early_inc_range(llvm::reverse(block->getOperations()))) {
290         erasedAnything |=
291             succeeded(deleteDeadness(childOp.getRegions(), liveMap));
292         if (!liveMap.wasProvenLive(&childOp)) {
293           erasedAnything = true;
294           childOp.erase();
295         }
296       }
297     }
298     // Delete block arguments.
299     // The entry block has an unknown contract with their enclosing block, so
300     // skip it.
301     for (Block &block : llvm::drop_begin(region.getBlocks(), 1)) {
302       // Iterate in reverse to avoid shifting later arguments when deleting
303       // earlier arguments.
304       for (unsigned i = 0, e = block.getNumArguments(); i < e; i++)
305         if (!liveMap.wasProvenLive(block.getArgument(e - i - 1))) {
306           block.eraseArgument(e - i - 1, /*updatePredTerms=*/false);
307           erasedAnything = true;
308         }
309     }
310   }
311   return success(erasedAnything);
312 }
313 
314 // This function performs a simple dead code elimination algorithm over the
315 // given regions.
316 //
317 // The overall goal is to prove that Values are dead, which allows deleting ops
318 // and block arguments.
319 //
320 // This uses an optimistic algorithm that assumes everything is dead until
321 // proved otherwise, allowing it to delete recursively dead cycles.
322 //
323 // This is a simple fixed-point dataflow analysis algorithm on a lattice
324 // {Dead,Alive}. Because liveness flows backward, we generally try to
325 // iterate everything backward to speed up convergence to the fixed-point. This
326 // allows for being able to delete recursively dead cycles of the use-def graph,
327 // including block arguments.
328 //
329 // This function returns success if any operations or arguments were deleted,
330 // failure otherwise.
331 static LogicalResult runRegionDCE(MutableArrayRef<Region> regions) {
332   assert(regions.size() == 1);
333 
334   LiveMap liveMap;
335   do {
336     liveMap.resetChanged();
337 
338     for (Region &region : regions)
339       propagateLiveness(region, liveMap);
340   } while (liveMap.hasChanged());
341 
342   return deleteDeadness(regions, liveMap);
343 }
344 
345 //===----------------------------------------------------------------------===//
346 // Region Simplification
347 //===----------------------------------------------------------------------===//
348 
349 /// Run a set of structural simplifications over the given regions. This
350 /// includes transformations like unreachable block elimination, dead argument
351 /// elimination, as well as some other DCE. This function returns success if any
352 /// of the regions were simplified, failure otherwise.
353 LogicalResult mlir::simplifyRegions(MutableArrayRef<Region> regions) {
354   LogicalResult eliminatedBlocks = eraseUnreachableBlocks(regions);
355   LogicalResult eliminatedOpsOrArgs = runRegionDCE(regions);
356   return success(succeeded(eliminatedBlocks) || succeeded(eliminatedOpsOrArgs));
357 }
358