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