Skip to content

[CycleAnalysis] Methods to verify cycles and their nesting. #103006

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 141 additions & 54 deletions llvm/include/llvm/ADT/GenericCycleImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/DepthFirstIterator.h"
#include "llvm/ADT/GenericCycleInfo.h"
#include "llvm/ADT/StringExtras.h"

#define DEBUG_TYPE "generic-cycle-impl"

Expand Down Expand Up @@ -119,6 +120,104 @@ auto GenericCycle<ContextT>::getCyclePredecessor() const -> BlockT * {
return Out;
}

/// \brief Verify that this is actually a well-formed cycle in the CFG.
template <typename ContextT> void GenericCycle<ContextT>::verifyCycle() const {
#ifndef NDEBUG
assert(!Blocks.empty() && "Cycle cannot be empty.");
DenseSet<BlockT *> Blocks;
for (BlockT *BB : blocks()) {
assert(Blocks.insert(BB).second); // duplicates in block list?
}
assert(!Entries.empty() && "Cycle must have one or more entries.");

DenseSet<BlockT *> Entries;
for (BlockT *Entry : entries()) {
assert(Entries.insert(Entry).second); // duplicate entry?
assert(contains(Entry));
}

// Setup for using a depth-first iterator to visit every block in the cycle.
SmallVector<BlockT *, 8> ExitBBs;
getExitBlocks(ExitBBs);
df_iterator_default_set<BlockT *> VisitSet;
VisitSet.insert(ExitBBs.begin(), ExitBBs.end());

// Keep track of the BBs visited.
SmallPtrSet<BlockT *, 8> VisitedBBs;

// Check the individual blocks.
for (BlockT *BB : depth_first_ext(getHeader(), VisitSet)) {
assert(llvm::any_of(llvm::children<BlockT *>(BB),
[&](BlockT *B) { return contains(B); }) &&
"Cycle block has no in-cycle successors!");

assert(llvm::any_of(llvm::inverse_children<BlockT *>(BB),
[&](BlockT *B) { return contains(B); }) &&
"Cycle block has no in-cycle predecessors!");

DenseSet<BlockT *> OutsideCyclePreds;
for (BlockT *B : llvm::inverse_children<BlockT *>(BB))
if (!contains(B))
OutsideCyclePreds.insert(B);

if (Entries.contains(BB)) {
assert(!OutsideCyclePreds.empty() && "Entry is unreachable!");
} else if (!OutsideCyclePreds.empty()) {
// A non-entry block shouldn't be reachable from outside the cycle,
// though it is permitted if the predecessor is not itself actually
// reachable.
BlockT *EntryBB = &BB->getParent()->front();
for (BlockT *CB : depth_first(EntryBB))
assert(!OutsideCyclePreds.contains(CB) &&
"Non-entry block reachable from outside!");
}
assert(BB != &getHeader()->getParent()->front() &&
"Cycle contains function entry block!");

VisitedBBs.insert(BB);
}

if (VisitedBBs.size() != getNumBlocks()) {
dbgs() << "The following blocks are unreachable in the cycle:\n ";
ListSeparator LS;
for (auto *BB : Blocks) {
if (!VisitedBBs.count(BB)) {
dbgs() << LS;
BB->printAsOperand(dbgs());
}
}
dbgs() << "\n";
assert(false && "Unreachable block in cycle");
}

verifyCycleNest();
#endif
}

/// \brief Verify the parent-child relations of this cycle.
///
/// Note that this does \em not check that cycle is really a cycle in the CFG.
template <typename ContextT>
void GenericCycle<ContextT>::verifyCycleNest() const {
#ifndef NDEBUG
// Check the subcycles.
for (GenericCycle *Child : children()) {
// Each block in each subcycle should be contained within this cycle.
for (BlockT *BB : Child->blocks()) {
assert(contains(BB) &&
"Cycle does not contain all the blocks of a subcycle!");
}
assert(Child->Depth == Depth + 1);
}

// Check the parent cycle pointer.
if (ParentCycle) {
assert(is_contained(ParentCycle->children(), this) &&
"Cycle is not a subcycle of its parent!");
}
#endif
}

