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.
BlockInfoBuilder__anon9fb02cfc0111::BlockInfoBuilder30 BlockInfoBuilder() : block(nullptr) {}
31
32 /// Fills the block builder with initial liveness information.
BlockInfoBuilder__anon9fb02cfc0111::BlockInfoBuilder33 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.
updateLiveIn__anon9fb02cfc0111::BlockInfoBuilder87 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.
updateLiveOut__anon9fb02cfc0111::BlockInfoBuilder104 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 /// Builds the internal liveness block mapping.
buildBlockMapping(Operation * operation,DenseMap<Block *,BlockInfoBuilder> & builders)129 static void buildBlockMapping(Operation *operation,
130 DenseMap<Block *, BlockInfoBuilder> &builders) {
131 llvm::SetVector<Block *> toProcess;
132
133 operation->walk([&](Block *block) {
134 BlockInfoBuilder &builder =
135 builders.try_emplace(block, block).first->second;
136
137 if (builder.updateLiveIn())
138 toProcess.insert(block->pred_begin(), block->pred_end());
139 });
140
141 // Propagate the in and out-value sets (fixpoint iteration)
142 while (!toProcess.empty()) {
143 Block *current = toProcess.pop_back_val();
144 BlockInfoBuilder &builder = builders[current];
145
146 // Update the current out values.
147 builder.updateLiveOut(builders);
148
149 // Compute (potentially) updated live in values.
150 if (builder.updateLiveIn())
151 toProcess.insert(current->pred_begin(), current->pred_end());
152 }
153 }
154
155 //===----------------------------------------------------------------------===//
156 // Liveness
157 //===----------------------------------------------------------------------===//
158
159 /// Creates a new Liveness analysis that computes liveness information for all
160 /// associated regions.
Liveness(Operation * op)161 Liveness::Liveness(Operation *op) : operation(op) { build(); }
162
163 /// Initializes the internal mappings.
build()164 void Liveness::build() {
165
166 // Build internal block mapping.
167 DenseMap<Block *, BlockInfoBuilder> builders;
168 buildBlockMapping(operation, builders);
169
170 // Store internal block data.
171 for (auto &entry : builders) {
172 BlockInfoBuilder &builder = entry.second;
173 LivenessBlockInfo &info = blockMapping[entry.first];
174
175 info.block = builder.block;
176 info.inValues = std::move(builder.inValues);
177 info.outValues = std::move(builder.outValues);
178 }
179 }
180
181 /// Gets liveness info (if any) for the given value.
resolveLiveness(Value value) const182 Liveness::OperationListT Liveness::resolveLiveness(Value value) const {
183 OperationListT result;
184 SmallPtrSet<Block *, 32> visited;
185 SmallVector<Block *, 8> toProcess;
186
187 // Start with the defining block
188 Block *currentBlock;
189 if (Operation *defOp = value.getDefiningOp())
190 currentBlock = defOp->getBlock();
191 else
192 currentBlock = value.cast<BlockArgument>().getOwner();
193 toProcess.push_back(currentBlock);
194 visited.insert(currentBlock);
195
196 // Start with all associated blocks
197 for (OpOperand &use : value.getUses()) {
198 Block *useBlock = use.getOwner()->getBlock();
199 if (visited.insert(useBlock).second)
200 toProcess.push_back(useBlock);
201 }
202
203 while (!toProcess.empty()) {
204 // Get block and block liveness information.
205 Block *block = toProcess.back();
206 toProcess.pop_back();
207 const LivenessBlockInfo *blockInfo = getLiveness(block);
208
209 // Note that start and end will be in the same block.
210 Operation *start = blockInfo->getStartOperation(value);
211 Operation *end = blockInfo->getEndOperation(value, start);
212
213 result.push_back(start);
214 while (start != end) {
215 start = start->getNextNode();
216 result.push_back(start);
217 }
218
219 for (Block *successor : block->getSuccessors()) {
220 if (getLiveness(successor)->isLiveIn(value) &&
221 visited.insert(successor).second)
222 toProcess.push_back(successor);
223 }
224 }
225
226 return result;
227 }
228
229 /// Gets liveness info (if any) for the block.
getLiveness(Block * block) const230 const LivenessBlockInfo *Liveness::getLiveness(Block *block) const {
231 auto it = blockMapping.find(block);
232 return it == blockMapping.end() ? nullptr : &it->second;
233 }
234
235 /// Returns a reference to a set containing live-in values.
getLiveIn(Block * block) const236 const Liveness::ValueSetT &Liveness::getLiveIn(Block *block) const {
237 return getLiveness(block)->in();
238 }
239
240 /// Returns a reference to a set containing live-out values.
getLiveOut(Block * block) const241 const Liveness::ValueSetT &Liveness::getLiveOut(Block *block) const {
242 return getLiveness(block)->out();
243 }
244
245 /// Returns true if the given operation represent the last use of the given
246 /// value.
isLastUse(Value value,Operation * operation) const247 bool Liveness::isLastUse(Value value, Operation *operation) const {
248 Block *block = operation->getBlock();
249 const LivenessBlockInfo *blockInfo = getLiveness(block);
250
251 // The given value escapes the associated block.
252 if (blockInfo->isLiveOut(value))
253 return false;
254
255 Operation *endOperation = blockInfo->getEndOperation(value, operation);
256 // If the operation is a real user of `value` the first check is sufficient.
257 // If not, we will have to test whether the end operation is executed before
258 // the given operation in the block.
259 return endOperation == operation || endOperation->isBeforeInBlock(operation);
260 }
261
262 /// Dumps the liveness information in a human readable format.
dump() const263 void Liveness::dump() const { print(llvm::errs()); }
264
265 /// Dumps the liveness information to the given stream.
print(raw_ostream & os) const266 void Liveness::print(raw_ostream &os) const {
267 os << "// ---- Liveness -----\n";
268
269 // Builds unique block/value mappings for testing purposes.
270 DenseMap<Block *, size_t> blockIds;
271 DenseMap<Operation *, size_t> operationIds;
272 DenseMap<Value, size_t> valueIds;
273 operation->walk([&](Block *block) {
274 blockIds.insert({block, blockIds.size()});
275 for (BlockArgument argument : block->getArguments())
276 valueIds.insert({argument, valueIds.size()});
277 for (Operation &operation : *block) {
278 operationIds.insert({&operation, operationIds.size()});
279 for (Value result : operation.getResults())
280 valueIds.insert({result, valueIds.size()});
281 }
282 });
283
284 // Local printing helpers
285 auto printValueRef = [&](Value value) {
286 if (value.getDefiningOp())
287 os << "val_" << valueIds[value];
288 else {
289 auto blockArg = value.cast<BlockArgument>();
290 os << "arg" << blockArg.getArgNumber() << "@"
291 << blockIds[blockArg.getOwner()];
292 }
293 os << " ";
294 };
295
296 auto printValueRefs = [&](const ValueSetT &values) {
297 std::vector<Value> orderedValues(values.begin(), values.end());
298 std::sort(orderedValues.begin(), orderedValues.end(),
299 [&](Value left, Value right) {
300 return valueIds[left] < valueIds[right];
301 });
302 for (Value value : orderedValues)
303 printValueRef(value);
304 };
305
306 // Dump information about in and out values.
307 operation->walk([&](Block *block) {
308 os << "// - Block: " << blockIds[block] << "\n";
309 const auto *liveness = getLiveness(block);
310 os << "// --- LiveIn: ";
311 printValueRefs(liveness->inValues);
312 os << "\n// --- LiveOut: ";
313 printValueRefs(liveness->outValues);
314 os << "\n";
315
316 // Print liveness intervals.
317 os << "// --- BeginLiveness";
318 for (Operation &op : *block) {
319 if (op.getNumResults() < 1)
320 continue;
321 os << "\n";
322 for (Value result : op.getResults()) {
323 os << "// ";
324 printValueRef(result);
325 os << ":";
326 auto liveOperations = resolveLiveness(result);
327 std::sort(liveOperations.begin(), liveOperations.end(),
328 [&](Operation *left, Operation *right) {
329 return operationIds[left] < operationIds[right];
330 });
331 for (Operation *operation : liveOperations) {
332 os << "\n// ";
333 operation->print(os);
334 }
335 }
336 }
337 os << "\n// --- EndLiveness\n";
338 });
339 os << "// -------------------\n";
340 }
341
342 //===----------------------------------------------------------------------===//
343 // LivenessBlockInfo
344 //===----------------------------------------------------------------------===//
345
346 /// Returns true if the given value is in the live-in set.
isLiveIn(Value value) const347 bool LivenessBlockInfo::isLiveIn(Value value) const {
348 return inValues.count(value);
349 }
350
351 /// Returns true if the given value is in the live-out set.
isLiveOut(Value value) const352 bool LivenessBlockInfo::isLiveOut(Value value) const {
353 return outValues.count(value);
354 }
355
356 /// Gets the start operation for the given value (must be referenced in this
357 /// block).
getStartOperation(Value value) const358 Operation *LivenessBlockInfo::getStartOperation(Value value) const {
359 Operation *definingOp = value.getDefiningOp();
360 // The given value is either live-in or is defined
361 // in the scope of this block.
362 if (isLiveIn(value) || !definingOp)
363 return &block->front();
364 return definingOp;
365 }
366
367 /// Gets the end operation for the given value using the start operation
368 /// provided (must be referenced in this block).
getEndOperation(Value value,Operation * startOperation) const369 Operation *LivenessBlockInfo::getEndOperation(Value value,
370 Operation *startOperation) const {
371 // The given value is either dying in this block or live-out.
372 if (isLiveOut(value))
373 return &block->back();
374
375 // Resolve the last operation (must exist by definition).
376 Operation *endOperation = startOperation;
377 for (Operation *useOp : value.getUsers()) {
378 // Find the associated operation in the current block (if any).
379 useOp = block->findAncestorOpInBlock(*useOp);
380 // Check whether the use is in our block and after the current end
381 // operation.
382 if (useOp && endOperation->isBeforeInBlock(useOp))
383 endOperation = useOp;
384 }
385 return endOperation;
386 }
387