[llvm] b432afc - [CycleAnalysis] Methods to verify cycles and their nesting. (#102300)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 20 02:53:51 PDT 2024
Author: Sameer Sahasrabuddhe
Date: 2024-08-20T15:23:48+05:30
New Revision: b432afc28406b670a58933c2fe56c73e6f85911e
URL: https://github.com/llvm/llvm-project/commit/b432afc28406b670a58933c2fe56c73e6f85911e
DIFF: https://github.com/llvm/llvm-project/commit/b432afc28406b670a58933c2fe56c73e6f85911e.diff
LOG: [CycleAnalysis] Methods to verify cycles and their nesting. (#102300)
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 all cycles, either
invdividually, or as the entire forest. These will be used by future
transforms that modify CycleInfo.
Added:
Modified:
llvm/include/llvm/ADT/GenericCycleImpl.h
llvm/include/llvm/ADT/GenericCycleInfo.h
llvm/include/llvm/ADT/GenericSSAContext.h
llvm/include/llvm/Analysis/CycleAnalysis.h
llvm/include/llvm/CodeGen/MachineSSAContext.h
llvm/include/llvm/IR/SSAContext.h
llvm/lib/Analysis/CycleAnalysis.cpp
llvm/lib/CodeGen/MachineCycleAnalysis.cpp
llvm/lib/IR/CycleInfo.cpp
llvm/lib/Passes/PassRegistry.def
llvm/test/Analysis/CycleInfo/basic.ll
llvm/test/Analysis/CycleInfo/unreachable-predecessor.ll
Removed:
################################################################################
diff --git a/llvm/include/llvm/ADT/GenericCycleImpl.h b/llvm/include/llvm/ADT/GenericCycleImpl.h
index 56d5ba6e060772..dc3d3d3f3fe328 100644
--- a/llvm/include/llvm/ADT/GenericCycleImpl.h
+++ b/llvm/include/llvm/ADT/GenericCycleImpl.h
@@ -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"
@@ -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";
+ llvm_unreachable("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;
@@ -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>
@@ -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.
@@ -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>
diff --git a/llvm/include/llvm/ADT/GenericCycleInfo.h b/llvm/include/llvm/ADT/GenericCycleInfo.h
index b601fc9bae38af..b5d719c6313c43 100644
--- a/llvm/include/llvm/ADT/GenericCycleInfo.h
+++ b/llvm/include/llvm/ADT/GenericCycleInfo.h
@@ -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 =
@@ -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;
@@ -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); }
diff --git a/llvm/include/llvm/ADT/GenericSSAContext.h b/llvm/include/llvm/ADT/GenericSSAContext.h
index 6aa3a8b9b6e0b6..316014cd3c4fee 100644
--- a/llvm/include/llvm/ADT/GenericSSAContext.h
+++ b/llvm/include/llvm/ADT/GenericSSAContext.h
@@ -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) {}
diff --git a/llvm/include/llvm/Analysis/CycleAnalysis.h b/llvm/include/llvm/Analysis/CycleAnalysis.h
index 6b45cece199913..40c42d5495f7c7 100644
--- a/llvm/include/llvm/Analysis/CycleAnalysis.h
+++ b/llvm/include/llvm/Analysis/CycleAnalysis.h
@@ -59,15 +59,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; }
};
diff --git a/llvm/include/llvm/CodeGen/MachineSSAContext.h b/llvm/include/llvm/CodeGen/MachineSSAContext.h
index b70450c19f283a..a5d07d41fce864 100644
--- a/llvm/include/llvm/CodeGen/MachineSSAContext.h
+++ b/llvm/include/llvm/CodeGen/MachineSSAContext.h
@@ -22,6 +22,8 @@
namespace llvm {
class MachineInstr;
class MachineFunction;
+class MachineLoop;
+class MachineLoopInfo;
class Register;
inline unsigned succ_size(const MachineBasicBlock *BB) {
@@ -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>;
diff --git a/llvm/include/llvm/IR/SSAContext.h b/llvm/include/llvm/IR/SSAContext.h
index d0da5e222a8e63..c665672cdc8dca 100644
--- a/llvm/include/llvm/IR/SSAContext.h
+++ b/llvm/include/llvm/IR/SSAContext.h
@@ -21,6 +21,8 @@
namespace llvm {
class BasicBlock;
class Function;
+class Loop;
+class LoopInfo;
class Instruction;
class Value;
@@ -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>;
diff --git a/llvm/lib/Analysis/CycleAnalysis.cpp b/llvm/lib/Analysis/CycleAnalysis.cpp
index 4d7980af2beae9..85c30da02b7d47 100644
--- a/llvm/lib/Analysis/CycleAnalysis.cpp
+++ b/llvm/lib/Analysis/CycleAnalysis.cpp
@@ -8,6 +8,7 @@
#include "llvm/Analysis/CycleAnalysis.h"
#include "llvm/ADT/GenericCycleImpl.h"
+#include "llvm/Analysis/LoopInfo.h"
#include "llvm/IR/CFG.h" // for successors found by ADL in GenericCycleImpl.h
#include "llvm/InitializePasses.h"
@@ -35,6 +36,14 @@ PreservedAnalyses CycleInfoPrinterPass::run(Function &F,
return PreservedAnalyses::all();
}
+PreservedAnalyses CycleInfoVerifierPass::run(Function &F,
+ FunctionAnalysisManager &AM) {
+ CycleInfo &CI = AM.getResult<CycleAnalysis>(F);
+ LoopInfo &LI = AM.getResult<LoopAnalysis>(F);
+ CI.verify(&LI);
+ return PreservedAnalyses::all();
+}
+
//===----------------------------------------------------------------------===//
// CycleInfoWrapperPass Implementation
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/CodeGen/MachineCycleAnalysis.cpp b/llvm/lib/CodeGen/MachineCycleAnalysis.cpp
index 57f7a098ac1757..5e1e56d59d5e0d 100644
--- a/llvm/lib/CodeGen/MachineCycleAnalysis.cpp
+++ b/llvm/lib/CodeGen/MachineCycleAnalysis.cpp
@@ -8,6 +8,7 @@
#include "llvm/CodeGen/MachineCycleAnalysis.h"
#include "llvm/ADT/GenericCycleImpl.h"
+#include "llvm/CodeGen/MachineLoopInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/MachineSSAContext.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
diff --git a/llvm/lib/IR/CycleInfo.cpp b/llvm/lib/IR/CycleInfo.cpp
index a9b9129f24f098..4a87380dac8595 100644
--- a/llvm/lib/IR/CycleInfo.cpp
+++ b/llvm/lib/IR/CycleInfo.cpp
@@ -8,6 +8,7 @@
#include "llvm/IR/CycleInfo.h"
#include "llvm/ADT/GenericCycleImpl.h"
+#include "llvm/Analysis/LoopInfo.h"
#include "llvm/IR/CFG.h"
using namespace llvm;
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 6b5e1cf83c4698..d6067089c6b5c1 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -475,6 +475,7 @@ FUNCTION_PASS("typepromotion", TypePromotionPass(TM))
FUNCTION_PASS("unify-loop-exits", UnifyLoopExitsPass())
FUNCTION_PASS("vector-combine", VectorCombinePass())
FUNCTION_PASS("verify", VerifierPass())
+FUNCTION_PASS("verify<cycles>", CycleInfoVerifierPass())
FUNCTION_PASS("verify<domtree>", DominatorTreeVerifierPass())
FUNCTION_PASS("verify<loops>", LoopVerifierPass())
FUNCTION_PASS("verify<memoryssa>", MemorySSAVerifierPass())
diff --git a/llvm/test/Analysis/CycleInfo/basic.ll b/llvm/test/Analysis/CycleInfo/basic.ll
index f8326f13589146..e459b0c317f948 100644
--- a/llvm/test/Analysis/CycleInfo/basic.ll
+++ b/llvm/test/Analysis/CycleInfo/basic.ll
@@ -1,4 +1,4 @@
-; RUN: opt < %s -disable-output -passes='print<cycles>' 2>&1 | FileCheck %s -check-prefix=CHECK
+; RUN: opt < %s -disable-output -passes='verify<cycles>,print<cycles>' 2>&1 | FileCheck %s -check-prefix=CHECK
define void @empty() {
; CHECK-LABEL: CycleInfo for function: empty
diff --git a/llvm/test/Analysis/CycleInfo/unreachable-predecessor.ll b/llvm/test/Analysis/CycleInfo/unreachable-predecessor.ll
index 36a2115bd7a943..3655095ff6d827 100644
--- a/llvm/test/Analysis/CycleInfo/unreachable-predecessor.ll
+++ b/llvm/test/Analysis/CycleInfo/unreachable-predecessor.ll
@@ -1,4 +1,4 @@
-; RUN: opt < %s -disable-output -passes='print<cycles>' 2>&1 | FileCheck %s
+; RUN: opt < %s -disable-output -passes='verify<cycles>,print<cycles>' 2>&1 | FileCheck %s
; CHECK-LABEL: CycleInfo for function: unreachable
; CHECK: depth=1: entries(loop.body) loop.latch inner.block
define void @unreachable(i32 %n) {
More information about the llvm-commits
mailing list