-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[PassManager] Add pretty stack frames #96078
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
Conversation
@llvm/pr-subscribers-llvm-ir @llvm/pr-subscribers-llvm-analysis Author: Nikita Popov (nikic) ChangesIn NewPM pass managers, add a "pretty stack frame" that tells you which pass crashed while running which function:
While the crashing pass is usually evident from the stack trace, knowing which function caused it is quite handy. Similar functionality existed in the LegacyPM. I'm not sure whether the way I did this is the best way to do it -- took me a while to figure out the correct combination of C++ template magic to make this work. I would have liked to print the pipeline string instead of pass name (so we also get pass options), but the necessary information for that doesn't seem to be available. Full diff: https://github.com/llvm/llvm-project/pull/96078.diff 7 Files Affected:
diff --git a/llvm/include/llvm/Analysis/CGSCCPassManager.h b/llvm/include/llvm/Analysis/CGSCCPassManager.h
index b19d53621ac86..4d79493461b4e 100644
--- a/llvm/include/llvm/Analysis/CGSCCPassManager.h
+++ b/llvm/include/llvm/Analysis/CGSCCPassManager.h
@@ -131,6 +131,10 @@ PassManager<LazyCallGraph::SCC, CGSCCAnalysisManager, LazyCallGraph &,
CGSCCUpdateResult &>::run(LazyCallGraph::SCC &InitialC,
CGSCCAnalysisManager &AM,
LazyCallGraph &G, CGSCCUpdateResult &UR);
+template <>
+void PassManager<LazyCallGraph::SCC, CGSCCAnalysisManager, LazyCallGraph &,
+ CGSCCUpdateResult &>::StackTraceEntry::print(raw_ostream &OS)
+ const;
extern template class PassManager<LazyCallGraph::SCC, CGSCCAnalysisManager,
LazyCallGraph &, CGSCCUpdateResult &>;
diff --git a/llvm/include/llvm/CodeGen/MachinePassManager.h b/llvm/include/llvm/CodeGen/MachinePassManager.h
index 7d15664fbe754..780f8308dfcb6 100644
--- a/llvm/include/llvm/CodeGen/MachinePassManager.h
+++ b/llvm/include/llvm/CodeGen/MachinePassManager.h
@@ -238,6 +238,11 @@ template <>
PreservedAnalyses
PassManager<MachineFunction>::run(MachineFunction &,
AnalysisManager<MachineFunction> &);
+
+template <>
+void PassManager<MachineFunction>::StackTraceEntry::print(
+ raw_ostream &OS) const;
+
extern template class PassManager<MachineFunction>;
/// Convenience typedef for a pass manager over functions.
diff --git a/llvm/include/llvm/IR/PassManager.h b/llvm/include/llvm/IR/PassManager.h
index d701481202f8d..5efaa3f66ac47 100644
--- a/llvm/include/llvm/IR/PassManager.h
+++ b/llvm/include/llvm/IR/PassManager.h
@@ -48,6 +48,7 @@
#include "llvm/IR/PassInstrumentation.h"
#include "llvm/IR/PassManagerInternal.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/TimeProfiler.h"
#include "llvm/Support/TypeName.h"
#include <cassert>
@@ -171,6 +172,20 @@ template <typename IRUnitT,
typename... ExtraArgTs>
class PassManager : public PassInfoMixin<
PassManager<IRUnitT, AnalysisManagerT, ExtraArgTs...>> {
+ using PassConceptT =
+ detail::PassConcept<IRUnitT, AnalysisManagerT, ExtraArgTs...>;
+
+ class StackTraceEntry : public PrettyStackTraceEntry {
+ PassConceptT &Pass;
+ IRUnitT &IR;
+
+ public:
+ explicit StackTraceEntry(PassConceptT &Pass, IRUnitT &IR)
+ : Pass(Pass), IR(IR) {}
+
+ void print(raw_ostream &OS) const override;
+ };
+
public:
/// Construct a pass manager.
explicit PassManager() = default;
@@ -221,6 +236,7 @@ class PassManager : public PassInfoMixin<
if (!PI.runBeforePass<IRUnitT>(*Pass, IR))
continue;
+ StackTraceEntry Entry(*Pass, IR);
PreservedAnalyses PassPA = Pass->run(IR, AM, ExtraArgs...);
// Update the analysis manager as each pass runs and potentially
@@ -271,17 +287,20 @@ class PassManager : public PassInfoMixin<
static bool isRequired() { return true; }
protected:
- using PassConceptT =
- detail::PassConcept<IRUnitT, AnalysisManagerT, ExtraArgTs...>;
-
std::vector<std::unique_ptr<PassConceptT>> Passes;
};
+template <>
+void PassManager<Module>::StackTraceEntry::print(raw_ostream &OS) const;
+
extern template class PassManager<Module>;
/// Convenience typedef for a pass manager over modules.
using ModulePassManager = PassManager<Module>;
+template <>
+void PassManager<Function>::StackTraceEntry::print(raw_ostream &OS) const;
+
extern template class PassManager<Function>;
/// Convenience typedef for a pass manager over functions.
diff --git a/llvm/lib/Analysis/CGSCCPassManager.cpp b/llvm/lib/Analysis/CGSCCPassManager.cpp
index 2ed1d98f80068..c2386e6ab95c4 100644
--- a/llvm/lib/Analysis/CGSCCPassManager.cpp
+++ b/llvm/lib/Analysis/CGSCCPassManager.cpp
@@ -1193,3 +1193,8 @@ LazyCallGraph::SCC &llvm::updateCGAndAnalysisManagerForCGSCCPass(
return updateCGAndAnalysisManagerForPass(G, InitialC, N, AM, UR, FAM,
/* FunctionPass */ false);
}
+
+template <>
+void CGSCCPassManager::StackTraceEntry::print(raw_ostream &OS) const {
+ OS << "Running pass \"" << Pass.name() << "\" on cgscc\n";
+}
diff --git a/llvm/lib/CodeGen/MachinePassManager.cpp b/llvm/lib/CodeGen/MachinePassManager.cpp
index 6d540808d4cc9..41eae9491fef9 100644
--- a/llvm/lib/CodeGen/MachinePassManager.cpp
+++ b/llvm/lib/CodeGen/MachinePassManager.cpp
@@ -163,3 +163,10 @@ PreservedAnalyses llvm::getMachineFunctionPassPreservedAnalyses() {
PA.template preserveSet<AllAnalysesOn<Function>>();
return PA;
}
+
+template <>
+void PassManager<MachineFunction>::StackTraceEntry::print(
+ raw_ostream &OS) const {
+ OS << "Running pass \"" << Pass.name() << "\" on machine function \""
+ << IR.getName() << "\"\n";
+}
diff --git a/llvm/lib/IR/PassManager.cpp b/llvm/lib/IR/PassManager.cpp
index cbddf3dfb056c..6f43fba516adf 100644
--- a/llvm/lib/IR/PassManager.cpp
+++ b/llvm/lib/IR/PassManager.cpp
@@ -13,6 +13,7 @@
using namespace llvm;
namespace llvm {
+
// Explicit template instantiations and specialization defininitions for core
// template typedefs.
template class AllAnalysesOn<Module>;
@@ -144,6 +145,18 @@ PreservedAnalyses ModuleToFunctionPassAdaptor::run(Module &M,
return PA;
}
+template <>
+void PassManager<Module>::StackTraceEntry::print(raw_ostream &OS) const {
+ OS << "Running pass \"" << Pass.name() << "\" on module \"" << IR.getName()
+ << "\"\n";
+}
+
+template <>
+void PassManager<Function>::StackTraceEntry::print(raw_ostream &OS) const {
+ OS << "Running pass \"" << Pass.name() << "\" on function \"" << IR.getName()
+ << "\"\n";
+}
+
AnalysisSetKey CFGAnalyses::SetKey;
AnalysisSetKey PreservedAnalyses::AllAnalysesKey;
diff --git a/llvm/unittests/IR/PassManagerTest.cpp b/llvm/unittests/IR/PassManagerTest.cpp
index a6487169224c2..fb1ae17b9ed50 100644
--- a/llvm/unittests/IR/PassManagerTest.cpp
+++ b/llvm/unittests/IR/PassManagerTest.cpp
@@ -1072,3 +1072,9 @@ TEST_F(PassManagerTest, ModulePassMissedModuleAnalysisInvalidation) {
#endif
}
+
+template <>
+void PassManager<Function, CustomizedAnalysisManager, int,
+ int &>::StackTraceEntry::print(raw_ostream &OS) const {
+ // Dummy
+}
|
I originally tried to improve this in 128745c which did things a bit more complicated fashion. That approach turned out to be relatively expensive and was reverted and I never had time to get back to it. Would be good to check the compile-time impact just to be sure |
Looks like there is close to no impact: https://llvm-compile-time-tracker.com/compare.php?from=60984f5be90d3f0378d739792adbd4c5e3dcc0e2&to=3d48d4932284fd29d0ede70f0ea6834a7c204d5e&stat=instructions:u |
I just realized that this is available via PI -> PIC, just currently not exposed in the API. I think I'll change this PR to make use of that. |
It's only available when |
8317de2
to
2aaf753
Compare
Initializing this map is somewhat expensive (especially for O0), so we currently only do it if certain flags are used. I would like to make use of it for crash dumps (llvm#96078), where I don't know in advance whether it will be needed or not. This patch changes the initialization to a lazy approach, where a callback is registered that does the actual initialization. The callbacks will be run the first time the pass name is requested.
Always initializing the map does have measurable overhead: http://llvm-compile-time-tracker.com/compare.php?from=36c6632eb43bf67e19c8a6a21981cf66e06389b4&to=22e3ed4c5dfd93dcc3684410515ee053ace2edf3&stat=instructions:u I've put up #96321 to use lazy initialization instead. |
Initializing this map is somewhat expensive (especially for O0), so we currently only do it if certain flags are used. I would like to make use of it for crash dumps (#96078), where we don't know in advance whether it will be needed or not. This patch changes the initialization to a lazy approach, where a callback is registered that does the actual initialization. The callbacks will be run the first time the pass name is requested. This way there is no compile-time impact if the mapping is not used.
2aaf753
to
b218927
Compare
Okay, I think this is ready for review now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM assuming compile-time impact is fine, thanks!
b218927
to
510a920
Compare
…lvm#96321) Use [=] instead of [PIC] capture. On Windows, the "this" uses inside decltype result in an error otherwise. We can't explicitly capture "this", because it would result in warnings for the cases where it isn't used. My previous attempt to fix this used [&], which obviously causes lifetime issues instead... ----- Initializing this map is somewhat expensive (especially for O0), so we currently only do it if certain flags are used. I would like to make use of it for crash dumps (llvm#96078), where we don't know in advance whether it will be needed or not. This patch changes the initialization to a lazy approach, where a callback is registered that does the actual initialization. The callbacks will be run the first time the pass name is requested. This way there is no compile-time impact if the mapping is not used.
…96321) (#96462) On MSVC the `this` uses inside `decltype` require a lambda capture. On clang they result in an unused capture warning instead. Add the capture and suppress the warning with `(void)this`. ----- Initializing this map is somewhat expensive (especially for O0), so we currently only do it if certain flags are used. I would like to make use of it for crash dumps (#96078), where we don't know in advance whether it will be needed or not. This patch changes the initialization to a lazy approach, where a callback is registered that does the actual initialization. The callbacks will be run the first time the pass name is requested. This way there is no compile-time impact if the mapping is not used.
In NewPM pass managers, add a "pretty stack frame" that tells you which pass crashed while running which function: ``` Stack dump: 0. Program arguments: build/bin/opt -S -passes=instcombine llvm/test/Transforms/InstCombine/vec_shuffle.ll 1. Running pass "ModuleToFunctionPassAdaptor" on module "llvm/test/Transforms/InstCombine/vec_shuffle.ll" 2. Running pass "InstCombinePass" on function "test16a" ``` While the crashing pass is usually evident from the stack trace, knowing which function caused it is quite handy. Similar functionality existed in the LegacyPM.
510a920
to
164f203
Compare
@@ -224,11 +224,21 @@ class PassManager : public PassInfoMixin< | |||
std::vector<std::unique_ptr<PassConceptT>> Passes; | |||
}; | |||
|
|||
template <typename IRUnitT> | |||
void printIRUnitNameForStackTrace(raw_ostream &OS, const IRUnitT &IR); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we put these in PassManagerImpl.h?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In theory, no, because other places using PassManagerImpl.h also need to specialize these for other types. In practice, yes, we could, because all non-Module/Function users actually specialize the PassManager::run implementation (and as such currently aren't part of the stack trace).
Architecturally, I think putting them in PassManager.h is correct, though possibly this whole implementation is unnecessarily general given that we only end up using it for Module/Function right now.
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/95/builds/530 Here is the relevant piece of the build log for the reference:
|
In NewPM pass managers, add a "pretty stack frame" that tells you which pass crashed while running which function. For example `opt -O3 -passes-ep-peephole=trigger-crash-function test.ll` will print something like this: ``` Stack dump: 0. Program arguments: build/bin/opt -S -O3 -passes-ep-peephole=trigger-crash-function test.ll 1. Running pass "function<eager-inv>(mem2reg,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,trigger-crash-function,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>)" on module "test.ll" 2. Running pass "trigger-crash-function" on function "fshl_concat_i8_i8" ``` While the crashing pass is usually evident from the stack trace, this also shows which function triggered the crash, as well as the pipeline string for the pass (including options). Similar functionality existed in the LegacyPM.
Initializing this map is somewhat expensive (especially for O0), so we currently only do it if certain flags are used. I would like to make use of it for crash dumps (llvm#96078), where we don't know in advance whether it will be needed or not. This patch changes the initialization to a lazy approach, where a callback is registered that does the actual initialization. The callbacks will be run the first time the pass name is requested. This way there is no compile-time impact if the mapping is not used.
…lvm#96321) (llvm#96462) On MSVC the `this` uses inside `decltype` require a lambda capture. On clang they result in an unused capture warning instead. Add the capture and suppress the warning with `(void)this`. ----- Initializing this map is somewhat expensive (especially for O0), so we currently only do it if certain flags are used. I would like to make use of it for crash dumps (llvm#96078), where we don't know in advance whether it will be needed or not. This patch changes the initialization to a lazy approach, where a callback is registered that does the actual initialization. The callbacks will be run the first time the pass name is requested. This way there is no compile-time impact if the mapping is not used.
In NewPM pass managers, add a "pretty stack frame" that tells you which pass crashed while running which function. For example `opt -O3 -passes-ep-peephole=trigger-crash-function test.ll` will print something like this: ``` Stack dump: 0. Program arguments: build/bin/opt -S -O3 -passes-ep-peephole=trigger-crash-function test.ll 1. Running pass "function<eager-inv>(mem2reg,instcombine<max-iterations=1;no-use-loop-info;no-verify-fixpoint>,trigger-crash-function,simplifycfg<bonus-inst-threshold=1;no-forward-switch-cond;switch-range-to-icmp;no-switch-to-lookup;keep-loops;no-hoist-common-insts;no-sink-common-insts;speculate-blocks;simplify-cond-branch>)" on module "test.ll" 2. Running pass "trigger-crash-function" on function "fshl_concat_i8_i8" ``` While the crashing pass is usually evident from the stack trace, this also shows which function triggered the crash, as well as the pipeline string for the pass (including options). Similar functionality existed in the LegacyPM.
In NewPM pass managers, add a "pretty stack frame" that tells you which pass crashed while running which function.
For example
opt -O3 -passes-ep-peephole=trigger-crash-function test.ll
will print something like this:While the crashing pass is usually evident from the stack trace, this also shows which function triggered the crash, as well as the pipeline string for the pass (including options).
Similar functionality existed in the LegacyPM.
I'm not sure whether the way I did this is the best way to do it -- took me a while to figure out the correct combination of C++ template magic to make this work.