//===--- SCCVisitor.h - SIL SCC Visitor -------------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #ifndef SWIFT_SIL_SCCVISITOR_H #define SWIFT_SIL_SCCVISITOR_H #include "swift/SILOptimizer/Analysis/Analysis.h" #include "swift/SIL/CFG.h" #include "swift/SIL/SILArgument.h" #include "swift/SIL/SILBasicBlock.h" #include "swift/SIL/SILFunction.h" #include "swift/SIL/SILInstruction.h" #include "swift/SIL/SILValue.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" #include #include namespace swift { /// A visitor class for visiting the instructions and basic block /// arguments of a SIL function one strongly connected component at a /// time in reverse post-order. /// /// Inherit from this in the usual CRTP fashion and define a visit() /// function. This function will get called with a vector of /// pointer-to-ValueBase which are the elements of the SCC. template class SCCVisitor { public: SCCVisitor(SILFunction &F) : F(F) {} ~SCCVisitor() { cleanup(); } ImplClass &asImpl() { return static_cast(*this); } void visit(llvm::SmallVectorImpl &SCC) { } void run() { llvm::ReversePostOrderTraversal RPOT(&F); for (auto Iter = RPOT.begin(), E = RPOT.end(); Iter != E; ++Iter) { auto *BB = *Iter; for (auto &I : *BB) maybeDFS(&I); } cleanup(); } private: struct DFSInfo { SILNode *Node; int DFSNum; int LowNum; DFSInfo(SILNode *node, int num) : Node(node), DFSNum(num), LowNum(num) {} }; SILFunction &F; int CurrentNum = 0; llvm::DenseSet Visited; llvm::SetVector DFSStack; typedef llvm::DenseMap> ValueInfoMapType; ValueInfoMapType ValueInfoMap; void cleanup() { Visited.clear(); DFSStack.clear(); ValueInfoMap.clear(); CurrentNum = 0; } DFSInfo &addDFSInfo(SILNode *node) { auto insertion = ValueInfoMap.try_emplace(node, new DFSInfo(node, CurrentNum++)); assert(insertion.second && "Cannot add DFS info more than once!"); return *insertion.first->second; } DFSInfo &getDFSInfo(SILNode *node) { auto it = ValueInfoMap.find(node); assert(it != ValueInfoMap.end() && "Expected to find value in DFS info map!"); return *it->second; } void getArgsForTerminator(TermInst *Term, SILBasicBlock *SuccBB, int Index, llvm::SmallVectorImpl &Operands) { switch (Term->getTermKind()) { case TermKind::BranchInst: return Operands.push_back(cast(Term)->getArg(Index)); case TermKind::CondBranchInst: { auto *CBI = cast(Term); if (SuccBB == CBI->getTrueBB()) return Operands.push_back(CBI->getTrueArgs()[Index]); assert(SuccBB == CBI->getFalseBB() && "Block is not a successor of terminator!"); Operands.push_back(CBI->getFalseArgs()[Index]); return; } case TermKind::AwaitAsyncContinuationInst: { auto *AACI = cast(Term); Operands.push_back(AACI->getOperand()); return; } case TermKind::SwitchEnumInst: case TermKind::SwitchEnumAddrInst: case TermKind::CheckedCastBranchInst: case TermKind::CheckedCastAddrBranchInst: case TermKind::DynamicMethodBranchInst: assert(Index == 0 && "Expected argument index to always be zero!"); return Operands.push_back(Term->getOperand(0)); case TermKind::UnreachableInst: case TermKind::ReturnInst: case TermKind::SwitchValueInst: case TermKind::ThrowInst: case TermKind::ThrowAddrInst: case TermKind::UnwindInst: llvm_unreachable("Did not expect terminator that does not have args!"); case TermKind::YieldInst: for (auto &O : cast(Term)->getAllOperands()) Operands.push_back(O.get()); return; case TermKind::TryApplyInst: for (auto &O : cast(Term)->getAllOperands()) Operands.push_back(O.get()); return; } } void collectOperandsForUser(SILNode *node, llvm::SmallVectorImpl &Operands) { if (auto *I = dyn_cast(node)) { for (auto &O : I->getAllOperands()) Operands.push_back(O.get()); return; } if (auto *A = dyn_cast(node)) { auto *BB = A->getParent(); auto Index = A->getIndex(); for (auto *Pred : BB->getPredecessorBlocks()) getArgsForTerminator(Pred->getTerminator(), BB, Index, Operands); return; } } void maybeDFS(SILInstruction *inst) { (void) maybeDFSCanonicalNode(inst->asSILNode()); } /// Continue a DFS from the given node, finding the strongly /// component that User is a part of, calling visit() with that SCC, /// and returning the DFSInfo for the node. /// But if we've already visited the node, just return null. DFSInfo *maybeDFSCanonicalNode(SILNode *node) { if (!Visited.insert(node).second) return nullptr; DFSStack.insert(node); auto &nodeInfo = addDFSInfo(node); llvm::SmallVector operands; collectOperandsForUser(node, operands); // Visit each unvisited operand, updating the lowest DFS number we've seen // reachable in User's SCC. for (SILValue operandValue : operands) { SILNode *operandNode = operandValue; if (auto operandNodeInfo = maybeDFSCanonicalNode(operandNode)) { nodeInfo.LowNum = std::min(nodeInfo.LowNum, operandNodeInfo->LowNum); } else if (DFSStack.count(operandNode)) { auto operandNodeInfo = &getDFSInfo(operandNode); nodeInfo.LowNum = std::min(nodeInfo.LowNum, operandNodeInfo->DFSNum); } } // If User is the head of its own SCC, pop that SCC off the DFS stack. if (nodeInfo.DFSNum == nodeInfo.LowNum) { llvm::SmallVector SCC; SILNode *poppedNode; do { poppedNode = DFSStack.pop_back_val(); SCC.push_back(poppedNode); } while (poppedNode != node); asImpl().visit(SCC); } return &nodeInfo; } }; } #endif