/// \brief Helper class for computing cycle information.
template <typename ContextT> class GenericCycleInfoCompute {
using BlockT = typename ContextT::BlockT;
Expand Down Expand Up @@ -400,8 +499,6 @@ void GenericCycleInfo<ContextT>::compute(FunctionT &F) {
LLVM_DEBUG(errs() << "Computing cycles for function: " << F.getName()
<< "\n");
Compute.run(&F.front());

assert(validateTree());
}

template <typename ContextT>
Expand All @@ -414,7 +511,7 @@ void GenericCycleInfo<ContextT>::splitCriticalEdge(BlockT *Pred, BlockT *Succ,
return;

addBlockToCycle(NewBlock, Cycle);
assert(validateTree());
verifyCycleNest();
}

/// \brief Find the innermost cycle containing a given block.
Expand Down Expand Up @@ -468,73 +565,63 @@ unsigned GenericCycleInfo<ContextT>::getCycleDepth(const BlockT *Block) const {
return Cycle->getDepth();
}

#ifndef NDEBUG
/// \brief Validate the internal consistency of the cycle tree.
/// \brief Verify 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.
///
/// Every natural loop has a corresponding cycle (possibly irreducible) with the
/// same header, and every reducible cycle is a natural loop with the same
/// header. We check this by comparing headers encountered in the two forests.
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)
void GenericCycleInfo<ContextT>::verifyCycleNest(bool VerifyFull,
LoopInfoT *LI) const {
#ifndef NDEBUG
DenseSet<BlockT *> LoopHeaders;
DenseSet<BlockT *> CycleHeaders;

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?
if (LI) {
for (LoopT *TopLoop : *LI) {
for (LoopT *Loop : depth_first(TopLoop)) {
LoopHeaders.insert(Loop->getHeader());
}
Blocks.clear();
}
}

check(!Cycle->Entries.empty());
for (BlockT *Entry : Cycle->Entries) {
check(Entries.insert(Entry).second); // duplicate entry?
check(is_contained(Cycle->Blocks, Entry));
for (CycleT *TopCycle : toplevel_cycles()) {
for (CycleT *Cycle : depth_first(TopCycle)) {
if (VerifyFull)
Cycle->verifyCycle();
else
Cycle->verifyCycleNest();
// Check the block map entries for blocks contained in this cycle.
for (BlockT *BB : Cycle->blocks()) {
auto MapIt = BlockMap.find(BB);
assert(MapIt != BlockMap.end());
assert(Cycle->contains(MapIt->second));
}
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);
}
if (LI) {
BlockT *Header = Cycle->getHeader();
assert(CycleHeaders.insert(Header).second);
if (Cycle->isReducible())
assert(LoopHeaders.contains(Header));
}
}
}

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));
if (LI) {
for (BlockT *Header : LoopHeaders) {
assert(CycleHeaders.contains(Header));
}
}
#endif
}

#undef check

return true;
/// \brief Verify that the entire cycle tree well-formed.
template <typename ContextT>
void GenericCycleInfo<ContextT>::verify(LoopInfoT *LI) const {
verifyCycleNest(/*VerifyFull=*/true, LI);
}
#endif

/// \brief Print the cycle info.
template <typename ContextT>
Expand Down
10 changes: 7 additions & 3 deletions llvm/include/llvm/ADT/GenericCycleInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ template <typename ContextT> class GenericCycle {
/// it, otherwise return nullptr.
BlockT *getCyclePredecessor() const;

void verifyCycle() const;
void verifyCycleNest() const;

/// Iteration over child cycles.
//@{
using const_child_iterator_base =
Expand Down Expand Up @@ -228,6 +231,8 @@ template <typename ContextT> class GenericCycleInfo {
using BlockT = typename ContextT::BlockT;
using CycleT = GenericCycle<ContextT>;
using FunctionT = typename ContextT::FunctionT;
using LoopT = typename ContextT::LoopT;
using LoopInfoT = typename ContextT::LoopInfoT;
template <typename> friend class GenericCycle;
template <typename> friend class GenericCycleInfoCompute;

Expand Down Expand Up @@ -277,9 +282,8 @@ template <typename ContextT> class GenericCycleInfo {

/// Methods for debug and self-test.
//@{
#ifndef NDEBUG
bool validateTree() const;
#endif
void verifyCycleNest(bool VerifyFull = false, LoopInfoT *LI = nullptr) const;
void verify(LoopInfoT *LI = nullptr) const;
void print(raw_ostream &Out) const;
void dump() const { print(dbgs()); }
Printable print(const CycleT *Cycle) { return Cycle->print(Context); }
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/ADT/GenericSSAContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ template <typename _FunctionT> class GenericSSAContext {
// a given funciton.
using DominatorTreeT = DominatorTreeBase<BlockT, false>;

// A LoopT is a natural loop in the CFG.
using LoopT = typename SSATraits::LoopT;

// A LoopInfoT contains a forest of natural loops in the CFG.
using LoopInfoT = typename SSATraits::LoopInfoT;

GenericSSAContext() = default;
GenericSSAContext(const FunctionT *F) : F(F) {}

Expand Down
6 changes: 4 additions & 2 deletions llvm/include/llvm/Analysis/CycleAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,17 @@ class CycleAnalysis : public AnalysisInfoMixin<CycleAnalysis> {
// TODO: verify analysis?
};

/// Printer pass for the \c DominatorTree.
class CycleInfoPrinterPass : public PassInfoMixin<CycleInfoPrinterPass> {
raw_ostream &OS;

public:
explicit CycleInfoPrinterPass(raw_ostream &OS);

PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
static bool isRequired() { return true; }
};

struct CycleInfoVerifierPass : public PassInfoMixin<CycleInfoVerifierPass> {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
static bool isRequired() { return true; }
};

Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/CodeGen/MachineSSAContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
namespace llvm {
class MachineInstr;
class MachineFunction;
class MachineLoop;
class MachineLoopInfo;
class Register;

inline unsigned succ_size(const MachineBasicBlock *BB) {
Expand All @@ -39,6 +41,8 @@ template <> struct GenericSSATraits<MachineFunction> {
using ValueRefT = Register;
using ConstValueRefT = Register;
using UseT = MachineOperand;
using LoopT = MachineLoop;
using LoopInfoT = MachineLoopInfo;
};

using MachineSSAContext = GenericSSAContext<MachineFunction>;
Expand Down
4 changes: 4 additions & 0 deletions llvm/include/llvm/IR/SSAContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
namespace llvm {
class BasicBlock;
class Function;
class Loop;
class LoopInfo;
class Instruction;
class Value;

Expand All @@ -35,6 +37,8 @@ template <> struct GenericSSATraits<Function> {
using ValueRefT = Value *;
using ConstValueRefT = const Value *;
using UseT = Use;
using LoopT = Loop;
using LoopInfoT = LoopInfo;
};

using SSAContext = GenericSSAContext<Function>;
Expand Down
Loading