Skip to content

Commit 166890b

Browse files
committed
[CycleAnalysis] Methods to verify cycles and their nesting.
The original implementation provided a simple method to check whether the forest of nested cycles is well-formed. This is now augmented with other methods to check well-formedness of every cycle, either invdividually, or as the entire forest. These will be used by future transforms that modify CycleInfo.
1 parent b68df87 commit 166890b

File tree

12 files changed

+176
-61
lines changed

12 files changed

+176
-61
lines changed

llvm/include/llvm/ADT/GenericCycleImpl.h

Lines changed: 137 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,101 @@ auto GenericCycle<ContextT>::getCyclePredecessor() const -> BlockT * {
119119
return Out;
120120
}
121121

122+
/// \brief Verify that this is actually a well-formed cycle in the CFG.
123+
template <typename ContextT> void GenericCycle<ContextT>::verifyCycle() const {
124+
#ifndef NDEBUG
125+
assert(!Blocks.empty() && "Cycle cannot be empty.");
126+
DenseSet<BlockT *> Blocks;
127+
for (BlockT *BB : blocks()) {
128+
assert(Blocks.insert(BB).second); // duplicates in block list?
129+
}
130+
assert(!Entries.empty() && "Cycle must have one or more entries.");
131+
132+
DenseSet<BlockT *> Entries;
133+
for (BlockT *Entry : entries()) {
134+
assert(Entries.insert(Entry).second); // duplicate entry?
135+
assert(contains(Entry));
136+
}
137+
138+
// Setup for using a depth-first iterator to visit every block in the cycle.
139+
SmallVector<BlockT *, 8> ExitBBs;
140+
getExitBlocks(ExitBBs);
141+
df_iterator_default_set<BlockT *> VisitSet;
142+
VisitSet.insert(ExitBBs.begin(), ExitBBs.end());
143+
144+
// Keep track of the BBs visited.
145+
SmallPtrSet<BlockT *, 8> VisitedBBs;
146+
147+
// Check the individual blocks.
148+
for (BlockT *BB : depth_first_ext(getHeader(), VisitSet)) {
149+
assert(llvm::any_of(llvm::children<BlockT *>(BB),
150+
[&](BlockT *B) { return contains(B); }) &&
151+
"Cycle block has no in-cycle successors!");
152+
153+
assert(llvm::any_of(llvm::inverse_children<BlockT *>(BB),
154+
[&](BlockT *B) { return contains(B); }) &&
155+
"Cycle block has no in-cycle predecessors!");
156+
157+
DenseSet<BlockT *> OutsideCyclePreds;
158+
for (BlockT *B : llvm::inverse_children<BlockT *>(BB))
159+
if (!contains(B))
160+
OutsideCyclePreds.insert(B);
161+
162+
if (Entries.contains(BB)) {
163+
assert(!OutsideCyclePreds.empty() && "Entry is unreachable!");
164+
} else if (!OutsideCyclePreds.empty()) {
165+
// A non-entry block shouldn't be reachable from outside the cycle,
166+
// though it is permitted if the predecessor is not itself actually
167+
// reachable.
168+
BlockT *EntryBB = &BB->getParent()->front();
169+
for (BlockT *CB : depth_first(EntryBB))
170+
assert(!OutsideCyclePreds.contains(CB) &&
171+
"Non-entry block reachable from outside!");
172+
}
173+
assert(BB != &getHeader()->getParent()->front() &&
174+
"Cycle contains function entry block!");
175+
176+
VisitedBBs.insert(BB);
177+
}
178+
179+
if (VisitedBBs.size() != getNumBlocks()) {
180+
dbgs() << "The following blocks are unreachable in the cycle: ";
181+
for (auto *BB : Blocks) {
182+
if (!VisitedBBs.count(BB)) {
183+
dbgs() << *BB << "\n";
184+
}
185+
}
186+
assert(false && "Unreachable block in cycle");
187+
}
188+
189+
verifyCycleNest();
190+
#endif
191+
}
192+
193+
/// \brief Verify the parent-child relations of this cycle.
194+
///
195+
/// Note that this does \em not check that cycle is really a cycle in the CFG.
196+
template <typename ContextT>
197+
void GenericCycle<ContextT>::verifyCycleNest() const {
198+
#ifndef NDEBUG
199+
// Check the subcycles.
200+
for (GenericCycle *Child : children()) {
201+
// Each block in each subcycle should be contained within this cycle.
202+
for (BlockT *BB : Child->blocks()) {
203+
assert(contains(BB) &&
204+
"Cycle does not contain all the blocks of a subcycle!");
205+
}
206+
assert(Child->Depth == Depth + 1);
207+
}
208+
209+
// Check the parent cycle pointer.
210+
if (ParentCycle) {
211+
assert(is_contained(ParentCycle->children(), this) &&
212+
"Cycle is not a subcycle of its parent!");
213+
}
214+
#endif
215+
}
216+
122217
/// \brief Helper class for computing cycle information.
123218
template <typename ContextT> class GenericCycleInfoCompute {
124219
using BlockT = typename ContextT::BlockT;
@@ -400,8 +495,6 @@ void GenericCycleInfo<ContextT>::compute(FunctionT &F) {
400495
LLVM_DEBUG(errs() << "Computing cycles for function: " << F.getName()
401496
<< "\n");
402497
Compute.run(&F.front());
403-
404-
assert(validateTree());
405498
}
406499

407500
template <typename ContextT>
@@ -414,7 +507,7 @@ void GenericCycleInfo<ContextT>::splitCriticalEdge(BlockT *Pred, BlockT *Succ,
414507
return;
415508

416509
addBlockToCycle(NewBlock, Cycle);
417-
assert(validateTree());
510+
verifyCycleNest();
418511
}
419512

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

471-
#ifndef NDEBUG
472-
/// \brief Validate the internal consistency of the cycle tree.
564+
/// \brief Verify the internal consistency of the cycle tree.
473565
///
474566
/// Note that this does \em not check that cycles are really cycles in the CFG,
475567
/// or that the right set of cycles in the CFG were found.
568+
///
569+
/// Every natural loop has a corresponding cycle (possibly irreducible) with the
570+
/// same header, and every reducible cycle is a natural loop with the same
571+
/// header. We check this by comparing headers encountered in the two forests.
476572
template <typename ContextT>
477-
bool GenericCycleInfo<ContextT>::validateTree() const {
478-
DenseSet<BlockT *> Blocks;
479-
DenseSet<BlockT *> Entries;
480-
481-
auto reportError = [](const char *File, int Line, const char *Cond) {
482-
errs() << File << ':' << Line
483-
<< ": GenericCycleInfo::validateTree: " << Cond << '\n';
484-
};
485-
#define check(cond) \
486-
do { \
487-
if (!(cond)) { \
488-
reportError(__FILE__, __LINE__, #cond); \
489-
return false; \
490-
} \
491-
} while (false)
573+
void GenericCycleInfo<ContextT>::verifyCycleNest(bool VerifyFull,
574+
LoopInfoT *LI) const {
575+
#ifndef NDEBUG
576+
DenseSet<BlockT *> LoopHeaders;
577+
DenseSet<BlockT *> CycleHeaders;
492578

493-
for (const auto *TLC : toplevel_cycles()) {
494-
for (const CycleT *Cycle : depth_first(TLC)) {
495-
if (Cycle->ParentCycle)
496-
check(is_contained(Cycle->ParentCycle->children(), Cycle));
497-
498-
for (BlockT *Block : Cycle->Blocks) {
499-
auto MapIt = BlockMap.find(Block);
500-
check(MapIt != BlockMap.end());
501-
check(Cycle->contains(MapIt->second));
502-
check(Blocks.insert(Block).second); // duplicates in block list?
579+
if (LI) {
580+
for (LoopT *TopLoop : *LI) {
581+
for (LoopT *Loop : depth_first(TopLoop)) {
582+
LoopHeaders.insert(Loop->getHeader());
503583
}
504-
Blocks.clear();
584+
}
585+
}
505586

506-
check(!Cycle->Entries.empty());
507-
for (BlockT *Entry : Cycle->Entries) {
508-
check(Entries.insert(Entry).second); // duplicate entry?
509-
check(is_contained(Cycle->Blocks, Entry));
587+
for (CycleT *TopCycle : toplevel_cycles()) {
588+
for (CycleT *Cycle : depth_first(TopCycle)) {
589+
if (VerifyFull)
590+
Cycle->verifyCycle();
591+
else
592+
Cycle->verifyCycleNest();
593+
// Check the block map entries for blocks contained in this cycle.
594+
for (BlockT *BB : Cycle->blocks()) {
595+
auto MapIt = BlockMap.find(BB);
596+
assert(MapIt != BlockMap.end());
597+
assert(Cycle->contains(MapIt->second));
510598
}
511-
Entries.clear();
512-
513-
unsigned ChildDepth = 0;
514-
for (const CycleT *Child : Cycle->children()) {
515-
check(Child->Depth > Cycle->Depth);
516-
if (!ChildDepth) {
517-
ChildDepth = Child->Depth;
518-
} else {
519-
check(ChildDepth == Child->Depth);
520-
}
599+
if (LI) {
600+
BlockT *Header = Cycle->getHeader();
601+
assert(CycleHeaders.insert(Header).second);
602+
if (Cycle->isReducible())
603+
assert(LoopHeaders.contains(Header));
521604
}
522605
}
523606
}
524607

525-
for (const auto &Entry : BlockMap) {
526-
BlockT *Block = Entry.first;
527-
for (const CycleT *Cycle = Entry.second; Cycle;
528-
Cycle = Cycle->ParentCycle) {
529-
check(is_contained(Cycle->Blocks, Block));
608+
if (LI) {
609+
for (BlockT *Header : LoopHeaders) {
610+
assert(CycleHeaders.contains(Header));
530611
}
531612
}
613+
#endif
614+
}
532615

533-
#undef check
534-
535-
return true;
616+
/// \brief Verify that the entire cycle tree well-formed.
617+
template <typename ContextT>
618+
void GenericCycleInfo<ContextT>::verify(LoopInfoT *LI) const {
619+
verifyCycleNest(/*VerifyFull=*/true, LI);
536620
}
537-
#endif
538621

539622
/// \brief Print the cycle info.
540623
template <typename ContextT>

llvm/include/llvm/ADT/GenericCycleInfo.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ template <typename ContextT> class GenericCycle {
140140
/// it, otherwise return nullptr.
141141
BlockT *getCyclePredecessor() const;
142142

143+
void verifyCycle() const;
144+
void verifyCycleNest() const;
145+
143146
/// Iteration over child cycles.
144147
//@{
145148
using const_child_iterator_base =
@@ -228,6 +231,8 @@ template <typename ContextT> class GenericCycleInfo {
228231
using BlockT = typename ContextT::BlockT;
229232
using CycleT = GenericCycle<ContextT>;
230233
using FunctionT = typename ContextT::FunctionT;
234+
using LoopT = typename ContextT::LoopT;
235+
using LoopInfoT = typename ContextT::LoopInfoT;
231236
template <typename> friend class GenericCycle;
232237
template <typename> friend class GenericCycleInfoCompute;
233238

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

278283
/// Methods for debug and self-test.
279284
//@{
280-
#ifndef NDEBUG
281-
bool validateTree() const;
282-
#endif
285+
void verifyCycleNest(bool VerifyFull = false, LoopInfoT *LI = nullptr) const;
286+
void verify(LoopInfoT *LI = nullptr) const;
283287
void print(raw_ostream &Out) const;
284288
void dump() const { print(dbgs()); }
285289
Printable print(const CycleT *Cycle) { return Cycle->print(Context); }

llvm/include/llvm/ADT/GenericSSAContext.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,12 @@ template <typename _FunctionT> class GenericSSAContext {
7777
// a given funciton.
7878
using DominatorTreeT = DominatorTreeBase<BlockT, false>;
7979

80+
// A LoopT is a natural loop in the CFG.
81+
using LoopT = typename SSATraits::LoopT;
82+
83+
// A LoopInfoT contains a forest of natural loops in the CFG.
84+
using LoopInfoT = typename SSATraits::LoopInfoT;
85+
8086
GenericSSAContext() = default;
8187
GenericSSAContext(const FunctionT *F) : F(F) {}
8288

llvm/include/llvm/Analysis/CycleAnalysis.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,17 @@ class CycleAnalysis : public AnalysisInfoMixin<CycleAnalysis> {
6060
// TODO: verify analysis?
6161
};
6262

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

6766
public:
6867
explicit CycleInfoPrinterPass(raw_ostream &OS);
69-
7068
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
69+
static bool isRequired() { return true; }
70+
};
7171

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

llvm/include/llvm/CodeGen/MachineSSAContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
namespace llvm {
2323
class MachineInstr;
2424
class MachineFunction;
25+
class MachineLoop;
26+
class MachineLoopInfo;
2527
class Register;
2628

2729
inline unsigned succ_size(const MachineBasicBlock *BB) {
@@ -39,6 +41,8 @@ template <> struct GenericSSATraits<MachineFunction> {
3941
using ValueRefT = Register;
4042
using ConstValueRefT = Register;
4143
using UseT = MachineOperand;
44+
using LoopT = MachineLoop;
45+
using LoopInfoT = MachineLoopInfo;
4246
};
4347

4448
using MachineSSAContext = GenericSSAContext<MachineFunction>;

llvm/include/llvm/IR/SSAContext.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
namespace llvm {
2222
class BasicBlock;
2323
class Function;
24+
class Loop;
25+
class LoopInfo;
2426
class Instruction;
2527
class Value;
2628

@@ -35,6 +37,8 @@ template <> struct GenericSSATraits<Function> {
3537
using ValueRefT = Value *;
3638
using ConstValueRefT = const Value *;
3739
using UseT = Use;
40+
using LoopT = Loop;
41+
using LoopInfoT = LoopInfo;
3842
};
3943

4044
using SSAContext = GenericSSAContext<Function>;

llvm/lib/Analysis/CycleAnalysis.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "llvm/Analysis/CycleAnalysis.h"
1010
#include "llvm/ADT/GenericCycleImpl.h"
11+
#include "llvm/Analysis/LoopInfo.h"
1112
#include "llvm/IR/CFG.h" // for successors found by ADL in GenericCycleImpl.h
1213
#include "llvm/InitializePasses.h"
1314

@@ -35,6 +36,14 @@ PreservedAnalyses CycleInfoPrinterPass::run(Function &F,
3536
return PreservedAnalyses::all();
3637
}
3738

39+
PreservedAnalyses CycleInfoVerifierPass::run(Function &F,
40+
FunctionAnalysisManager &AM) {
41+
CycleInfo &CI = AM.getResult<CycleAnalysis>(F);
42+
LoopInfo &LI = AM.getResult<LoopAnalysis>(F);
43+
CI.verify(&LI);
44+
return PreservedAnalyses::all();
45+
}
46+
3847
//===----------------------------------------------------------------------===//
3948
// CycleInfoWrapperPass Implementation
4049
//===----------------------------------------------------------------------===//

llvm/lib/CodeGen/MachineCycleAnalysis.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "llvm/CodeGen/MachineCycleAnalysis.h"
1010
#include "llvm/ADT/GenericCycleImpl.h"
11+
#include "llvm/CodeGen/MachineLoopInfo.h"
1112
#include "llvm/CodeGen/MachineRegisterInfo.h"
1213
#include "llvm/CodeGen/MachineSSAContext.h"
1314
#include "llvm/CodeGen/TargetInstrInfo.h"

llvm/lib/IR/CycleInfo.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "llvm/IR/CycleInfo.h"
1010
#include "llvm/ADT/GenericCycleImpl.h"
11+
#include "llvm/Analysis/LoopInfo.h"
1112
#include "llvm/IR/CFG.h"
1213

1314
using namespace llvm;

llvm/lib/Passes/PassRegistry.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ FUNCTION_PASS("typepromotion", TypePromotionPass(TM))
470470
FUNCTION_PASS("unify-loop-exits", UnifyLoopExitsPass())
471471
FUNCTION_PASS("vector-combine", VectorCombinePass())
472472
FUNCTION_PASS("verify", VerifierPass())
473+
FUNCTION_PASS("verify<cycles>", CycleInfoVerifierPass())
473474
FUNCTION_PASS("verify<domtree>", DominatorTreeVerifierPass())
474475
FUNCTION_PASS("verify<loops>", LoopVerifierPass())
475476
FUNCTION_PASS("verify<memoryssa>", MemorySSAVerifierPass())

llvm/test/Analysis/CycleInfo/basic.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
; RUN: opt < %s -disable-output -passes='print<cycles>' 2>&1 | FileCheck %s -check-prefix=CHECK
1+
; RUN: opt < %s -disable-output -passes='verify<cycles>,print<cycles>' 2>&1 | FileCheck %s -check-prefix=CHECK
22

33
define void @empty() {
44
; CHECK-LABEL: CycleInfo for function: empty

llvm/test/Analysis/CycleInfo/unreachable-predecessor.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
; RUN: opt < %s -disable-output -passes='print<cycles>' 2>&1 | FileCheck %s
1+
; RUN: opt < %s -disable-output -passes='verify<cycles>,print<cycles>' 2>&1 | FileCheck %s
22
; CHECK-LABEL: CycleInfo for function: unreachable
33
; CHECK: depth=1: entries(loop.body) loop.latch inner.block
44
define void @unreachable(i32 %n) {

0 commit comments

Comments
 (0)