1 //===- Liveness.cpp - Liveness analysis for MLIR --------------------------===//
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 // Implementation of the liveness analysis.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "mlir/Analysis/Liveness.h"
14 #include "mlir/IR/Block.h"
15 #include "mlir/IR/Operation.h"
16 #include "mlir/IR/Region.h"
17 #include "mlir/IR/Value.h"
18 #include "llvm/ADT/SetOperations.h"
19 #include "llvm/ADT/SetVector.h"
20 #include "llvm/Support/raw_ostream.h"
21 
22 using namespace mlir;
23 
24 namespace {
25 /// Builds and holds block information during the construction phase.
26 struct BlockInfoBuilder {
27   using ValueSetT = Liveness::ValueSetT;
28 
29   /// Constructs an empty block builder.
30   BlockInfoBuilder() : block(nullptr) {}
31 
32   /// Fills the block builder with initial liveness information.
33   BlockInfoBuilder(Block *block) : block(block) {
34     auto gatherOutValues = [&](Value value) {
35       // Check whether this value will be in the outValues set (its uses escape
36       // this block). Due to the SSA properties of the program, the uses must
37       // occur after the definition. Therefore, we do not have to check
38       // additional conditions to detect an escaping value.
39       for (Operation *useOp : value.getUsers()) {
40         Block *ownerBlock = useOp->getBlock();
41         // Find an owner block in the current region. Note that a value does not
42         // escape this block if it is used in a nested region.
43         ownerBlock = block->getParent()->findAncestorBlockInRegion(*ownerBlock);
44         assert(ownerBlock && "Use leaves the current parent region");
45         if (ownerBlock != block) {
46           outValues.insert(value);
47           break;
48         }
49       }
50     };
51 
52     // Mark all block arguments (phis) as defined.
53     for (BlockArgument argument : block->getArguments()) {
54       // Insert value into the set of defined values.
55       defValues.insert(argument);
56 
57       // Gather all out values of all arguments in the current block.
58       gatherOutValues(argument);
59     }
60 
61     // Gather out values of all operations in the current block.
62     for (Operation &operation : *block)
63       for (Value result : operation.getResults())
64         gatherOutValues(result);
65 
66     // Mark all nested operation results as defined.
67     block->walk([&](Operation *op) {
68       for (Value result : op->getResults())
69         defValues.insert(result);
70     });
71 
72     // Check all operations for used operands.
73     block->walk([&](Operation *op) {
74       for (Value operand : op->getOperands()) {
75         // If the operand is already defined in the scope of this
76         // block, we can skip the value in the use set.
77         if (!defValues.count(operand))
78           useValues.insert(operand);
79       }
80     });
81   }
82 
83   /// Updates live-in information of the current block. To do so it uses the
84   /// default liveness-computation formula: newIn = use union out \ def. The
85   /// methods returns true, if the set has changed (newIn != in), false
86   /// otherwise.
87   bool updateLiveIn() {
88     ValueSetT newIn = useValues;
89     llvm::set_union(newIn, outValues);
90     llvm::set_subtract(newIn, defValues);
91 
92     // It is sufficient to check the set sizes (instead of their contents) since
93     // the live-in set can only grow monotonically during all update operations.
94     if (newIn.size() == inValues.size())
95       return false;
96 
97     inValues = newIn;
98     return true;
99   }
100 
101   /// Updates live-out information of the current block. It iterates over all
102   /// successors and unifies their live-in values with the current live-out
103   /// values.
104   template <typename SourceT> void updateLiveOut(SourceT &source) {
105     for (Block *succ : block->getSuccessors()) {
106       BlockInfoBuilder &builder = source[succ];
107       llvm::set_union(outValues, builder.inValues);
108     }
109   }
110 
111   /// The current block.
112   Block *block;
113 
114   /// The set of all live in values.
115   ValueSetT inValues;
116 
117   /// The set of all live out values.
118   ValueSetT outValues;
119 
120   /// The set of all defined values.
121   ValueSetT defValues;
122 
123   /// The set of all used values.
124   ValueSetT useValues;
125 };
126 } // namespace
127 
128 /// Walks all regions (including nested regions recursively) and invokes the
129 /// given function for every block.
130 template <typename FuncT>
131 static void walkRegions(MutableArrayRef<Region> regions, const FuncT &func) {
132   for (Region &region : regions)
133     for (Block &block : region) {
134       func(block);
135 
136       // Traverse all nested regions.
137       for (Operation &operation : block)
138         walkRegions(operation.getRegions(), func);
139     }
140 }
141 
142 /// Builds the internal liveness block mapping.
143 static void buildBlockMapping(MutableArrayRef<Region> regions,
144                               DenseMap<Block *, BlockInfoBuilder> &builders) {
145   llvm::SetVector<Block *> toProcess;
146 
147   walkRegions(regions, [&](Block &block) {
148     BlockInfoBuilder &builder =
149         builders.try_emplace(&block, &block).first->second;
150 
151     if (builder.updateLiveIn())
152       toProcess.insert(block.pred_begin(), block.pred_end());
153   });
154 
155   // Propagate the in and out-value sets (fixpoint iteration)
156   while (!toProcess.empty()) {
157     Block *current = toProcess.pop_back_val();
158     BlockInfoBuilder &builder = builders[current];
159 
160     // Update the current out values.
161     builder.updateLiveOut(builders);
162 
163     // Compute (potentially) updated live in values.
164     if (builder.updateLiveIn())
165       toProcess.insert(current->pred_begin(), current->pred_end());
166   }
167 }
168 
169 //===----------------------------------------------------------------------===//
170 // Liveness
171 //===----------------------------------------------------------------------===//
172 
173 /// Creates a new Liveness analysis that computes liveness information for all
174 /// associated regions.
175 Liveness::Liveness(Operation *op) : operation(op) { build(op->getRegions()); }
176 
177 /// Initializes the internal mappings.
178 void Liveness::build(MutableArrayRef<Region> regions) {
179 
180   // Build internal block mapping.
181   DenseMap<Block *, BlockInfoBuilder> builders;
182   buildBlockMapping(regions, builders);
183 
184   // Store internal block data.
185   for (auto &entry : builders) {
186     BlockInfoBuilder &builder = entry.second;
187     LivenessBlockInfo &info = blockMapping[entry.first];
188 
189     info.block = builder.block;
190     info.inValues = std::move(builder.inValues);
191     info.outValues = std::move(builder.outValues);
192   }
193 }
194 
195 /// Gets liveness info (if any) for the given value.
196 Liveness::OperationListT Liveness::resolveLiveness(Value value) const {
197   OperationListT result;
198   SmallPtrSet<Block *, 32> visited;
199   SmallVector<Block *, 8> toProcess;
200 
201   // Start with the defining block
202   Block *currentBlock;
203   if (Operation *defOp = value.getDefiningOp())
204     currentBlock = defOp->getBlock();
205   else
206     currentBlock = value.cast<BlockArgument>().getOwner();
207   toProcess.push_back(currentBlock);
208   visited.insert(currentBlock);
209 
210   // Start with all associated blocks
211   for (OpOperand &use : value.getUses()) {
212     Block *useBlock = use.getOwner()->getBlock();
213     if (visited.insert(useBlock).second)
214       toProcess.push_back(useBlock);
215   }
216 
217   while (!toProcess.empty()) {
218     // Get block and block liveness information.
219     Block *block = toProcess.back();
220     toProcess.pop_back();
221     const LivenessBlockInfo *blockInfo = getLiveness(block);
222 
223     // Note that start and end will be in the same block.
224     Operation *start = blockInfo->getStartOperation(value);
225     Operation *end = blockInfo->getEndOperation(value, start);
226 
227     result.push_back(start);
228     while (start != end) {
229       start = start->getNextNode();
230       result.push_back(start);
231     }
232 
233     for (Block *successor : block->getSuccessors()) {
234       if (getLiveness(successor)->isLiveIn(value) &&
235           visited.insert(successor).second)
236         toProcess.push_back(successor);
237     }
238   }
239 
240   return result;
241 }
242 
243 /// Gets liveness info (if any) for the block.
244 const LivenessBlockInfo *Liveness::getLiveness(Block *block) const {
245   auto it = blockMapping.find(block);
246   return it == blockMapping.end() ? nullptr : &it->second;
247 }
248 
249 /// Returns a reference to a set containing live-in values.
250 const Liveness::ValueSetT &Liveness::getLiveIn(Block *block) const {
251   return getLiveness(block)->in();
252 }
253 
254 /// Returns a reference to a set containing live-out values.
255 const Liveness::ValueSetT &Liveness::getLiveOut(Block *block) const {
256   return getLiveness(block)->out();
257 }
258 
259 /// Returns true if the given operation represent the last use of the given
260 /// value.
261 bool Liveness::isLastUse(Value value, Operation *operation) const {
262   Block *block = operation->getBlock();
263   const LivenessBlockInfo *blockInfo = getLiveness(block);
264 
265   // The given value escapes the associated block.
266   if (blockInfo->isLiveOut(value))
267     return false;
268 
269   Operation *endOperation = blockInfo->getEndOperation(value, operation);
270   // If the operation is a real user of `value` the first check is sufficient.
271   // If not, we will have to test whether the end operation is executed before
272   // the given operation in the block.
273   return endOperation == operation || endOperation->isBeforeInBlock(operation);
274 }
275 
276 /// Dumps the liveness information in a human readable format.
277 void Liveness::dump() const { print(llvm::errs()); }
278 
279 /// Dumps the liveness information to the given stream.
280 void Liveness::print(raw_ostream &os) const {
281   os << "// ---- Liveness -----\n";
282 
283   // Builds unique block/value mappings for testing purposes.
284   DenseMap<Block *, size_t> blockIds;
285   DenseMap<Operation *, size_t> operationIds;
286   DenseMap<Value, size_t> valueIds;
287   walkRegions(operation->getRegions(), [&](Block &block) {
288     blockIds.insert({&block, blockIds.size()});
289     for (BlockArgument argument : block.getArguments())
290       valueIds.insert({argument, valueIds.size()});
291     for (Operation &operation : block) {
292       operationIds.insert({&operation, operationIds.size()});
293       for (Value result : operation.getResults())
294         valueIds.insert({result, valueIds.size()});
295     }
296   });
297 
298   // Local printing helpers
299   auto printValueRef = [&](Value value) {
300     if (value.getDefiningOp())
301       os << "val_" << valueIds[value];
302     else {
303       auto blockArg = value.cast<BlockArgument>();
304       os << "arg" << blockArg.getArgNumber() << "@"
305          << blockIds[blockArg.getOwner()];
306     }
307     os << " ";
308   };
309 
310   auto printValueRefs = [&](const ValueSetT &values) {
311     std::vector<Value> orderedValues(values.begin(), values.end());
312     std::sort(orderedValues.begin(), orderedValues.end(),
313               [&](Value left, Value right) {
314                 return valueIds[left] < valueIds[right];
315               });
316     for (Value value : orderedValues)
317       printValueRef(value);
318   };
319 
320   // Dump information about in and out values.
321   walkRegions(operation->getRegions(), [&](Block &block) {
322     os << "// - Block: " << blockIds[&block] << "\n";
323     auto liveness = getLiveness(&block);
324     os << "// --- LiveIn: ";
325     printValueRefs(liveness->inValues);
326     os << "\n// --- LiveOut: ";
327     printValueRefs(liveness->outValues);
328     os << "\n";
329 
330     // Print liveness intervals.
331     os << "// --- BeginLiveness";
332     for (Operation &op : block) {
333       if (op.getNumResults() < 1)
334         continue;
335       os << "\n";
336       for (Value result : op.getResults()) {
337         os << "// ";
338         printValueRef(result);
339         os << ":";
340         auto liveOperations = resolveLiveness(result);
341         std::sort(liveOperations.begin(), liveOperations.end(),
342                   [&](Operation *left, Operation *right) {
343                     return operationIds[left] < operationIds[right];
344                   });
345         for (Operation *operation : liveOperations) {
346           os << "\n//     ";
347           operation->print(os);
348         }
349       }
350     }
351     os << "\n// --- EndLiveness\n";
352   });
353   os << "// -------------------\n";
354 }
355 
356 //===----------------------------------------------------------------------===//
357 // LivenessBlockInfo
358 //===----------------------------------------------------------------------===//
359 
360 /// Returns true if the given value is in the live-in set.
361 bool LivenessBlockInfo::isLiveIn(Value value) const {
362   return inValues.count(value);
363 }
364 
365 /// Returns true if the given value is in the live-out set.
366 bool LivenessBlockInfo::isLiveOut(Value value) const {
367   return outValues.count(value);
368 }
369 
370 /// Gets the start operation for the given value (must be referenced in this
371 /// block).
372 Operation *LivenessBlockInfo::getStartOperation(Value value) const {
373   Operation *definingOp = value.getDefiningOp();
374   // The given value is either live-in or is defined
375   // in the scope of this block.
376   if (isLiveIn(value) || !definingOp)
377     return &block->front();
378   return definingOp;
379 }
380 
381 /// Gets the end operation for the given value using the start operation
382 /// provided (must be referenced in this block).
383 Operation *LivenessBlockInfo::getEndOperation(Value value,
384                                               Operation *startOperation) const {
385   // The given value is either dying in this block or live-out.
386   if (isLiveOut(value))
387     return &block->back();
388 
389   // Resolve the last operation (must exist by definition).
390   Operation *endOperation = startOperation;
391   for (Operation *useOp : value.getUsers()) {
392     // Find the associated operation in the current block (if any).
393     useOp = block->findAncestorOpInBlock(*useOp);
394     // Check whether the use is in our block and after the current end
395     // operation.
396     if (useOp && endOperation->isBeforeInBlock(useOp))
397       endOperation = useOp;
398   }
399   return endOperation;
400 }
401