//===- GenericCycleImpl.h -------------------------------------*- C++ -*---===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This template implementation resides in a separate file so that it /// does not get injected into every .cpp file that includes the /// generic header. /// /// DO NOT INCLUDE THIS FILE WHEN MERELY USING CYCLEINFO. /// /// This file should only be included by files that implement a /// specialization of the relevant templates. Currently these are: /// - llvm/lib/IR/CycleInfo.cpp /// - llvm/lib/CodeGen/MachineCycleAnalysis.cpp /// //===----------------------------------------------------------------------===// #ifndef LLVM_ADT_GENERICCYCLEIMPL_H #define LLVM_ADT_GENERICCYCLEIMPL_H #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DepthFirstIterator.h" #include "llvm/ADT/GenericCycleInfo.h" #define DEBUG_TYPE "generic-cycle-impl" namespace llvm { template bool GenericCycle::contains(const GenericCycle *C) const { if (!C) return false; if (Depth > C->Depth) return false; while (Depth < C->Depth) C = C->ParentCycle; return this == C; } template void GenericCycle::getExitBlocks( SmallVectorImpl &TmpStorage) const { TmpStorage.clear(); size_t NumExitBlocks = 0; for (BlockT *Block : blocks()) { llvm::append_range(TmpStorage, successors(Block)); for (size_t Idx = NumExitBlocks, End = TmpStorage.size(); Idx < End; ++Idx) { BlockT *Succ = TmpStorage[Idx]; if (!contains(Succ)) { auto ExitEndIt = TmpStorage.begin() + NumExitBlocks; if (std::find(TmpStorage.begin(), ExitEndIt, Succ) == ExitEndIt) TmpStorage[NumExitBlocks++] = Succ; } } TmpStorage.resize(NumExitBlocks); } } template auto GenericCycle::getCyclePreheader() const -> BlockT * { BlockT *Predecessor = getCyclePredecessor(); if (!Predecessor) return nullptr; assert(isReducible() && "Cycle Predecessor must be in a reducible cycle!"); if (succ_size(Predecessor) != 1) return nullptr; // Make sure we are allowed to hoist instructions into the predecessor. if (!Predecessor->isLegalToHoistInto()) return nullptr; return Predecessor; } template auto GenericCycle::getCyclePredecessor() const -> BlockT * { if (!isReducible()) return nullptr; BlockT *Out = nullptr; // Loop over the predecessors of the header node... BlockT *Header = getHeader(); for (const auto Pred : predecessors(Header)) { if (!contains(Pred)) { if (Out && Out != Pred) return nullptr; Out = Pred; } } return Out; } /// \brief Helper class for computing cycle information. template class GenericCycleInfoCompute { using BlockT = typename ContextT::BlockT; using CycleInfoT = GenericCycleInfo; using CycleT = typename CycleInfoT::CycleT; CycleInfoT &Info; struct DFSInfo { unsigned Start = 0; // DFS start; positive if block is found unsigned End = 0; // DFS end DFSInfo() = default; explicit DFSInfo(unsigned Start) : Start(Start) {} /// Whether this node is an ancestor (or equal to) the node \p Other /// in the DFS tree. bool isAncestorOf(const DFSInfo &Other) const { return Start <= Other.Start && Other.End <= End; } }; DenseMap BlockDFSInfo; SmallVector BlockPreorder; GenericCycleInfoCompute(const GenericCycleInfoCompute &) = delete; GenericCycleInfoCompute &operator=(const GenericCycleInfoCompute &) = delete; public: GenericCycleInfoCompute(CycleInfoT &Info) : Info(Info) {} void run(BlockT *EntryBlock); static void updateDepth(CycleT *SubTree); private: void dfs(BlockT *EntryBlock); }; template auto GenericCycleInfo::getTopLevelParentCycle(BlockT *Block) -> CycleT * { auto Cycle = BlockMapTopLevel.find(Block); if (Cycle != BlockMapTopLevel.end()) return Cycle->second; auto MapIt = BlockMap.find(Block); if (MapIt == BlockMap.end()) return nullptr; auto *C = MapIt->second; while (C->ParentCycle) C = C->ParentCycle; BlockMapTopLevel.try_emplace(Block, C); return C; } template void GenericCycleInfo::moveTopLevelCycleToNewParent(CycleT *NewParent, CycleT *Child) { assert((!Child->ParentCycle && !NewParent->ParentCycle) && "NewParent and Child must be both top level cycle!\n"); auto &CurrentContainer = Child->ParentCycle ? Child->ParentCycle->Children : TopLevelCycles; auto Pos = llvm::find_if(CurrentContainer, [=](const auto &Ptr) -> bool { return Child == Ptr.get(); }); assert(Pos != CurrentContainer.end()); NewParent->Children.push_back(std::move(*Pos)); *Pos = std::move(CurrentContainer.back()); CurrentContainer.pop_back(); Child->ParentCycle = NewParent; NewParent->Blocks.insert(Child->block_begin(), Child->block_end()); for (auto &It : BlockMapTopLevel) if (It.second == Child) It.second = NewParent; } template void GenericCycleInfo::addBlockToCycle(BlockT *Block, CycleT *Cycle) { // FixMe: Appending NewBlock is fine as a set of blocks in a cycle. When // printing, cycle NewBlock is at the end of list but it should be in the // middle to represent actual traversal of a cycle. Cycle->appendBlock(Block); BlockMap.try_emplace(Block, Cycle); CycleT *ParentCycle = Cycle->getParentCycle(); while (ParentCycle) { Cycle = ParentCycle; Cycle->appendBlock(Block); ParentCycle = Cycle->getParentCycle(); } BlockMapTopLevel.try_emplace(Block, Cycle); } /// \brief Main function of the cycle info computations. template void GenericCycleInfoCompute::run(BlockT *EntryBlock) { LLVM_DEBUG(errs() << "Entry block: " << Info.Context.print(EntryBlock) << "\n"); dfs(EntryBlock); SmallVector Worklist; for (BlockT *HeaderCandidate : llvm::reverse(BlockPreorder)) { const DFSInfo CandidateInfo = BlockDFSInfo.lookup(HeaderCandidate); for (BlockT *Pred : predecessors(HeaderCandidate)) { const DFSInfo PredDFSInfo = BlockDFSInfo.lookup(Pred); if (CandidateInfo.isAncestorOf(PredDFSInfo)) Worklist.push_back(Pred); } if (Worklist.empty()) { continue; } // Found a cycle with the candidate as its header. LLVM_DEBUG(errs() << "Found cycle for header: " << Info.Context.print(HeaderCandidate) << "\n"); std::unique_ptr NewCycle = std::make_unique(); NewCycle->appendEntry(HeaderCandidate); NewCycle->appendBlock(HeaderCandidate); Info.BlockMap.try_emplace(HeaderCandidate, NewCycle.get()); // Helper function to process (non-back-edge) predecessors of a discovered // block and either add them to the worklist or recognize that the given // block is an additional cycle entry. auto ProcessPredecessors = [&](BlockT *Block) { LLVM_DEBUG(errs() << " block " << Info.Context.print(Block) << ": "); bool IsEntry = false; for (BlockT *Pred : predecessors(Block)) { const DFSInfo PredDFSInfo = BlockDFSInfo.lookup(Pred); if (CandidateInfo.isAncestorOf(PredDFSInfo)) { Worklist.push_back(Pred); } else { IsEntry = true; } } if (IsEntry) { assert(!NewCycle->isEntry(Block)); LLVM_DEBUG(errs() << "append as entry\n"); NewCycle->appendEntry(Block); } else { LLVM_DEBUG(errs() << "append as child\n"); } }; do { BlockT *Block = Worklist.pop_back_val(); if (Block == HeaderCandidate) continue; // If the block has already been discovered by some cycle // (possibly by ourself), then the outermost cycle containing it // should become our child. if (auto *BlockParent = Info.getTopLevelParentCycle(Block)) { LLVM_DEBUG(errs() << " block " << Info.Context.print(Block) << ": "); if (BlockParent != NewCycle.get()) { LLVM_DEBUG(errs() << "discovered child cycle " << Info.Context.print(BlockParent->getHeader()) << "\n"); // Make BlockParent the child of NewCycle. Info.moveTopLevelCycleToNewParent(NewCycle.get(), BlockParent); for (auto *ChildEntry : BlockParent->entries()) ProcessPredecessors(ChildEntry); } else { LLVM_DEBUG(errs() << "known child cycle " << Info.Context.print(BlockParent->getHeader()) << "\n"); } } else { Info.BlockMap.try_emplace(Block, NewCycle.get()); assert(!is_contained(NewCycle->Blocks, Block)); NewCycle->Blocks.insert(Block); ProcessPredecessors(Block); Info.BlockMapTopLevel.try_emplace(Block, NewCycle.get()); } } while (!Worklist.empty()); Info.TopLevelCycles.push_back(std::move(NewCycle)); } // Fix top-level cycle links and compute cycle depths. for (auto *TLC : Info.toplevel_cycles()) { LLVM_DEBUG(errs() << "top-level cycle: " << Info.Context.print(TLC->getHeader()) << "\n"); TLC->ParentCycle = nullptr; updateDepth(TLC); } } /// \brief Recompute depth values of \p SubTree and all descendants. template void GenericCycleInfoCompute::updateDepth(CycleT *SubTree) { for (CycleT *Cycle : depth_first(SubTree)) Cycle->Depth = Cycle->ParentCycle ? Cycle->ParentCycle->Depth + 1 : 1; } /// \brief Compute a DFS of basic blocks starting at the function entry. /// /// Fills BlockDFSInfo with start/end counters and BlockPreorder. template void GenericCycleInfoCompute::dfs(BlockT *EntryBlock) { SmallVector DFSTreeStack; SmallVector TraverseStack; unsigned Counter = 0; TraverseStack.emplace_back(EntryBlock); do { BlockT *Block = TraverseStack.back(); LLVM_DEBUG(errs() << "DFS visiting block: " << Info.Context.print(Block) << "\n"); if (!BlockDFSInfo.count(Block)) { // We're visiting the block for the first time. Open its DFSInfo, add // successors to the traversal stack, and remember the traversal stack // depth at which the block was opened, so that we can correctly record // its end time. LLVM_DEBUG(errs() << " first encountered at depth " << TraverseStack.size() << "\n"); DFSTreeStack.emplace_back(TraverseStack.size()); llvm::append_range(TraverseStack, successors(Block)); bool Added = BlockDFSInfo.try_emplace(Block, ++Counter).second; (void)Added; assert(Added); BlockPreorder.push_back(Block); LLVM_DEBUG(errs() << " preorder number: " << Counter << "\n"); } else { assert(!DFSTreeStack.empty()); if (DFSTreeStack.back() == TraverseStack.size()) { LLVM_DEBUG(errs() << " ended at " << Counter << "\n"); BlockDFSInfo.find(Block)->second.End = Counter; DFSTreeStack.pop_back(); } else { LLVM_DEBUG(errs() << " already done\n"); } TraverseStack.pop_back(); } } while (!TraverseStack.empty()); assert(DFSTreeStack.empty()); LLVM_DEBUG( errs() << "Preorder:\n"; for (int i = 0, e = BlockPreorder.size(); i != e; ++i) { errs() << " " << Info.Context.print(BlockPreorder[i]) << ": " << i << "\n"; } ); } /// \brief Reset the object to its initial state. template void GenericCycleInfo::clear() { TopLevelCycles.clear(); BlockMap.clear(); BlockMapTopLevel.clear(); } /// \brief Compute the cycle info for a function. template void GenericCycleInfo::compute(FunctionT &F) { GenericCycleInfoCompute Compute(*this); Context = ContextT(&F); LLVM_DEBUG(errs() << "Computing cycles for function: " << F.getName() << "\n"); Compute.run(&F.front()); assert(validateTree()); } template void GenericCycleInfo::splitCriticalEdge(BlockT *Pred, BlockT *Succ, BlockT *NewBlock) { // Edge Pred-Succ is replaced by edges Pred-NewBlock and NewBlock-Succ, all // cycles that had blocks Pred and Succ also get NewBlock. CycleT *Cycle = getSmallestCommonCycle(getCycle(Pred), getCycle(Succ)); if (!Cycle) return; addBlockToCycle(NewBlock, Cycle); assert(validateTree()); } /// \brief Find the innermost cycle containing a given block. /// /// \returns the innermost cycle containing \p Block or nullptr if /// it is not contained in any cycle. template auto GenericCycleInfo::getCycle(const BlockT *Block) const -> CycleT * { return BlockMap.lookup(Block); } /// \brief Find the innermost cycle containing both given cycles. /// /// \returns the innermost cycle containing both \p A and \p B /// or nullptr if there is no such cycle. template auto GenericCycleInfo::getSmallestCommonCycle(CycleT *A, CycleT *B) const -> CycleT * { if (!A || !B) return nullptr; // If cycles A and B have different depth replace them with parent cycle // until they have the same depth. while (A->getDepth() > B->getDepth()) A = A->getParentCycle(); while (B->getDepth() > A->getDepth()) B = B->getParentCycle(); // Cycles A and B are at same depth but may be disjoint, replace them with // parent cycles until we find cycle that contains both or we run out of // parent cycles. while (A != B) { A = A->getParentCycle(); B = B->getParentCycle(); } return A; } /// \brief get the depth for the cycle which containing a given block. /// /// \returns the depth for the innermost cycle containing \p Block or 0 if it is /// not contained in any cycle. template unsigned GenericCycleInfo::getCycleDepth(const BlockT *Block) const { CycleT *Cycle = getCycle(Block); if (!Cycle) return 0; return Cycle->getDepth(); } #ifndef NDEBUG /// \brief Validate the internal consistency of the cycle tree. /// /// Note that this does \em not check that cycles are really cycles in the CFG, /// or that the right set of cycles in the CFG were found. template bool GenericCycleInfo::validateTree() const { DenseSet Blocks; DenseSet Entries; auto reportError = [](const char *File, int Line, const char *Cond) { errs() << File << ':' << Line << ": GenericCycleInfo::validateTree: " << Cond << '\n'; }; #define check(cond) \ do { \ if (!(cond)) { \ reportError(__FILE__, __LINE__, #cond); \ return false; \ } \ } while (false) for (const auto *TLC : toplevel_cycles()) { for (const CycleT *Cycle : depth_first(TLC)) { if (Cycle->ParentCycle) check(is_contained(Cycle->ParentCycle->children(), Cycle)); for (BlockT *Block : Cycle->Blocks) { auto MapIt = BlockMap.find(Block); check(MapIt != BlockMap.end()); check(Cycle->contains(MapIt->second)); check(Blocks.insert(Block).second); // duplicates in block list? } Blocks.clear(); check(!Cycle->Entries.empty()); for (BlockT *Entry : Cycle->Entries) { check(Entries.insert(Entry).second); // duplicate entry? check(is_contained(Cycle->Blocks, Entry)); } Entries.clear(); unsigned ChildDepth = 0; for (const CycleT *Child : Cycle->children()) { check(Child->Depth > Cycle->Depth); if (!ChildDepth) { ChildDepth = Child->Depth; } else { check(ChildDepth == Child->Depth); } } } } for (const auto &Entry : BlockMap) { BlockT *Block = Entry.first; for (const CycleT *Cycle = Entry.second; Cycle; Cycle = Cycle->ParentCycle) { check(is_contained(Cycle->Blocks, Block)); } } #undef check return true; } #endif /// \brief Print the cycle info. template void GenericCycleInfo::print(raw_ostream &Out) const { for (const auto *TLC : toplevel_cycles()) { for (const CycleT *Cycle : depth_first(TLC)) { for (unsigned I = 0; I < Cycle->Depth; ++I) Out << " "; Out << Cycle->print(Context) << '\n'; } } } } // namespace llvm #undef DEBUG_TYPE #endif // LLVM_ADT_GENERICCYCLEIMPL_H