[PM] Improve the debugging and logging facilities of the CGSCC bits of

the new pass manager.

This adds operator<< overloads for the various bits of the
LazyCallGraph, dump methods for use from the debugger, and debug logging
using them to the CGSCC pass manager.

Having this was essential for debugging the call graph update patch, and
I've extracted what I could from that patch here to minimize the delta.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@273961 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Chandler Carruth 2016-06-27 23:26:08 +00:00
parent 5e49c0d4a2
commit 77e165569e
5 changed files with 125 additions and 39 deletions

View File

@ -67,19 +67,20 @@ template <typename CGSCCPassT>
class ModuleToPostOrderCGSCCPassAdaptor class ModuleToPostOrderCGSCCPassAdaptor
: public PassInfoMixin<ModuleToPostOrderCGSCCPassAdaptor<CGSCCPassT>> { : public PassInfoMixin<ModuleToPostOrderCGSCCPassAdaptor<CGSCCPassT>> {
public: public:
explicit ModuleToPostOrderCGSCCPassAdaptor(CGSCCPassT Pass) explicit ModuleToPostOrderCGSCCPassAdaptor(CGSCCPassT Pass, bool DebugLogging = false)
: Pass(std::move(Pass)) {} : Pass(std::move(Pass)), DebugLogging(DebugLogging) {}
// We have to explicitly define all the special member functions because MSVC // We have to explicitly define all the special member functions because MSVC
// refuses to generate them. // refuses to generate them.
ModuleToPostOrderCGSCCPassAdaptor( ModuleToPostOrderCGSCCPassAdaptor(
const ModuleToPostOrderCGSCCPassAdaptor &Arg) const ModuleToPostOrderCGSCCPassAdaptor &Arg)
: Pass(Arg.Pass) {} : Pass(Arg.Pass), DebugLogging(Arg.DebugLogging) {}
ModuleToPostOrderCGSCCPassAdaptor(ModuleToPostOrderCGSCCPassAdaptor &&Arg) ModuleToPostOrderCGSCCPassAdaptor(ModuleToPostOrderCGSCCPassAdaptor &&Arg)
: Pass(std::move(Arg.Pass)) {} : Pass(std::move(Arg.Pass)), DebugLogging(Arg.DebugLogging) {}
friend void swap(ModuleToPostOrderCGSCCPassAdaptor &LHS, friend void swap(ModuleToPostOrderCGSCCPassAdaptor &LHS,
ModuleToPostOrderCGSCCPassAdaptor &RHS) { ModuleToPostOrderCGSCCPassAdaptor &RHS) {
using std::swap; using std::swap;
swap(LHS.Pass, RHS.Pass); swap(LHS.Pass, RHS.Pass);
swap(LHS.DebugLogging, RHS.DebugLogging);
} }
ModuleToPostOrderCGSCCPassAdaptor & ModuleToPostOrderCGSCCPassAdaptor &
operator=(ModuleToPostOrderCGSCCPassAdaptor RHS) { operator=(ModuleToPostOrderCGSCCPassAdaptor RHS) {
@ -97,8 +98,11 @@ public:
LazyCallGraph &CG = AM.getResult<LazyCallGraphAnalysis>(M); LazyCallGraph &CG = AM.getResult<LazyCallGraphAnalysis>(M);
PreservedAnalyses PA = PreservedAnalyses::all(); PreservedAnalyses PA = PreservedAnalyses::all();
for (LazyCallGraph::RefSCC &OuterC : CG.postorder_ref_sccs()) for (LazyCallGraph::RefSCC &RC : CG.postorder_ref_sccs()) {
for (LazyCallGraph::SCC &C : OuterC) { if (DebugLogging)
dbgs() << "Running an SCC pass across the RefSCC: " << RC << "\n";
for (LazyCallGraph::SCC &C : RC) {
PreservedAnalyses PassPA = Pass.run(C, CGAM); PreservedAnalyses PassPA = Pass.run(C, CGAM);
// We know that the CGSCC pass couldn't have invalidated any other // We know that the CGSCC pass couldn't have invalidated any other
@ -115,6 +119,7 @@ public:
// analyses will eventually occur when the module pass completes. // analyses will eventually occur when the module pass completes.
PA.intersect(std::move(PassPA)); PA.intersect(std::move(PassPA));
} }
}
// By definition we preserve the proxy. This precludes *any* invalidation // By definition we preserve the proxy. This precludes *any* invalidation
// of CGSCC analyses by the proxy, but that's OK because we've taken // of CGSCC analyses by the proxy, but that's OK because we've taken
@ -126,14 +131,15 @@ public:
private: private:
CGSCCPassT Pass; CGSCCPassT Pass;
bool DebugLogging;
}; };
/// \brief A function to deduce a function pass type and wrap it in the /// \brief A function to deduce a function pass type and wrap it in the
/// templated adaptor. /// templated adaptor.
template <typename CGSCCPassT> template <typename CGSCCPassT>
ModuleToPostOrderCGSCCPassAdaptor<CGSCCPassT> ModuleToPostOrderCGSCCPassAdaptor<CGSCCPassT>
createModuleToPostOrderCGSCCPassAdaptor(CGSCCPassT Pass) { createModuleToPostOrderCGSCCPassAdaptor(CGSCCPassT Pass, bool DebugLogging = false) {
return ModuleToPostOrderCGSCCPassAdaptor<CGSCCPassT>(std::move(Pass)); return ModuleToPostOrderCGSCCPassAdaptor<CGSCCPassT>(std::move(Pass), DebugLogging);
} }
extern template class InnerAnalysisManagerProxy<FunctionAnalysisManager, extern template class InnerAnalysisManagerProxy<FunctionAnalysisManager,
@ -159,18 +165,19 @@ template <typename FunctionPassT>
class CGSCCToFunctionPassAdaptor class CGSCCToFunctionPassAdaptor
: public PassInfoMixin<CGSCCToFunctionPassAdaptor<FunctionPassT>> { : public PassInfoMixin<CGSCCToFunctionPassAdaptor<FunctionPassT>> {
public: public:
explicit CGSCCToFunctionPassAdaptor(FunctionPassT Pass) explicit CGSCCToFunctionPassAdaptor(FunctionPassT Pass, bool DebugLogging = false)
: Pass(std::move(Pass)) {} : Pass(std::move(Pass)), DebugLogging(DebugLogging) {}
// We have to explicitly define all the special member functions because MSVC // We have to explicitly define all the special member functions because MSVC
// refuses to generate them. // refuses to generate them.
CGSCCToFunctionPassAdaptor(const CGSCCToFunctionPassAdaptor &Arg) CGSCCToFunctionPassAdaptor(const CGSCCToFunctionPassAdaptor &Arg)
: Pass(Arg.Pass) {} : Pass(Arg.Pass), DebugLogging(Arg.DebugLogging) {}
CGSCCToFunctionPassAdaptor(CGSCCToFunctionPassAdaptor &&Arg) CGSCCToFunctionPassAdaptor(CGSCCToFunctionPassAdaptor &&Arg)
: Pass(std::move(Arg.Pass)) {} : Pass(std::move(Arg.Pass)), DebugLogging(Arg.DebugLogging) {}
friend void swap(CGSCCToFunctionPassAdaptor &LHS, friend void swap(CGSCCToFunctionPassAdaptor &LHS,
CGSCCToFunctionPassAdaptor &RHS) { CGSCCToFunctionPassAdaptor &RHS) {
using std::swap; using std::swap;
swap(LHS.Pass, RHS.Pass); swap(LHS.Pass, RHS.Pass);
swap(LHS.DebugLogging, RHS.DebugLogging);
} }
CGSCCToFunctionPassAdaptor &operator=(CGSCCToFunctionPassAdaptor RHS) { CGSCCToFunctionPassAdaptor &operator=(CGSCCToFunctionPassAdaptor RHS) {
swap(*this, RHS); swap(*this, RHS);
@ -183,6 +190,9 @@ public:
FunctionAnalysisManager &FAM = FunctionAnalysisManager &FAM =
AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C).getManager(); AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C).getManager();
if (DebugLogging)
dbgs() << "Running function passes across an SCC: " << C << "\n";
PreservedAnalyses PA = PreservedAnalyses::all(); PreservedAnalyses PA = PreservedAnalyses::all();
for (LazyCallGraph::Node &N : C) { for (LazyCallGraph::Node &N : C) {
PreservedAnalyses PassPA = Pass.run(N.getFunction(), FAM); PreservedAnalyses PassPA = Pass.run(N.getFunction(), FAM);
@ -211,14 +221,16 @@ public:
private: private:
FunctionPassT Pass; FunctionPassT Pass;
bool DebugLogging;
}; };
/// \brief A function to deduce a function pass type and wrap it in the /// \brief A function to deduce a function pass type and wrap it in the
/// templated adaptor. /// templated adaptor.
template <typename FunctionPassT> template <typename FunctionPassT>
CGSCCToFunctionPassAdaptor<FunctionPassT> CGSCCToFunctionPassAdaptor<FunctionPassT>
createCGSCCToFunctionPassAdaptor(FunctionPassT Pass) { createCGSCCToFunctionPassAdaptor(FunctionPassT Pass, bool DebugLogging = false) {
return CGSCCToFunctionPassAdaptor<FunctionPassT>(std::move(Pass)); return CGSCCToFunctionPassAdaptor<FunctionPassT>(std::move(Pass),
DebugLogging);
} }
} }

View File

@ -48,6 +48,7 @@
#include "llvm/IR/Module.h" #include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h" #include "llvm/IR/PassManager.h"
#include "llvm/Support/Allocator.h" #include "llvm/Support/Allocator.h"
#include "llvm/Support/raw_ostream.h"
#include <iterator> #include <iterator>
#include <utility> #include <utility>
@ -222,6 +223,12 @@ public:
/// Internal helper to remove the edge to the given function. /// Internal helper to remove the edge to the given function.
void removeEdgeInternal(Function &ChildF); void removeEdgeInternal(Function &ChildF);
/// Print the name of this node's function.
friend raw_ostream &operator<<(raw_ostream &OS, const Node &N);
/// Dump the name of this node's function to stderr.
void dump() const;
public: public:
LazyCallGraph &getGraph() const { return *G; } LazyCallGraph &getGraph() const { return *G; }
@ -353,6 +360,15 @@ public:
Nodes.clear(); Nodes.clear();
} }
/// Print a short descrtiption useful for debugging or logging.
///
/// We print the function names in the SCC wrapped in '()'s and skipping
/// the middle functions if there are a large number.
friend raw_ostream &operator<<(raw_ostream &OS, const SCC &C);
/// Dump a short description of this SCC to stderr.
void dump() const;
#ifndef NDEBUG #ifndef NDEBUG
/// Verify invariants about the SCC. /// Verify invariants about the SCC.
/// ///
@ -373,25 +389,15 @@ public:
RefSCC &getOuterRefSCC() const { return *OuterRefSCC; } RefSCC &getOuterRefSCC() const { return *OuterRefSCC; }
/// Short name useful for debugging or logging. /// Provide a short name by printing this SCC to a std::string.
/// ///
/// We use the name of the first function in the SCC to name the SCC for /// This copes with the fact that we don't have a name per-se for an SCC
/// the purposes of debugging and logging. /// while still making the use of this in debugging and logging useful.
std::string getName() const { std::string getName() const {
std::string Name; std::string Name;
int i = 0; raw_string_ostream OS(Name);
for (Node &N : *this) { OS << *this;
if (i > 0) OS.flush();
Name += ", ";
// Elide the inner elements if there are too many.
if (i > 8) {
Name += "..., ";
Name += Nodes.back()->getFunction().getName().str();
break;
}
Name += N.getFunction().getName().str();
++i;
}
return Name; return Name;
} }
}; };
@ -426,6 +432,15 @@ public:
/// formRefSCCFast on the graph itself. /// formRefSCCFast on the graph itself.
RefSCC(LazyCallGraph &G); RefSCC(LazyCallGraph &G);
/// Print a short description useful for debugging or logging.
///
/// We print the SCCs wrapped in '[]'s and skipping the middle SCCs if
/// there are a large number.
friend raw_ostream &operator<<(raw_ostream &OS, const RefSCC &RC);
/// Dump a short description of this RefSCC to stderr.
void dump() const;
#ifndef NDEBUG #ifndef NDEBUG
/// Verify invariants about the RefSCC and all its SCCs. /// Verify invariants about the RefSCC and all its SCCs.
/// ///
@ -477,12 +492,16 @@ public:
/// Test if this RefSCC is a descendant of \a C. /// Test if this RefSCC is a descendant of \a C.
bool isDescendantOf(const RefSCC &C) const; bool isDescendantOf(const RefSCC &C) const;
/// Short name useful for debugging or logging. /// Provide a short name by printing this SCC to a std::string.
/// ///
/// We use the name of the first function in the SCC to name the SCC for /// This copes with the fact that we don't have a name per-se for an SCC
/// the purposes of debugging and logging. /// while still making the use of this in debugging and logging useful.
StringRef getName() const { std::string getName() const {
return begin()->begin()->getFunction().getName(); std::string Name;
raw_string_ostream OS(Name);
OS << *this;
OS.flush();
return Name;
} }
///@{ ///@{

