diff --git a/llvm/include/llvm/IR/PassManager.h b/llvm/include/llvm/IR/PassManager.h --- a/llvm/include/llvm/IR/PassManager.h +++ b/llvm/include/llvm/IR/PassManager.h @@ -46,6 +46,7 @@ #include "llvm/IR/Module.h" #include "llvm/IR/PassInstrumentation.h" #include "llvm/IR/PassManagerInternal.h" +#include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/TypeName.h" #include @@ -53,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -451,6 +453,34 @@ // header. class PassInstrumentationAnalysis; +class MachineFunction; +class Loop; + +/// Add stack entry with the pass name and the IR unit it runs on. +class NewPassManagerPrettyStackEntry : public PrettyStackTraceEntry { + StringRef PassName; + Module *M = nullptr; + MachineFunction *MF = nullptr; + Value *V = nullptr; + StringRef LoopName = ""; + std::string CGSCCName = ""; + +public: + NewPassManagerPrettyStackEntry(StringRef PassName, Module &M) + : PassName(PassName), M(&M) {} + NewPassManagerPrettyStackEntry(StringRef PassName, MachineFunction &MF) + : PassName(PassName), MF(&MF) {} + NewPassManagerPrettyStackEntry(StringRef PassName, Value &V) + : PassName(PassName), V(&V) {} + NewPassManagerPrettyStackEntry(StringRef PassName, StringRef LoopName) + : PassName(PassName), LoopName(LoopName) {} + NewPassManagerPrettyStackEntry(StringRef PassName, std::string CGSCCName) + : PassName(PassName), CGSCCName(CGSCCName) {} + + /// print - Emit information about this stack frame to OS. + void print(raw_ostream &OS) const override; +}; + /// Manages a sequence of passes over a particular unit of IR. /// /// A pass manager contains a sequence of passes to run over a particular unit @@ -519,6 +549,7 @@ PreservedAnalyses PassPA; { TimeTraceScope TimeScope(P->name(), IR.getName()); + NewPassManagerPrettyStackEntry StackEntry(P->name(), IR); PassPA = P->run(IR, AM, ExtraArgs...); } diff --git a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h --- a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h +++ b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h @@ -395,6 +395,7 @@ PreservedAnalyses PA; { TimeTraceScope TimeScope(Pass->name(), IR.getName()); + NewPassManagerPrettyStackEntry StackEntry(Pass->name(), L.getName()); PA = Pass->run(IR, AM, AR, U); } diff --git a/llvm/lib/Analysis/CGSCCPassManager.cpp b/llvm/lib/Analysis/CGSCCPassManager.cpp --- a/llvm/lib/Analysis/CGSCCPassManager.cpp +++ b/llvm/lib/Analysis/CGSCCPassManager.cpp @@ -87,6 +87,7 @@ PreservedAnalyses PassPA; { TimeTraceScope TimeScope(Pass->name()); + NewPassManagerPrettyStackEntry StackEntry(Pass->name(), C->getName()); PassPA = Pass->run(*C, AM, G, UR); } diff --git a/llvm/lib/IR/PassManager.cpp b/llvm/lib/IR/PassManager.cpp --- a/llvm/lib/IR/PassManager.cpp +++ b/llvm/lib/IR/PassManager.cpp @@ -124,6 +124,7 @@ PreservedAnalyses PassPA; { TimeTraceScope TimeScope(Pass->name(), F.getName()); + NewPassManagerPrettyStackEntry StackEntry(Pass->name(), F); PassPA = Pass->run(F, FAM); } @@ -152,3 +153,43 @@ AnalysisSetKey CFGAnalyses::SetKey; AnalysisSetKey PreservedAnalyses::AllAnalysesKey; + +void NewPassManagerPrettyStackEntry::print(raw_ostream &OS) const { + OS << "Running pass '" << PassName << "'"; + + if (M) { + OS << " on module '" << M->getModuleIdentifier() << "'.\n"; + return; + } + + if (MF) { + // Printing the machine function's name would require pulling in + // llvm/Codegen/MachineFunction.h + OS << " on machine function \n"; + return; + } + + if (!LoopName.empty()) { + OS << " on loop '" << LoopName << "'.\n"; + return; + } + + if (!CGSCCName.empty()) { + OS << " on CGSCC '" << CGSCCName << "'.\n"; + return; + } + + assert(V && "Either M, MF or V must be set"); + + OS << " on "; + if (isa(V)) + OS << "function"; + else if (isa(V)) + OS << "basic block"; + else + OS << "value"; + + OS << " '"; + V->printAsOperand(OS, /*PrintType=*/false); + OS << "'\n"; +} diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -374,6 +374,31 @@ } // namespace +/// Passes to test crash handling. +/// DO NOT USE THIS EXCEPT FOR TESTING! +struct CrashingFunctionPass : public PassInfoMixin { + PreservedAnalyses run(Function &, FunctionAnalysisManager &) { + llvm_unreachable(""); + } +}; +struct CrashingModulePass : public PassInfoMixin { + PreservedAnalyses run(Module &, ModuleAnalysisManager &) { + llvm_unreachable(""); + } +}; +struct CrashingLoopPass : public PassInfoMixin { + PreservedAnalyses run(Loop &, LoopAnalysisManager &, + LoopStandardAnalysisResults &, LPMUpdater &) { + llvm_unreachable(""); + } +}; +struct CrashingCGSCCPass : public PassInfoMixin { + PreservedAnalyses run(LazyCallGraph::SCC &, CGSCCAnalysisManager &, + LazyCallGraph &, CGSCCUpdateResult &) { + llvm_unreachable(""); + } +}; + PassBuilder::PassBuilder(TargetMachine *TM, PipelineTuningOptions PTO, Optional PGOOpt, PassInstrumentationCallbacks *PIC) diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -50,6 +50,7 @@ MODULE_PASS("cg-profile", CGProfilePass()) MODULE_PASS("check-debugify", NewPMCheckDebugifyPass()) MODULE_PASS("constmerge", ConstantMergePass()) +MODULE_PASS("crash-module", CrashingModulePass()) MODULE_PASS("cross-dso-cfi", CrossDSOCFIPass()) MODULE_PASS("deadargelim", DeadArgumentEliminationPass()) MODULE_PASS("debugify", NewPMDebugifyPass()) @@ -161,6 +162,7 @@ #define CGSCC_PASS(NAME, CREATE_PASS) #endif CGSCC_PASS("argpromotion", ArgumentPromotionPass()) +CGSCC_PASS("crash-cgscc", CrashingCGSCCPass()) CGSCC_PASS("invalidate", InvalidateAllAnalysesPass()) CGSCC_PASS("function-attrs", PostOrderFunctionAttrsPass()) CGSCC_PASS("attributor-cgscc", AttributorCGSCCPass()) @@ -251,6 +253,7 @@ FUNCTION_PASS("coro-elide", CoroElidePass()) FUNCTION_PASS("coro-cleanup", CoroCleanupPass()) FUNCTION_PASS("correlated-propagation", CorrelatedValuePropagationPass()) +FUNCTION_PASS("crash-function", CrashingFunctionPass()) FUNCTION_PASS("dce", DCEPass()) FUNCTION_PASS("dfa-jump-threading", DFAJumpThreadingPass()) FUNCTION_PASS("div-rem-pairs", DivRemPairsPass()) @@ -481,6 +484,7 @@ #define LOOP_PASS(NAME, CREATE_PASS) #endif LOOP_PASS("canon-freeze", CanonicalizeFreezeInLoopsPass()) +LOOP_PASS("crash-loop", CrashingLoopPass()) LOOP_PASS("dot-ddg", DDGDotPrinterPass()) LOOP_PASS("invalidate", InvalidateAllAnalysesPass()) LOOP_PASS("licm", LICMPass()) diff --git a/llvm/lib/Transforms/Scalar/LoopPassManager.cpp b/llvm/lib/Transforms/Scalar/LoopPassManager.cpp --- a/llvm/lib/Transforms/Scalar/LoopPassManager.cpp +++ b/llvm/lib/Transforms/Scalar/LoopPassManager.cpp @@ -294,6 +294,7 @@ PreservedAnalyses PassPA; { TimeTraceScope TimeScope(Pass->name()); + NewPassManagerPrettyStackEntry StackEntry(Pass->name(), L->getName()); PassPA = Pass->run(*L, LAM, LAR, Updater); } diff --git a/llvm/test/Other/crash-cgscc.ll b/llvm/test/Other/crash-cgscc.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/crash-cgscc.ll @@ -0,0 +1,14 @@ +; REQUIRES: asserts + +; RUN: not --crash opt -passes=crash-cgscc %s 2> %t +; RUN: FileCheck --input-file=%t %s + +; CHECK: Stack dump: +; CHECK-NEXT: 0. Program arguments: +; CHECK-NEXT: 1. Running pass 'ModuleToPostOrderCGSCCPassAdaptor' on module +; CHECK-NEXT: 2. Running pass 'CrashingCGSCCPass' on CGSCC '(foo)'. +; CHECK-NEXT: Stack dump + +define void @foo() { + ret void +} diff --git a/llvm/test/Other/crash-function.ll b/llvm/test/Other/crash-function.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/crash-function.ll @@ -0,0 +1,15 @@ +; REQUIRES: asserts + +; RUN: not --crash opt -passes=crash-function %s 2> %t +; RUN: FileCheck --input-file=%t %s + +; CHECK: Stack dump: +; CHECK-NEXT: 0. Program arguments: +; CHECK-NEXT: 1. Running pass 'ModuleToFunctionPassAdaptor' on module +; CHECK-NEXT: 2. Running pass 'PassManager' on function '@foo' +; CHECK-NEXT: 3. Running pass 'CrashingFunctionPass' on function '@foo' +; CHECK-NEXT: Stack dump + +define void @foo() { + ret void +} diff --git a/llvm/test/Other/crash-loop.ll b/llvm/test/Other/crash-loop.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/crash-loop.ll @@ -0,0 +1,21 @@ +; REQUIRES: asserts + +; RUN: not --crash opt -passes=crash-loop %s 2> %t +; RUN: FileCheck --input-file=%t %s + +; CHECK: Stack dump: +; CHECK-NEXT: 0. Program arguments: +; CHECK-NEXT: 1. Running pass 'ModuleToFunctionPassAdaptor' on module +; CHECK-NEXT: 2. Running pass 'PassManager' on function '@foo' +; CHECK-NEXT: 3. Running pass 'FunctionToLoopPassAdaptor' on function '@foo' +; CHECK-NEXT: 4. Running pass 'PassManager' on loop 'loop.header'. +; CHECK-NEXT: 5. Running pass 'CrashingLoopPass' on loop 'loop.header'. +; CHECK-NEXT: Stack dump + +define void @foo() { +entry: + br label %loop.header + +loop.header: + br label %loop.header +} diff --git a/llvm/test/Other/crash-module.ll b/llvm/test/Other/crash-module.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/crash-module.ll @@ -0,0 +1,13 @@ +; REQUIRES: asserts + +; RUN: not --crash opt -passes=crash-module %s 2> %t +; RUN: FileCheck --input-file=%t %s + +; CHECK: Stack dump: +; CHECK-NEXT: 0. Program arguments: +; CHECK-NEXT: 1. Running pass 'CrashingModulePass' on module +; CHECK-NEXT: Stack dump + +define void @foo() { + ret void +}