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