View File

@ -120,6 +120,14 @@ void LazyCallGraph::Node::removeEdgeInternal(Function &Target) {
EdgeIndexMap.erase(IndexMapI); EdgeIndexMap.erase(IndexMapI);
} }
raw_ostream &llvm::operator<<(raw_ostream &OS, const LazyCallGraph::Node &N) {
return OS << N.F.getName();
}
void LazyCallGraph::Node::dump() const {
dbgs() << *this << '\n';
}
LazyCallGraph::LazyCallGraph(Module &M) : NextDFSNumber(0) { LazyCallGraph::LazyCallGraph(Module &M) : NextDFSNumber(0) {
DEBUG(dbgs() << "Building CG for module: " << M.getModuleIdentifier() DEBUG(dbgs() << "Building CG for module: " << M.getModuleIdentifier()
<< "\n"); << "\n");
@ -173,6 +181,28 @@ LazyCallGraph &LazyCallGraph::operator=(LazyCallGraph &&G) {
return *this; return *this;
} }
raw_ostream &llvm::operator<<(raw_ostream &OS, const LazyCallGraph::SCC &C) {
OS << '(';
int i = 0;
for (LazyCallGraph::Node &N : C) {
if (i > 0)
OS << ", ";
// Elide the inner elements if there are too many.
if (i > 8) {
OS << "..., " << *C.Nodes.back();
break;
}
OS << N;
++i;
}
OS << ')';
return OS;
}
void LazyCallGraph::SCC::dump() const {
dbgs() << *this << '\n';
}
#ifndef NDEBUG #ifndef NDEBUG
void LazyCallGraph::SCC::verify() { void LazyCallGraph::SCC::verify() {
assert(OuterRefSCC && "Can't have a null RefSCC!"); assert(OuterRefSCC && "Can't have a null RefSCC!");
@ -194,6 +224,29 @@ void LazyCallGraph::SCC::verify() {
LazyCallGraph::RefSCC::RefSCC(LazyCallGraph &G) : G(&G) {} LazyCallGraph::RefSCC::RefSCC(LazyCallGraph &G) : G(&G) {}
raw_ostream &llvm::operator<<(raw_ostream &OS,
const LazyCallGraph::RefSCC &RC) {
OS << '[';
int i = 0;
for (LazyCallGraph::SCC &C : RC) {
if (i > 0)
OS << ", ";
// Elide the inner elements if there are too many.
if (i > 4) {
OS << "..., " << *RC.SCCs.back();
break;
}
OS << C;
++i;
}
OS << ']';
return OS;
}
void LazyCallGraph::RefSCC::dump() const {
dbgs() << *this << '\n';
}
#ifndef NDEBUG #ifndef NDEBUG
void LazyCallGraph::RefSCC::verify() { void LazyCallGraph::RefSCC::verify() {
assert(G && "Can't have a null graph!"); assert(G && "Can't have a null graph!");

View File

@ -561,7 +561,8 @@ bool PassBuilder::parseCGSCCPassPipeline(CGSCCPassManager &CGPM,
PipelineText = PipelineText.substr(1); PipelineText = PipelineText.substr(1);
// Add the nested pass manager with the appropriate adaptor. // Add the nested pass manager with the appropriate adaptor.
CGPM.addPass(createCGSCCToFunctionPassAdaptor(std::move(NestedFPM))); CGPM.addPass(
createCGSCCToFunctionPassAdaptor(std::move(NestedFPM), DebugLogging));
} else { } else {
// Otherwise try to parse a pass name. // Otherwise try to parse a pass name.
size_t End = PipelineText.find_first_of(",)"); size_t End = PipelineText.find_first_of(",)");
@ -627,8 +628,8 @@ bool PassBuilder::parseModulePassPipeline(ModulePassManager &MPM,
PipelineText = PipelineText.substr(1); PipelineText = PipelineText.substr(1);
// Add the nested pass manager with the appropriate adaptor. // Add the nested pass manager with the appropriate adaptor.
MPM.addPass( MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(NestedCGPM),
createModuleToPostOrderCGSCCPassAdaptor(std::move(NestedCGPM))); DebugLogging));
} else if (PipelineText.startswith("function(")) { } else if (PipelineText.startswith("function(")) {
FunctionPassManager NestedFPM(DebugLogging); FunctionPassManager NestedFPM(DebugLogging);
@ -689,7 +690,7 @@ bool PassBuilder::parsePassPipeline(ModulePassManager &MPM,
DebugLogging) || DebugLogging) ||
!PipelineText.empty()) !PipelineText.empty())
return false; return false;
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM), DebugLogging));
return true; return true;
} }

View File

@ -22,6 +22,7 @@
; CHECK-CGSCC-PASS-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor ; CHECK-CGSCC-PASS-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor
; CHECK-CGSCC-PASS-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}> ; CHECK-CGSCC-PASS-NEXT: Running analysis: InnerAnalysisManagerProxy<{{.*}}>
; CHECK-CGSCC-PASS-NEXT: Running analysis: LazyCallGraphAnalysis ; CHECK-CGSCC-PASS-NEXT: Running analysis: LazyCallGraphAnalysis
; CHECK-CGSCC-PASS-NEXT: Running an SCC pass across the RefSCC: [(foo)]
; CHECK-CGSCC-PASS-NEXT: Starting llvm::LazyCallGraph::SCC pass manager run ; CHECK-CGSCC-PASS-NEXT: Starting llvm::LazyCallGraph::SCC pass manager run
; CHECK-CGSCC-PASS-NEXT: Running pass: NoOpCGSCCPass ; CHECK-CGSCC-PASS-NEXT: Running pass: NoOpCGSCCPass
; CHECK-CGSCC-PASS-NEXT: Finished llvm::LazyCallGraph::SCC pass manager run ; CHECK-CGSCC-PASS-NEXT: Finished llvm::LazyCallGraph::SCC pass manager run