1c095afcbSMogball //===- DeadCodeAnalysis.cpp - Dead code analysis --------------------------===// 2c095afcbSMogball // 3c095afcbSMogball // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4c095afcbSMogball // See https://llvm.org/LICENSE.txt for license information. 5c095afcbSMogball // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6c095afcbSMogball // 7c095afcbSMogball //===----------------------------------------------------------------------===// 8c095afcbSMogball 9c095afcbSMogball #include "mlir/Analysis/DataFlow/DeadCodeAnalysis.h" 10c095afcbSMogball #include "mlir/Analysis/DataFlow/ConstantPropagationAnalysis.h" 11e48038a8SMehdi Amini #include "mlir/Analysis/DataFlow/SparseAnalysis.h" 126a666737SZhixun Tan #include "mlir/Analysis/DataFlowFramework.h" 13e48038a8SMehdi Amini #include "mlir/IR/Attributes.h" 14e48038a8SMehdi Amini #include "mlir/IR/Block.h" 15e48038a8SMehdi Amini #include "mlir/IR/Diagnostics.h" 16e48038a8SMehdi Amini #include "mlir/IR/Location.h" 17e48038a8SMehdi Amini #include "mlir/IR/Operation.h" 18e48038a8SMehdi Amini #include "mlir/IR/SymbolTable.h" 19e48038a8SMehdi Amini #include "mlir/IR/Value.h" 20e48038a8SMehdi Amini #include "mlir/IR/ValueRange.h" 21c095afcbSMogball #include "mlir/Interfaces/CallInterfaces.h" 22c095afcbSMogball #include "mlir/Interfaces/ControlFlowInterfaces.h" 23e48038a8SMehdi Amini #include "mlir/Support/LLVM.h" 24e48038a8SMehdi Amini #include "llvm/Support/Casting.h" 25e48038a8SMehdi Amini #include <cassert> 26a1fe1f5fSKazu Hirata #include <optional> 27c095afcbSMogball 28c095afcbSMogball using namespace mlir; 29c095afcbSMogball using namespace mlir::dataflow; 30c095afcbSMogball 31c095afcbSMogball //===----------------------------------------------------------------------===// 32c095afcbSMogball // Executable 33c095afcbSMogball //===----------------------------------------------------------------------===// 34c095afcbSMogball 35c095afcbSMogball ChangeResult Executable::setToLive() { 36c095afcbSMogball if (live) 37c095afcbSMogball return ChangeResult::NoChange; 38c095afcbSMogball live = true; 39c095afcbSMogball return ChangeResult::Change; 40c095afcbSMogball } 41c095afcbSMogball 42c095afcbSMogball void Executable::print(raw_ostream &os) const { 43c095afcbSMogball os << (live ? "live" : "dead"); 44c095afcbSMogball } 45c095afcbSMogball 46c095afcbSMogball void Executable::onUpdate(DataFlowSolver *solver) const { 476a666737SZhixun Tan AnalysisState::onUpdate(solver); 486a666737SZhixun Tan 494b3f251bSdonald chen if (ProgramPoint *pp = llvm::dyn_cast_if_present<ProgramPoint *>(anchor)) { 504b3f251bSdonald chen if (pp->isBlockStart()) { 51c095afcbSMogball // Re-invoke the analyses on the block itself. 52c095afcbSMogball for (DataFlowAnalysis *analysis : subscribers) 534b3f251bSdonald chen solver->enqueue({pp, analysis}); 54c095afcbSMogball // Re-invoke the analyses on all operations in the block. 55c095afcbSMogball for (DataFlowAnalysis *analysis : subscribers) 564b3f251bSdonald chen for (Operation &op : *pp->getBlock()) 574b3f251bSdonald chen solver->enqueue({solver->getProgramPointAfter(&op), analysis}); 58b6603e1bSdonald chen } 59b6603e1bSdonald chen } else if (auto *latticeAnchor = 60b6603e1bSdonald chen llvm::dyn_cast_if_present<GenericLatticeAnchor *>(anchor)) { 61c095afcbSMogball // Re-invoke the analysis on the successor block. 62b6603e1bSdonald chen if (auto *edge = dyn_cast<CFGEdge>(latticeAnchor)) { 63c095afcbSMogball for (DataFlowAnalysis *analysis : subscribers) 644b3f251bSdonald chen solver->enqueue( 654b3f251bSdonald chen {solver->getProgramPointBefore(edge->getTo()), analysis}); 66c095afcbSMogball } 67c095afcbSMogball } 68c095afcbSMogball } 69c095afcbSMogball 70c095afcbSMogball //===----------------------------------------------------------------------===// 71c095afcbSMogball // PredecessorState 72c095afcbSMogball //===----------------------------------------------------------------------===// 73c095afcbSMogball 74c095afcbSMogball void PredecessorState::print(raw_ostream &os) const { 75c095afcbSMogball if (allPredecessorsKnown()) 76c095afcbSMogball os << "(all) "; 77c095afcbSMogball os << "predecessors:\n"; 78c095afcbSMogball for (Operation *op : getKnownPredecessors()) 79c095afcbSMogball os << " " << *op << "\n"; 80c095afcbSMogball } 81c095afcbSMogball 829432fbfeSMogball ChangeResult PredecessorState::join(Operation *predecessor) { 839432fbfeSMogball return knownPredecessors.insert(predecessor) ? ChangeResult::Change 849432fbfeSMogball : ChangeResult::NoChange; 859432fbfeSMogball } 869432fbfeSMogball 879432fbfeSMogball ChangeResult PredecessorState::join(Operation *predecessor, ValueRange inputs) { 889432fbfeSMogball ChangeResult result = join(predecessor); 899432fbfeSMogball if (!inputs.empty()) { 909432fbfeSMogball ValueRange &curInputs = successorInputs[predecessor]; 919432fbfeSMogball if (curInputs != inputs) { 929432fbfeSMogball curInputs = inputs; 939432fbfeSMogball result |= ChangeResult::Change; 949432fbfeSMogball } 959432fbfeSMogball } 969432fbfeSMogball return result; 979432fbfeSMogball } 989432fbfeSMogball 99c095afcbSMogball //===----------------------------------------------------------------------===// 100c095afcbSMogball // CFGEdge 101c095afcbSMogball //===----------------------------------------------------------------------===// 102c095afcbSMogball 103c095afcbSMogball Location CFGEdge::getLoc() const { 104c095afcbSMogball return FusedLoc::get( 105c095afcbSMogball getFrom()->getParent()->getContext(), 106c095afcbSMogball {getFrom()->getParent()->getLoc(), getTo()->getParent()->getLoc()}); 107c095afcbSMogball } 108c095afcbSMogball 109c095afcbSMogball void CFGEdge::print(raw_ostream &os) const { 110c095afcbSMogball getFrom()->print(os); 111c095afcbSMogball os << "\n -> \n"; 112c095afcbSMogball getTo()->print(os); 113c095afcbSMogball } 114c095afcbSMogball 115c095afcbSMogball //===----------------------------------------------------------------------===// 116c095afcbSMogball // DeadCodeAnalysis 117c095afcbSMogball //===----------------------------------------------------------------------===// 118c095afcbSMogball 119c095afcbSMogball DeadCodeAnalysis::DeadCodeAnalysis(DataFlowSolver &solver) 120c095afcbSMogball : DataFlowAnalysis(solver) { 121b6603e1bSdonald chen registerAnchorKind<CFGEdge>(); 122c095afcbSMogball } 123c095afcbSMogball 124c095afcbSMogball LogicalResult DeadCodeAnalysis::initialize(Operation *top) { 125c095afcbSMogball // Mark the top-level blocks as executable. 126c095afcbSMogball for (Region ®ion : top->getRegions()) { 127c095afcbSMogball if (region.empty()) 128c095afcbSMogball continue; 1294b3f251bSdonald chen auto *state = 1304b3f251bSdonald chen getOrCreate<Executable>(getProgramPointBefore(®ion.front())); 131c095afcbSMogball propagateIfChanged(state, state->setToLive()); 132c095afcbSMogball } 133c095afcbSMogball 134c095afcbSMogball // Mark as overdefined the predecessors of symbol callables with potentially 135c095afcbSMogball // unknown predecessors. 136c095afcbSMogball initializeSymbolCallables(top); 137c095afcbSMogball 138c095afcbSMogball return initializeRecursively(top); 139c095afcbSMogball } 140c095afcbSMogball 141c095afcbSMogball void DeadCodeAnalysis::initializeSymbolCallables(Operation *top) { 14262fe67f9SJeff Niu analysisScope = top; 143c095afcbSMogball auto walkFn = [&](Operation *symTable, bool allUsesVisible) { 144c095afcbSMogball Region &symbolTableRegion = symTable->getRegion(0); 145c095afcbSMogball Block *symbolTableBlock = &symbolTableRegion.front(); 146c095afcbSMogball 147c095afcbSMogball bool foundSymbolCallable = false; 148c095afcbSMogball for (auto callable : symbolTableBlock->getOps<CallableOpInterface>()) { 149c095afcbSMogball Region *callableRegion = callable.getCallableRegion(); 150c095afcbSMogball if (!callableRegion) 151c095afcbSMogball continue; 152c095afcbSMogball auto symbol = dyn_cast<SymbolOpInterface>(callable.getOperation()); 153c095afcbSMogball if (!symbol) 154c095afcbSMogball continue; 155c095afcbSMogball 156c095afcbSMogball // Public symbol callables or those for which we can't see all uses have 157c095afcbSMogball // potentially unknown callsites. 158c095afcbSMogball if (symbol.isPublic() || (!allUsesVisible && symbol.isNested())) { 1594b3f251bSdonald chen auto *state = 1604b3f251bSdonald chen getOrCreate<PredecessorState>(getProgramPointAfter(callable)); 161c095afcbSMogball propagateIfChanged(state, state->setHasUnknownPredecessors()); 162c095afcbSMogball } 163c095afcbSMogball foundSymbolCallable = true; 164c095afcbSMogball } 165c095afcbSMogball 166c095afcbSMogball // Exit early if no eligible symbol callables were found in the table. 167c095afcbSMogball if (!foundSymbolCallable) 168c095afcbSMogball return; 169c095afcbSMogball 170c095afcbSMogball // Walk the symbol table to check for non-call uses of symbols. 171e8bcc37fSRamkumar Ramachandra std::optional<SymbolTable::UseRange> uses = 172c095afcbSMogball SymbolTable::getSymbolUses(&symbolTableRegion); 173c095afcbSMogball if (!uses) { 174c095afcbSMogball // If we couldn't gather the symbol uses, conservatively assume that 175c095afcbSMogball // we can't track information for any nested symbols. 176c095afcbSMogball return top->walk([&](CallableOpInterface callable) { 1774b3f251bSdonald chen auto *state = 1784b3f251bSdonald chen getOrCreate<PredecessorState>(getProgramPointAfter(callable)); 179c095afcbSMogball propagateIfChanged(state, state->setHasUnknownPredecessors()); 180c095afcbSMogball }); 181c095afcbSMogball } 182c095afcbSMogball 183c095afcbSMogball for (const SymbolTable::SymbolUse &use : *uses) { 184c095afcbSMogball if (isa<CallOpInterface>(use.getUser())) 185c095afcbSMogball continue; 186c095afcbSMogball // If a callable symbol has a non-call use, then we can't be guaranteed to 187c095afcbSMogball // know all callsites. 188c095afcbSMogball Operation *symbol = symbolTable.lookupSymbolIn(top, use.getSymbolRef()); 189*13317502SShlomi Regev if (!symbol) 190*13317502SShlomi Regev continue; 1914b3f251bSdonald chen auto *state = getOrCreate<PredecessorState>(getProgramPointAfter(symbol)); 192c095afcbSMogball propagateIfChanged(state, state->setHasUnknownPredecessors()); 193c095afcbSMogball } 194c095afcbSMogball }; 195c095afcbSMogball SymbolTable::walkSymbolTables(top, /*allSymUsesVisible=*/!top->getBlock(), 196c095afcbSMogball walkFn); 197c095afcbSMogball } 198c095afcbSMogball 199ab701975SMogball /// Returns true if the operation is a returning terminator in region 200ab701975SMogball /// control-flow or the terminator of a callable region. 201ab701975SMogball static bool isRegionOrCallableReturn(Operation *op) { 2024b3f251bSdonald chen return op->getBlock() != nullptr && !op->getNumSuccessors() && 203ab701975SMogball isa<RegionBranchOpInterface, CallableOpInterface>(op->getParentOp()) && 204ab701975SMogball op->getBlock()->getTerminator() == op; 205ab701975SMogball } 206ab701975SMogball 207c095afcbSMogball LogicalResult DeadCodeAnalysis::initializeRecursively(Operation *op) { 208c095afcbSMogball // Initialize the analysis by visiting every op with control-flow semantics. 209c095afcbSMogball if (op->getNumRegions() || op->getNumSuccessors() || 210ab701975SMogball isRegionOrCallableReturn(op) || isa<CallOpInterface>(op)) { 211c095afcbSMogball // When the liveness of the parent block changes, make sure to re-invoke the 212c095afcbSMogball // analysis on the op. 213c095afcbSMogball if (op->getBlock()) 2144b3f251bSdonald chen getOrCreate<Executable>(getProgramPointBefore(op->getBlock())) 2154b3f251bSdonald chen ->blockContentSubscribe(this); 216c095afcbSMogball // Visit the op. 2174b3f251bSdonald chen if (failed(visit(getProgramPointAfter(op)))) 218c095afcbSMogball return failure(); 219c095afcbSMogball } 220c095afcbSMogball // Recurse on nested operations. 221c095afcbSMogball for (Region ®ion : op->getRegions()) 222c095afcbSMogball for (Operation &op : region.getOps()) 223c095afcbSMogball if (failed(initializeRecursively(&op))) 224c095afcbSMogball return failure(); 225c095afcbSMogball return success(); 226c095afcbSMogball } 227c095afcbSMogball 228c095afcbSMogball void DeadCodeAnalysis::markEdgeLive(Block *from, Block *to) { 2294b3f251bSdonald chen auto *state = getOrCreate<Executable>(getProgramPointBefore(to)); 230c095afcbSMogball propagateIfChanged(state, state->setToLive()); 231b6603e1bSdonald chen auto *edgeState = 232b6603e1bSdonald chen getOrCreate<Executable>(getLatticeAnchor<CFGEdge>(from, to)); 233c095afcbSMogball propagateIfChanged(edgeState, edgeState->setToLive()); 234c095afcbSMogball } 235c095afcbSMogball 236c095afcbSMogball void DeadCodeAnalysis::markEntryBlocksLive(Operation *op) { 237c095afcbSMogball for (Region ®ion : op->getRegions()) { 238c095afcbSMogball if (region.empty()) 239c095afcbSMogball continue; 2404b3f251bSdonald chen auto *state = 2414b3f251bSdonald chen getOrCreate<Executable>(getProgramPointBefore(®ion.front())); 242c095afcbSMogball propagateIfChanged(state, state->setToLive()); 243c095afcbSMogball } 244c095afcbSMogball } 245c095afcbSMogball 2464b3f251bSdonald chen LogicalResult DeadCodeAnalysis::visit(ProgramPoint *point) { 2474b3f251bSdonald chen if (point->isBlockStart()) 248c095afcbSMogball return success(); 2494b3f251bSdonald chen Operation *op = point->getPrevOp(); 250c095afcbSMogball 251c095afcbSMogball // If the parent block is not executable, there is nothing to do. 2524b3f251bSdonald chen if (op->getBlock() != nullptr && 2534b3f251bSdonald chen !getOrCreate<Executable>(getProgramPointBefore(op->getBlock()))->isLive()) 254c095afcbSMogball return success(); 255c095afcbSMogball 256c095afcbSMogball // We have a live call op. Add this as a live predecessor of the callee. 257c095afcbSMogball if (auto call = dyn_cast<CallOpInterface>(op)) 258c095afcbSMogball visitCallOperation(call); 259c095afcbSMogball 260c095afcbSMogball // Visit the regions. 261c095afcbSMogball if (op->getNumRegions()) { 262c095afcbSMogball // Check if we can reason about the region control-flow. 263c095afcbSMogball if (auto branch = dyn_cast<RegionBranchOpInterface>(op)) { 264c095afcbSMogball visitRegionBranchOperation(branch); 265c095afcbSMogball 266c095afcbSMogball // Check if this is a callable operation. 267c095afcbSMogball } else if (auto callable = dyn_cast<CallableOpInterface>(op)) { 2684b3f251bSdonald chen const auto *callsites = getOrCreateFor<PredecessorState>( 2694b3f251bSdonald chen getProgramPointAfter(op), getProgramPointAfter(callable)); 270c095afcbSMogball 271c095afcbSMogball // If the callsites could not be resolved or are known to be non-empty, 272c095afcbSMogball // mark the callable as executable. 273c095afcbSMogball if (!callsites->allPredecessorsKnown() || 274c095afcbSMogball !callsites->getKnownPredecessors().empty()) 275c095afcbSMogball markEntryBlocksLive(callable); 276c095afcbSMogball 277c095afcbSMogball // Otherwise, conservatively mark all entry blocks as executable. 278c095afcbSMogball } else { 279c095afcbSMogball markEntryBlocksLive(op); 280c095afcbSMogball } 281c095afcbSMogball } 282c095afcbSMogball 283ab701975SMogball if (isRegionOrCallableReturn(op)) { 284c095afcbSMogball if (auto branch = dyn_cast<RegionBranchOpInterface>(op->getParentOp())) { 285c095afcbSMogball // Visit the exiting terminator of a region. 2866833a380SJustin Fargnoli visitRegionTerminator(op, branch); 287c095afcbSMogball } else if (auto callable = 288c095afcbSMogball dyn_cast<CallableOpInterface>(op->getParentOp())) { 289c095afcbSMogball // Visit the exiting terminator of a callable. 290c095afcbSMogball visitCallableTerminator(op, callable); 291c095afcbSMogball } 292c095afcbSMogball } 293c095afcbSMogball // Visit the successors. 294c095afcbSMogball if (op->getNumSuccessors()) { 295c095afcbSMogball // Check if we can reason about the control-flow. 296c095afcbSMogball if (auto branch = dyn_cast<BranchOpInterface>(op)) { 297c095afcbSMogball visitBranchOperation(branch); 298c095afcbSMogball 299c095afcbSMogball // Otherwise, conservatively mark all successors as exectuable. 300c095afcbSMogball } else { 301c095afcbSMogball for (Block *successor : op->getSuccessors()) 302c095afcbSMogball markEdgeLive(op->getBlock(), successor); 303c095afcbSMogball } 304c095afcbSMogball } 305c095afcbSMogball 306c095afcbSMogball return success(); 307c095afcbSMogball } 308c095afcbSMogball 309c095afcbSMogball void DeadCodeAnalysis::visitCallOperation(CallOpInterface call) { 310d1cad229SHenrich Lauko Operation *callableOp = call.resolveCallableInTable(&symbolTable); 311c095afcbSMogball 312c095afcbSMogball // A call to a externally-defined callable has unknown predecessors. 31362fe67f9SJeff Niu const auto isExternalCallable = [this](Operation *op) { 31462fe67f9SJeff Niu // A callable outside the analysis scope is an external callable. 31562fe67f9SJeff Niu if (!analysisScope->isAncestor(op)) 31662fe67f9SJeff Niu return true; 31762fe67f9SJeff Niu // Otherwise, check if the callable region is defined. 318c095afcbSMogball if (auto callable = dyn_cast<CallableOpInterface>(op)) 319c095afcbSMogball return !callable.getCallableRegion(); 320c095afcbSMogball return false; 321c095afcbSMogball }; 322c095afcbSMogball 323c095afcbSMogball // TODO: Add support for non-symbol callables when necessary. If the 324c095afcbSMogball // callable has non-call uses we would mark as having reached pessimistic 325c095afcbSMogball // fixpoint, otherwise allow for propagating the return values out. 326c095afcbSMogball if (isa_and_nonnull<SymbolOpInterface>(callableOp) && 327c095afcbSMogball !isExternalCallable(callableOp)) { 328c095afcbSMogball // Add the live callsite. 3294b3f251bSdonald chen auto *callsites = 3304b3f251bSdonald chen getOrCreate<PredecessorState>(getProgramPointAfter(callableOp)); 331c095afcbSMogball propagateIfChanged(callsites, callsites->join(call)); 332c095afcbSMogball } else { 333c095afcbSMogball // Mark this call op's predecessors as overdefined. 3344b3f251bSdonald chen auto *predecessors = 3354b3f251bSdonald chen getOrCreate<PredecessorState>(getProgramPointAfter(call)); 336c095afcbSMogball propagateIfChanged(predecessors, predecessors->setHasUnknownPredecessors()); 337c095afcbSMogball } 338c095afcbSMogball } 339c095afcbSMogball 340c095afcbSMogball /// Get the constant values of the operands of an operation. If any of the 341f09b0e35SKazu Hirata /// constant value lattices are uninitialized, return std::nullopt to indicate 342f09b0e35SKazu Hirata /// the analysis should bail out. 3430a81ace0SKazu Hirata static std::optional<SmallVector<Attribute>> getOperandValuesImpl( 344c095afcbSMogball Operation *op, 345c095afcbSMogball function_ref<const Lattice<ConstantValue> *(Value)> getLattice) { 346c095afcbSMogball SmallVector<Attribute> operands; 347c095afcbSMogball operands.reserve(op->getNumOperands()); 348c095afcbSMogball for (Value operand : op->getOperands()) { 349c095afcbSMogball const Lattice<ConstantValue> *cv = getLattice(operand); 350c095afcbSMogball // If any of the operands' values are uninitialized, bail out. 35147bf3e38SZhixun Tan if (cv->getValue().isUninitialized()) 352c095afcbSMogball return {}; 353c095afcbSMogball operands.push_back(cv->getValue().getConstantValue()); 354c095afcbSMogball } 355c095afcbSMogball return operands; 356c095afcbSMogball } 357c095afcbSMogball 3580a81ace0SKazu Hirata std::optional<SmallVector<Attribute>> 359c095afcbSMogball DeadCodeAnalysis::getOperandValues(Operation *op) { 360c095afcbSMogball return getOperandValuesImpl(op, [&](Value value) { 361c095afcbSMogball auto *lattice = getOrCreate<Lattice<ConstantValue>>(value); 362c095afcbSMogball lattice->useDefSubscribe(this); 363c095afcbSMogball return lattice; 364c095afcbSMogball }); 365c095afcbSMogball } 366c095afcbSMogball 367c095afcbSMogball void DeadCodeAnalysis::visitBranchOperation(BranchOpInterface branch) { 368c095afcbSMogball // Try to deduce a single successor for the branch. 3690a81ace0SKazu Hirata std::optional<SmallVector<Attribute>> operands = getOperandValues(branch); 370c095afcbSMogball if (!operands) 371c095afcbSMogball return; 372c095afcbSMogball 373c095afcbSMogball if (Block *successor = branch.getSuccessorForOperands(*operands)) { 374c095afcbSMogball markEdgeLive(branch->getBlock(), successor); 375c095afcbSMogball } else { 376c095afcbSMogball // Otherwise, mark all successors as executable and outgoing edges. 377c095afcbSMogball for (Block *successor : branch->getSuccessors()) 378c095afcbSMogball markEdgeLive(branch->getBlock(), successor); 379c095afcbSMogball } 380c095afcbSMogball } 381c095afcbSMogball 382c095afcbSMogball void DeadCodeAnalysis::visitRegionBranchOperation( 383c095afcbSMogball RegionBranchOpInterface branch) { 384c095afcbSMogball // Try to deduce which regions are executable. 3850a81ace0SKazu Hirata std::optional<SmallVector<Attribute>> operands = getOperandValues(branch); 386c095afcbSMogball if (!operands) 387c095afcbSMogball return; 388c095afcbSMogball 389c095afcbSMogball SmallVector<RegionSuccessor> successors; 390138df298SMarkus Böck branch.getEntrySuccessorRegions(*operands, successors); 391c095afcbSMogball for (const RegionSuccessor &successor : successors) { 3929432fbfeSMogball // The successor can be either an entry block or the parent operation. 3934b3f251bSdonald chen ProgramPoint *point = 3944b3f251bSdonald chen successor.getSuccessor() 3954b3f251bSdonald chen ? getProgramPointBefore(&successor.getSuccessor()->front()) 3964b3f251bSdonald chen : getProgramPointAfter(branch); 397c095afcbSMogball // Mark the entry block as executable. 3989432fbfeSMogball auto *state = getOrCreate<Executable>(point); 399c095afcbSMogball propagateIfChanged(state, state->setToLive()); 400c095afcbSMogball // Add the parent op as a predecessor. 4019432fbfeSMogball auto *predecessors = getOrCreate<PredecessorState>(point); 4029432fbfeSMogball propagateIfChanged( 4039432fbfeSMogball predecessors, 4049432fbfeSMogball predecessors->join(branch, successor.getSuccessorInputs())); 405c095afcbSMogball } 406c095afcbSMogball } 407c095afcbSMogball 4086833a380SJustin Fargnoli void DeadCodeAnalysis::visitRegionTerminator(Operation *op, 4096833a380SJustin Fargnoli RegionBranchOpInterface branch) { 4100a81ace0SKazu Hirata std::optional<SmallVector<Attribute>> operands = getOperandValues(op); 411c095afcbSMogball if (!operands) 412c095afcbSMogball return; 413c095afcbSMogball 414c095afcbSMogball SmallVector<RegionSuccessor> successors; 4156833a380SJustin Fargnoli if (auto terminator = dyn_cast<RegionBranchTerminatorOpInterface>(op)) 4166833a380SJustin Fargnoli terminator.getSuccessorRegions(*operands, successors); 4176833a380SJustin Fargnoli else 4186833a380SJustin Fargnoli branch.getSuccessorRegions(op->getParentRegion(), successors); 419c095afcbSMogball 420c095afcbSMogball // Mark successor region entry blocks as executable and add this op to the 421c095afcbSMogball // list of predecessors. 422c095afcbSMogball for (const RegionSuccessor &successor : successors) { 423c095afcbSMogball PredecessorState *predecessors; 424c095afcbSMogball if (Region *region = successor.getSuccessor()) { 4254b3f251bSdonald chen auto *state = 4264b3f251bSdonald chen getOrCreate<Executable>(getProgramPointBefore(®ion->front())); 427c095afcbSMogball propagateIfChanged(state, state->setToLive()); 4284b3f251bSdonald chen predecessors = getOrCreate<PredecessorState>( 4294b3f251bSdonald chen getProgramPointBefore(®ion->front())); 430c095afcbSMogball } else { 431c095afcbSMogball // Add this terminator as a predecessor to the parent op. 4324b3f251bSdonald chen predecessors = 4334b3f251bSdonald chen getOrCreate<PredecessorState>(getProgramPointAfter(branch)); 434c095afcbSMogball } 4359432fbfeSMogball propagateIfChanged(predecessors, 4369432fbfeSMogball predecessors->join(op, successor.getSuccessorInputs())); 437c095afcbSMogball } 438c095afcbSMogball } 439c095afcbSMogball 440c095afcbSMogball void DeadCodeAnalysis::visitCallableTerminator(Operation *op, 441c095afcbSMogball CallableOpInterface callable) { 442c095afcbSMogball // Add as predecessors to all callsites this return op. 4434b3f251bSdonald chen auto *callsites = getOrCreateFor<PredecessorState>( 4444b3f251bSdonald chen getProgramPointAfter(op), getProgramPointAfter(callable)); 445c095afcbSMogball bool canResolve = op->hasTrait<OpTrait::ReturnLike>(); 446c095afcbSMogball for (Operation *predecessor : callsites->getKnownPredecessors()) { 447c095afcbSMogball assert(isa<CallOpInterface>(predecessor)); 4484b3f251bSdonald chen auto *predecessors = 4494b3f251bSdonald chen getOrCreate<PredecessorState>(getProgramPointAfter(predecessor)); 450c095afcbSMogball if (canResolve) { 451c095afcbSMogball propagateIfChanged(predecessors, predecessors->join(op)); 452c095afcbSMogball } else { 453c095afcbSMogball // If the terminator is not a return-like, then conservatively assume we 454c095afcbSMogball // can't resolve the predecessor. 455c095afcbSMogball propagateIfChanged(predecessors, 456c095afcbSMogball predecessors->setHasUnknownPredecessors()); 457c095afcbSMogball } 458c095afcbSMogball } 459c095afcbSMogball } 460