1 //===- LivenessAnalysis.cpp - Liveness analysis ---------------------------===// 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 #include "mlir/IR/SymbolTable.h" 10 #include <cassert> 11 #include <mlir/Analysis/DataFlow/LivenessAnalysis.h> 12 13 #include <mlir/Analysis/DataFlow/ConstantPropagationAnalysis.h> 14 #include <mlir/Analysis/DataFlow/DeadCodeAnalysis.h> 15 #include <mlir/Analysis/DataFlow/SparseAnalysis.h> 16 #include <mlir/Analysis/DataFlowFramework.h> 17 #include <mlir/IR/Operation.h> 18 #include <mlir/IR/Value.h> 19 #include <mlir/Interfaces/CallInterfaces.h> 20 #include <mlir/Interfaces/SideEffectInterfaces.h> 21 #include <mlir/Support/LLVM.h> 22 23 using namespace mlir; 24 using namespace mlir::dataflow; 25 26 //===----------------------------------------------------------------------===// 27 // Liveness 28 //===----------------------------------------------------------------------===// 29 30 void Liveness::print(raw_ostream &os) const { 31 os << (isLive ? "live" : "not live"); 32 } 33 34 ChangeResult Liveness::markLive() { 35 bool wasLive = isLive; 36 isLive = true; 37 return wasLive ? ChangeResult::NoChange : ChangeResult::Change; 38 } 39 40 ChangeResult Liveness::meet(const AbstractSparseLattice &other) { 41 const auto *otherLiveness = reinterpret_cast<const Liveness *>(&other); 42 return otherLiveness->isLive ? markLive() : ChangeResult::NoChange; 43 } 44 45 //===----------------------------------------------------------------------===// 46 // LivenessAnalysis 47 //===----------------------------------------------------------------------===// 48 49 /// For every value, liveness analysis determines whether or not it is "live". 50 /// 51 /// A value is considered "live" iff it: 52 /// (1) has memory effects OR 53 /// (2) is returned by a public function OR 54 /// (3) is used to compute a value of type (1) or (2). 55 /// It is also to be noted that a value could be of multiple types (1/2/3) at 56 /// the same time. 57 /// 58 /// A value "has memory effects" iff it: 59 /// (1.a) is an operand of an op with memory effects OR 60 /// (1.b) is a non-forwarded branch operand and its branch op could take the 61 /// control to a block that has an op with memory effects OR 62 /// (1.c) is a non-forwarded call operand. 63 /// 64 /// A value `A` is said to be "used to compute" value `B` iff `B` cannot be 65 /// computed in the absence of `A`. Thus, in this implementation, we say that 66 /// value `A` is used to compute value `B` iff: 67 /// (3.a) `B` is a result of an op with operand `A` OR 68 /// (3.b) `A` is used to compute some value `C` and `C` is used to compute 69 /// `B`. 70 71 LogicalResult 72 LivenessAnalysis::visitOperation(Operation *op, ArrayRef<Liveness *> operands, 73 ArrayRef<const Liveness *> results) { 74 // This marks values of type (1.a) liveness as "live". 75 if (!isMemoryEffectFree(op)) { 76 for (auto *operand : operands) 77 propagateIfChanged(operand, operand->markLive()); 78 } 79 80 // This marks values of type (3) liveness as "live". 81 bool foundLiveResult = false; 82 for (const Liveness *r : results) { 83 if (r->isLive && !foundLiveResult) { 84 // It is assumed that each operand is used to compute each result of an 85 // op. Thus, if at least one result is live, each operand is live. 86 for (Liveness *operand : operands) 87 meet(operand, *r); 88 foundLiveResult = true; 89 } 90 addDependency(const_cast<Liveness *>(r), getProgramPointAfter(op)); 91 } 92 return success(); 93 } 94 95 void LivenessAnalysis::visitBranchOperand(OpOperand &operand) { 96 // We know (at the moment) and assume (for the future) that `operand` is a 97 // non-forwarded branch operand of a `RegionBranchOpInterface`, 98 // `BranchOpInterface`, `RegionBranchTerminatorOpInterface` or return-like op. 99 Operation *op = operand.getOwner(); 100 assert((isa<RegionBranchOpInterface>(op) || isa<BranchOpInterface>(op) || 101 isa<RegionBranchTerminatorOpInterface>(op)) && 102 "expected the op to be `RegionBranchOpInterface`, " 103 "`BranchOpInterface` or `RegionBranchTerminatorOpInterface`"); 104 105 // The lattices of the non-forwarded branch operands don't get updated like 106 // the forwarded branch operands or the non-branch operands. Thus they need 107 // to be handled separately. This is where we handle them. 108 109 // This marks values of type (1.b) liveness as "live". A non-forwarded 110 // branch operand will be live if a block where its op could take the control 111 // has an op with memory effects. 112 // Populating such blocks in `blocks`. 113 SmallVector<Block *, 4> blocks; 114 if (isa<RegionBranchOpInterface>(op)) { 115 // When the op is a `RegionBranchOpInterface`, like an `scf.for` or an 116 // `scf.index_switch` op, its branch operand controls the flow into this 117 // op's regions. 118 for (Region ®ion : op->getRegions()) { 119 for (Block &block : region) 120 blocks.push_back(&block); 121 } 122 } else if (isa<BranchOpInterface>(op)) { 123 // When the op is a `BranchOpInterface`, like a `cf.cond_br` or a 124 // `cf.switch` op, its branch operand controls the flow into this op's 125 // successors. 126 blocks = op->getSuccessors(); 127 } else { 128 // When the op is a `RegionBranchTerminatorOpInterface`, like an 129 // `scf.condition` op or return-like, like an `scf.yield` op, its branch 130 // operand controls the flow into this op's parent's (which is a 131 // `RegionBranchOpInterface`'s) regions. 132 Operation *parentOp = op->getParentOp(); 133 assert(isa<RegionBranchOpInterface>(parentOp) && 134 "expected parent op to implement `RegionBranchOpInterface`"); 135 for (Region ®ion : parentOp->getRegions()) { 136 for (Block &block : region) 137 blocks.push_back(&block); 138 } 139 } 140 bool foundMemoryEffectingOp = false; 141 for (Block *block : blocks) { 142 if (foundMemoryEffectingOp) 143 break; 144 for (Operation &nestedOp : *block) { 145 if (!isMemoryEffectFree(&nestedOp)) { 146 Liveness *operandLiveness = getLatticeElement(operand.get()); 147 propagateIfChanged(operandLiveness, operandLiveness->markLive()); 148 foundMemoryEffectingOp = true; 149 break; 150 } 151 } 152 } 153 154 // Now that we have checked for memory-effecting ops in the blocks of concern, 155 // we will simply visit the op with this non-forwarded operand to potentially 156 // mark it "live" due to type (1.a/3) liveness. 157 SmallVector<Liveness *, 4> operandLiveness; 158 operandLiveness.push_back(getLatticeElement(operand.get())); 159 SmallVector<const Liveness *, 4> resultsLiveness; 160 for (const Value result : op->getResults()) 161 resultsLiveness.push_back(getLatticeElement(result)); 162 (void)visitOperation(op, operandLiveness, resultsLiveness); 163 164 // We also visit the parent op with the parent's results and this operand if 165 // `op` is a `RegionBranchTerminatorOpInterface` because its non-forwarded 166 // operand depends on not only its memory effects/results but also on those of 167 // its parent's. 168 if (!isa<RegionBranchTerminatorOpInterface>(op)) 169 return; 170 Operation *parentOp = op->getParentOp(); 171 SmallVector<const Liveness *, 4> parentResultsLiveness; 172 for (const Value parentResult : parentOp->getResults()) 173 parentResultsLiveness.push_back(getLatticeElement(parentResult)); 174 (void)visitOperation(parentOp, operandLiveness, parentResultsLiveness); 175 } 176 177 void LivenessAnalysis::visitCallOperand(OpOperand &operand) { 178 // We know (at the moment) and assume (for the future) that `operand` is a 179 // non-forwarded call operand of an op implementing `CallOpInterface`. 180 assert(isa<CallOpInterface>(operand.getOwner()) && 181 "expected the op to implement `CallOpInterface`"); 182 183 // The lattices of the non-forwarded call operands don't get updated like the 184 // forwarded call operands or the non-call operands. Thus they need to be 185 // handled separately. This is where we handle them. 186 187 // This marks values of type (1.c) liveness as "live". A non-forwarded 188 // call operand is live. 189 Liveness *operandLiveness = getLatticeElement(operand.get()); 190 propagateIfChanged(operandLiveness, operandLiveness->markLive()); 191 } 192 193 void LivenessAnalysis::setToExitState(Liveness *lattice) { 194 // This marks values of type (2) liveness as "live". 195 (void)lattice->markLive(); 196 } 197 198 //===----------------------------------------------------------------------===// 199 // RunLivenessAnalysis 200 //===----------------------------------------------------------------------===// 201 202 RunLivenessAnalysis::RunLivenessAnalysis(Operation *op) { 203 SymbolTableCollection symbolTable; 204 205 solver.load<DeadCodeAnalysis>(); 206 solver.load<SparseConstantPropagation>(); 207 solver.load<LivenessAnalysis>(symbolTable); 208 (void)solver.initializeAndRun(op); 209 } 210 211 const Liveness *RunLivenessAnalysis::getLiveness(Value val) { 212 return solver.lookupState<Liveness>(val); 213 } 214