#pragma once #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif //===- RegionInfoImpl.h - SESE region detection analysis --------*- 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 // //===----------------------------------------------------------------------===// // Detects single entry single exit regions in the control flow graph. //===----------------------------------------------------------------------===// #ifndef LLVM_ANALYSIS_REGIONINFOIMPL_H #define LLVM_ANALYSIS_REGIONINFOIMPL_H #include "llvm/ADT/GraphTraits.h" #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Analysis/DominanceFrontier.h" #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/PostDominators.h" #include "llvm/Analysis/RegionInfo.h" #include "llvm/Analysis/RegionIterator.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include #include #include #define DEBUG_TYPE "region" namespace llvm { //===----------------------------------------------------------------------===// /// RegionBase Implementation template RegionBase::RegionBase(BlockT *Entry, BlockT *Exit, typename Tr::RegionInfoT *RInfo, DomTreeT *dt, RegionT *Parent) : RegionNodeBase(Parent, Entry, 1), RI(RInfo), DT(dt), exit(Exit) {} template RegionBase::~RegionBase() { // Only clean the cache for this Region. Caches of child Regions will be // cleaned when the child Regions are deleted. BBNodeMap.clear(); } template void RegionBase::replaceEntry(BlockT *BB) { this->entry.setPointer(BB); } template void RegionBase::replaceExit(BlockT *BB) { assert(exit && "No exit to replace!"); exit = BB; } template void RegionBase::replaceEntryRecursive(BlockT *NewEntry) { std::vector RegionQueue; BlockT *OldEntry = getEntry(); RegionQueue.push_back(static_cast(this)); while (!RegionQueue.empty()) { RegionT *R = RegionQueue.back(); RegionQueue.pop_back(); R->replaceEntry(NewEntry); for (std::unique_ptr &Child : *R) { if (Child->getEntry() == OldEntry) RegionQueue.push_back(Child.get()); } } } template void RegionBase::replaceExitRecursive(BlockT *NewExit) { std::vector RegionQueue; BlockT *OldExit = getExit(); RegionQueue.push_back(static_cast(this)); while (!RegionQueue.empty()) { RegionT *R = RegionQueue.back(); RegionQueue.pop_back(); R->replaceExit(NewExit); for (std::unique_ptr &Child : *R) { if (Child->getExit() == OldExit) RegionQueue.push_back(Child.get()); } } } template bool RegionBase::contains(const BlockT *B) const { BlockT *BB = const_cast(B); if (!DT->getNode(BB)) return false; BlockT *entry = getEntry(), *exit = getExit(); // Toplevel region. if (!exit) return true; return (DT->dominates(entry, BB) && !(DT->dominates(exit, BB) && DT->dominates(entry, exit))); } template bool RegionBase::contains(const LoopT *L) const { // BBs that are not part of any loop are element of the Loop // described by the NULL pointer. This loop is not part of any region, // except if the region describes the whole function. if (!L) return getExit() == nullptr; if (!contains(L->getHeader())) return false; SmallVector ExitingBlocks; L->getExitingBlocks(ExitingBlocks); for (BlockT *BB : ExitingBlocks) { if (!contains(BB)) return false; } return true; } template typename Tr::LoopT *RegionBase::outermostLoopInRegion(LoopT *L) const { if (!contains(L)) return nullptr; while (L && contains(L->getParentLoop())) { L = L->getParentLoop(); } return L; } template typename Tr::LoopT *RegionBase::outermostLoopInRegion(LoopInfoT *LI, BlockT *BB) const { assert(LI && BB && "LI and BB cannot be null!"); LoopT *L = LI->getLoopFor(BB); return outermostLoopInRegion(L); } template typename RegionBase::BlockT *RegionBase::getEnteringBlock() const { BlockT *entry = getEntry(); BlockT *enteringBlock = nullptr; for (BlockT *Pred : make_range(InvBlockTraits::child_begin(entry), InvBlockTraits::child_end(entry))) { if (DT->getNode(Pred) && !contains(Pred)) { if (enteringBlock) return nullptr; enteringBlock = Pred; } } return enteringBlock; } template bool RegionBase::getExitingBlocks( SmallVectorImpl &Exitings) const { bool CoverAll = true; if (!exit) return CoverAll; for (PredIterTy PI = InvBlockTraits::child_begin(exit), PE = InvBlockTraits::child_end(exit); PI != PE; ++PI) { BlockT *Pred = *PI; if (contains(Pred)) { Exitings.push_back(Pred); continue; } CoverAll = false; } return CoverAll; } template typename RegionBase::BlockT *RegionBase::getExitingBlock() const { BlockT *exit = getExit(); BlockT *exitingBlock = nullptr; if (!exit) return nullptr; for (BlockT *Pred : make_range(InvBlockTraits::child_begin(exit), InvBlockTraits::child_end(exit))) { if (contains(Pred)) { if (exitingBlock) return nullptr; exitingBlock = Pred; } } return exitingBlock; } template bool RegionBase::isSimple() const { return !isTopLevelRegion() && getEnteringBlock() && getExitingBlock(); } template std::string RegionBase::getNameStr() const { std::string exitName; std::string entryName; if (getEntry()->getName().empty()) { raw_string_ostream OS(entryName); getEntry()->printAsOperand(OS, false); } else entryName = std::string(getEntry()->getName()); if (getExit()) { if (getExit()->getName().empty()) { raw_string_ostream OS(exitName); getExit()->printAsOperand(OS, false); } else exitName = std::string(getExit()->getName()); } else exitName = ""; return entryName + " => " + exitName; } template void RegionBase::verifyBBInRegion(BlockT *BB) const { if (!contains(BB)) report_fatal_error("Broken region found: enumerated BB not in region!"); BlockT *entry = getEntry(), *exit = getExit(); for (BlockT *Succ : make_range(BlockTraits::child_begin(BB), BlockTraits::child_end(BB))) { if (!contains(Succ) && exit != Succ) report_fatal_error("Broken region found: edges leaving the region must go " "to the exit node!"); } if (entry != BB) { for (BlockT *Pred : make_range(InvBlockTraits::child_begin(BB), InvBlockTraits::child_end(BB))) { if (!contains(Pred)) report_fatal_error("Broken region found: edges entering the region must " "go to the entry node!"); } } } template void RegionBase::verifyWalk(BlockT *BB, std::set *visited) const { BlockT *exit = getExit(); visited->insert(BB); verifyBBInRegion(BB); for (BlockT *Succ : make_range(BlockTraits::child_begin(BB), BlockTraits::child_end(BB))) { if (Succ != exit && visited->find(Succ) == visited->end()) verifyWalk(Succ, visited); } } template void RegionBase::verifyRegion() const { // Only do verification when user wants to, otherwise this expensive check // will be invoked by PMDataManager::verifyPreservedAnalysis when // a regionpass (marked PreservedAll) finish. if (!RegionInfoBase::VerifyRegionInfo) return; std::set visited; verifyWalk(getEntry(), &visited); } template void RegionBase::verifyRegionNest() const { for (const std::unique_ptr &R : *this) R->verifyRegionNest(); verifyRegion(); } template typename RegionBase::element_iterator RegionBase::element_begin() { return GraphTraits::nodes_begin(static_cast(this)); } template typename RegionBase::element_iterator RegionBase::element_end() { return GraphTraits::nodes_end(static_cast(this)); } template typename RegionBase::const_element_iterator RegionBase::element_begin() const { return GraphTraits::nodes_begin( static_cast(this)); } template typename RegionBase::const_element_iterator RegionBase::element_end() const { return GraphTraits::nodes_end( static_cast(this)); } template typename Tr::RegionT *RegionBase::getSubRegionNode(BlockT *BB) const { using RegionT = typename Tr::RegionT; RegionT *R = RI->getRegionFor(BB); if (!R || R == this) return nullptr; // If we pass the BB out of this region, that means our code is broken. assert(contains(R) && "BB not in current region!"); while (contains(R->getParent()) && R->getParent() != this) R = R->getParent(); if (R->getEntry() != BB) return nullptr; return R; } template typename Tr::RegionNodeT *RegionBase::getBBNode(BlockT *BB) const { assert(contains(BB) && "Can get BB node out of this region!"); typename BBNodeMapT::const_iterator at = BBNodeMap.find(BB); if (at == BBNodeMap.end()) { auto Deconst = const_cast *>(this); typename BBNodeMapT::value_type V = { BB, std::make_unique(static_cast(Deconst), BB)}; at = BBNodeMap.insert(std::move(V)).first; } return at->second.get(); } template typename Tr::RegionNodeT *RegionBase::getNode(BlockT *BB) const { assert(contains(BB) && "Can get BB node out of this region!"); if (RegionT *Child = getSubRegionNode(BB)) return Child->getNode(); return getBBNode(BB); } template void RegionBase::transferChildrenTo(RegionT *To) { for (std::unique_ptr &R : *this) { R->parent = To; To->children.push_back(std::move(R)); } children.clear(); } template void RegionBase::addSubRegion(RegionT *SubRegion, bool moveChildren) { assert(!SubRegion->parent && "SubRegion already has a parent!"); assert(llvm::find_if(*this, [&](const std::unique_ptr &R) { return R.get() == SubRegion; }) == children.end() && "Subregion already exists!"); SubRegion->parent = static_cast(this); children.push_back(std::unique_ptr(SubRegion)); if (!moveChildren) return; assert(SubRegion->children.empty() && "SubRegions that contain children are not supported"); for (RegionNodeT *Element : elements()) { if (!Element->isSubRegion()) { BlockT *BB = Element->template getNodeAs(); if (SubRegion->contains(BB)) RI->setRegionFor(BB, SubRegion); } } std::vector> Keep; for (std::unique_ptr &R : *this) { if (SubRegion->contains(R.get()) && R.get() != SubRegion) { R->parent = SubRegion; SubRegion->children.push_back(std::move(R)); } else Keep.push_back(std::move(R)); } children.clear(); children.insert( children.begin(), std::move_iterator(Keep.begin()), std::move_iterator(Keep.end())); } template typename Tr::RegionT *RegionBase::removeSubRegion(RegionT *Child) { assert(Child->parent == this && "Child is not a child of this region!"); Child->parent = nullptr; typename RegionSet::iterator I = llvm::find_if(children, [&](const std::unique_ptr &R) { return R.get() == Child; }); assert(I != children.end() && "Region does not exit. Unable to remove."); children.erase(children.begin() + (I - begin())); return Child; } template unsigned RegionBase::getDepth() const { unsigned Depth = 0; for (RegionT *R = getParent(); R != nullptr; R = R->getParent()) ++Depth; return Depth; } template typename Tr::RegionT *RegionBase::getExpandedRegion() const { unsigned NumSuccessors = Tr::getNumSuccessors(exit); if (NumSuccessors == 0) return nullptr; RegionT *R = RI->getRegionFor(exit); if (R->getEntry() != exit) { for (BlockT *Pred : make_range(InvBlockTraits::child_begin(getExit()), InvBlockTraits::child_end(getExit()))) if (!contains(Pred)) return nullptr; if (Tr::getNumSuccessors(exit) == 1) return new RegionT(getEntry(), *BlockTraits::child_begin(exit), RI, DT); return nullptr; } while (R->getParent() && R->getParent()->getEntry() == exit) R = R->getParent(); for (BlockT *Pred : make_range(InvBlockTraits::child_begin(getExit()), InvBlockTraits::child_end(getExit()))) { if (!(contains(Pred) || R->contains(Pred))) return nullptr; } return new RegionT(getEntry(), R->getExit(), RI, DT); } template void RegionBase::print(raw_ostream &OS, bool print_tree, unsigned level, PrintStyle Style) const { if (print_tree) OS.indent(level * 2) << '[' << level << "] " << getNameStr(); else OS.indent(level * 2) << getNameStr(); OS << '\n'; if (Style != PrintNone) { OS.indent(level * 2) << "{\n"; OS.indent(level * 2 + 2); if (Style == PrintBB) { for (const auto *BB : blocks()) OS << BB->getName() << ", "; // TODO: remove the last "," } else if (Style == PrintRN) { for (const RegionNodeT *Element : elements()) { OS << *Element << ", "; // TODO: remove the last ", } } OS << '\n'; } if (print_tree) { for (const std::unique_ptr &R : *this) R->print(OS, print_tree, level + 1, Style); } if (Style != PrintNone) OS.indent(level * 2) << "} \n"; } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) template void RegionBase::dump() const { print(dbgs(), true, getDepth(), RegionInfoBase::printStyle); } #endif template void RegionBase::clearNodeCache() { BBNodeMap.clear(); for (std::unique_ptr &R : *this) R->clearNodeCache(); } //===----------------------------------------------------------------------===// // RegionInfoBase implementation // template RegionInfoBase::RegionInfoBase() = default; template RegionInfoBase::~RegionInfoBase() { releaseMemory(); } template void RegionInfoBase::verifyBBMap(const RegionT *R) const { assert(R && "Re must be non-null"); for (const typename Tr::RegionNodeT *Element : R->elements()) { if (Element->isSubRegion()) { const RegionT *SR = Element->template getNodeAs(); verifyBBMap(SR); } else { BlockT *BB = Element->template getNodeAs(); if (getRegionFor(BB) != R) report_fatal_error("BB map does not match region nesting"); } } } template bool RegionInfoBase::isCommonDomFrontier(BlockT *BB, BlockT *entry, BlockT *exit) const { for (BlockT *P : make_range(InvBlockTraits::child_begin(BB), InvBlockTraits::child_end(BB))) { if (DT->dominates(entry, P) && !DT->dominates(exit, P)) return false; } return true; } template bool RegionInfoBase::isRegion(BlockT *entry, BlockT *exit) const { assert(entry && exit && "entry and exit must not be null!"); using DST = typename DomFrontierT::DomSetType; DST *entrySuccs = &DF->find(entry)->second; // Exit is the header of a loop that contains the entry. In this case, // the dominance frontier must only contain the exit. if (!DT->dominates(entry, exit)) { for (BlockT *successor : *entrySuccs) { if (successor != exit && successor != entry) return false; } return true; } DST *exitSuccs = &DF->find(exit)->second; // Do not allow edges leaving the region. for (BlockT *Succ : *entrySuccs) { if (Succ == exit || Succ == entry) continue; if (exitSuccs->find(Succ) == exitSuccs->end()) return false; if (!isCommonDomFrontier(Succ, entry, exit)) return false; } // Do not allow edges pointing into the region. for (BlockT *Succ : *exitSuccs) { if (DT->properlyDominates(entry, Succ) && Succ != exit) return false; } return true; } template void RegionInfoBase::insertShortCut(BlockT *entry, BlockT *exit, BBtoBBMap *ShortCut) const { assert(entry && exit && "entry and exit must not be null!"); typename BBtoBBMap::iterator e = ShortCut->find(exit); if (e == ShortCut->end()) // No further region at exit available. (*ShortCut)[entry] = exit; else { // We found a region e that starts at exit. Therefore (entry, e->second) // is also a region, that is larger than (entry, exit). Insert the // larger one. BlockT *BB = e->second; (*ShortCut)[entry] = BB; } } template typename Tr::DomTreeNodeT * RegionInfoBase::getNextPostDom(DomTreeNodeT *N, BBtoBBMap *ShortCut) const { typename BBtoBBMap::iterator e = ShortCut->find(N->getBlock()); if (e == ShortCut->end()) return N->getIDom(); return PDT->getNode(e->second)->getIDom(); } template bool RegionInfoBase::isTrivialRegion(BlockT *entry, BlockT *exit) const { assert(entry && exit && "entry and exit must not be null!"); unsigned num_successors = BlockTraits::child_end(entry) - BlockTraits::child_begin(entry); if (num_successors <= 1 && exit == *(BlockTraits::child_begin(entry))) return true; return false; } template typename Tr::RegionT *RegionInfoBase::createRegion(BlockT *entry, BlockT *exit) { assert(entry && exit && "entry and exit must not be null!"); if (isTrivialRegion(entry, exit)) return nullptr; RegionT *region = new RegionT(entry, exit, static_cast(this), DT); BBtoRegion.insert({entry, region}); #ifdef EXPENSIVE_CHECKS region->verifyRegion(); #else LLVM_DEBUG(region->verifyRegion()); #endif updateStatistics(region); return region; } template void RegionInfoBase::findRegionsWithEntry(BlockT *entry, BBtoBBMap *ShortCut) { assert(entry); DomTreeNodeT *N = PDT->getNode(entry); if (!N) return; RegionT *lastRegion = nullptr; BlockT *lastExit = entry; // As only a BasicBlock that postdominates entry can finish a region, walk the // post dominance tree upwards. while ((N = getNextPostDom(N, ShortCut))) { BlockT *exit = N->getBlock(); if (!exit) break; if (isRegion(entry, exit)) { RegionT *newRegion = createRegion(entry, exit); if (lastRegion) newRegion->addSubRegion(lastRegion); lastRegion = newRegion; lastExit = exit; } // This can never be a region, so stop the search. if (!DT->dominates(entry, exit)) break; } // Tried to create regions from entry to lastExit. Next time take a // shortcut from entry to lastExit. if (lastExit != entry) insertShortCut(entry, lastExit, ShortCut); } template void RegionInfoBase::scanForRegions(FuncT &F, BBtoBBMap *ShortCut) { using FuncPtrT = std::add_pointer_t; BlockT *entry = GraphTraits::getEntryNode(&F); DomTreeNodeT *N = DT->getNode(entry); // Iterate over the dominance tree in post order to start with the small // regions from the bottom of the dominance tree. If the small regions are // detected first, detection of bigger regions is faster, as we can jump // over the small regions. for (auto DomNode : post_order(N)) findRegionsWithEntry(DomNode->getBlock(), ShortCut); } template typename Tr::RegionT *RegionInfoBase::getTopMostParent(RegionT *region) { while (region->getParent()) region = region->getParent(); return region; } template void RegionInfoBase::buildRegionsTree(DomTreeNodeT *N, RegionT *region) { BlockT *BB = N->getBlock(); // Passed region exit while (BB == region->getExit()) region = region->getParent(); typename BBtoRegionMap::iterator it = BBtoRegion.find(BB); // This basic block is a start block of a region. It is already in the // BBtoRegion relation. Only the child basic blocks have to be updated. if (it != BBtoRegion.end()) { RegionT *newRegion = it->second; region->addSubRegion(getTopMostParent(newRegion)); region = newRegion; } else { BBtoRegion[BB] = region; } for (DomTreeNodeBase *C : *N) { buildRegionsTree(C, region); } } #ifdef EXPENSIVE_CHECKS template bool RegionInfoBase::VerifyRegionInfo = true; #else template bool RegionInfoBase::VerifyRegionInfo = false; #endif template typename Tr::RegionT::PrintStyle RegionInfoBase::printStyle = RegionBase::PrintNone; template void RegionInfoBase::print(raw_ostream &OS) const { OS << "Region tree:\n"; TopLevelRegion->print(OS, true, 0, printStyle); OS << "End region tree\n"; } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) template void RegionInfoBase::dump() const { print(dbgs()); } #endif template void RegionInfoBase::releaseMemory() { BBtoRegion.clear(); if (TopLevelRegion) delete TopLevelRegion; TopLevelRegion = nullptr; } template void RegionInfoBase::verifyAnalysis() const { // Do only verify regions if explicitely activated using EXPENSIVE_CHECKS or // -verify-region-info if (!RegionInfoBase::VerifyRegionInfo) return; TopLevelRegion->verifyRegionNest(); verifyBBMap(TopLevelRegion); } // Region pass manager support. template typename Tr::RegionT *RegionInfoBase::getRegionFor(BlockT *BB) const { return BBtoRegion.lookup(BB); } template void RegionInfoBase::setRegionFor(BlockT *BB, RegionT *R) { BBtoRegion[BB] = R; } template typename Tr::RegionT *RegionInfoBase::operator[](BlockT *BB) const { return getRegionFor(BB); } template typename RegionInfoBase::BlockT * RegionInfoBase::getMaxRegionExit(BlockT *BB) const { BlockT *Exit = nullptr; while (true) { // Get largest region that starts at BB. RegionT *R = getRegionFor(BB); while (R && R->getParent() && R->getParent()->getEntry() == BB) R = R->getParent(); // Get the single exit of BB. if (R && R->getEntry() == BB) Exit = R->getExit(); else { auto next = BlockTraits::child_begin(BB); ++next; if (next == BlockTraits::child_end(BB)) Exit = *BlockTraits::child_begin(BB); else // No single exit exists. return Exit; } // Get largest region that starts at Exit. RegionT *ExitR = getRegionFor(Exit); while (ExitR && ExitR->getParent() && ExitR->getParent()->getEntry() == Exit) ExitR = ExitR->getParent(); for (BlockT *Pred : make_range(InvBlockTraits::child_begin(Exit), InvBlockTraits::child_end(Exit))) { if (!R->contains(Pred) && !ExitR->contains(Pred)) break; } // This stops infinite cycles. if (DT->dominates(Exit, BB)) break; BB = Exit; } return Exit; } template typename Tr::RegionT *RegionInfoBase::getCommonRegion(RegionT *A, RegionT *B) const { assert(A && B && "One of the Regions is NULL"); if (A->contains(B)) return A; while (!B->contains(A)) B = B->getParent(); return B; } template typename Tr::RegionT * RegionInfoBase::getCommonRegion(SmallVectorImpl &Regions) const { RegionT *ret = Regions.pop_back_val(); for (RegionT *R : Regions) ret = getCommonRegion(ret, R); return ret; } template typename Tr::RegionT * RegionInfoBase::getCommonRegion(SmallVectorImpl &BBs) const { RegionT *ret = getRegionFor(BBs.back()); BBs.pop_back(); for (BlockT *BB : BBs) ret = getCommonRegion(ret, getRegionFor(BB)); return ret; } template void RegionInfoBase::calculate(FuncT &F) { using FuncPtrT = std::add_pointer_t; // ShortCut a function where for every BB the exit of the largest region // starting with BB is stored. These regions can be threated as single BBS. // This improves performance on linear CFGs. BBtoBBMap ShortCut; scanForRegions(F, &ShortCut); BlockT *BB = GraphTraits::getEntryNode(&F); buildRegionsTree(DT->getNode(BB), TopLevelRegion); } } // end namespace llvm #undef DEBUG_TYPE #endif // LLVM_ANALYSIS_REGIONINFOIMPL_H #ifdef __GNUC__ #pragma GCC diagnostic pop #endif