blob: e1362d8597b2af044e611975e0430361a380e872 [file] [log] [blame]
//===- 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:
/// - CycleAnalysis.cpp
/// - 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 <typename ContextT>
bool GenericCycle<ContextT>::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 <typename ContextT>
void GenericCycle<ContextT>::getExitBlocks(
SmallVectorImpl<BlockT *> &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 <typename ContextT>
auto GenericCycle<ContextT>::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 <typename ContextT>
auto GenericCycle<ContextT>::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 <typename ContextT> class GenericCycleInfoCompute {
using BlockT = typename ContextT::BlockT;
using CycleInfoT = GenericCycleInfo<ContextT>;
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<BlockT *, DFSInfo> BlockDFSInfo;
SmallVector<BlockT *, 8> 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 <typename ContextT>
auto GenericCycleInfo<ContextT>::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 <typename ContextT>
void GenericCycleInfo<ContextT>::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;
}
/// \brief Main function of the cycle info computations.
template <typename ContextT>
void GenericCycleInfoCompute<ContextT>::run(BlockT *EntryBlock) {
LLVM_DEBUG(errs() << "Entry block: " << Info.Context.print(EntryBlock)
<< "\n");
dfs(EntryBlock);
SmallVector<BlockT *, 8> 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<CycleT> NewCycle = std::make_unique<CycleT>();
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 <typename ContextT>
void GenericCycleInfoCompute<ContextT>::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 <typename ContextT>
void GenericCycleInfoCompute<ContextT>::dfs(BlockT *EntryBlock) {
SmallVector<unsigned, 8> DFSTreeStack;
SmallVector<BlockT *, 8> 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 <typename ContextT> void GenericCycleInfo<ContextT>::clear() {
TopLevelCycles.clear();
BlockMap.clear();
BlockMapTopLevel.clear();
}
/// \brief Compute the cycle info for a function.
template <typename ContextT>
void GenericCycleInfo<ContextT>::compute(FunctionT &F) {
GenericCycleInfoCompute<ContextT> Compute(*this);
Context.setFunction(F);
LLVM_DEBUG(errs() << "Computing cycles for function: " << F.getName()
<< "\n");
Compute.run(ContextT::getEntryBlock(F));
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 <typename ContextT>
auto GenericCycleInfo<ContextT>::getCycle(const BlockT *Block) const
-> CycleT * {
auto MapIt = BlockMap.find(Block);
if (MapIt != BlockMap.end())
return MapIt->second;
return nullptr;
}
/// \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 <typename ContextT>
unsigned GenericCycleInfo<ContextT>::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 <typename ContextT>
bool GenericCycleInfo<ContextT>::validateTree() const {
DenseSet<BlockT *> Blocks;
DenseSet<BlockT *> 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 <typename ContextT>
void GenericCycleInfo<ContextT>::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