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 ®ion : 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