[PassManager] Add pretty stack entries before P->run() call.
authorFlorian Hahn <flo@fhahn.com>
Wed, 9 Mar 2022 13:01:09 +0000 (13:01 +0000)
committerFlorian Hahn <flo@fhahn.com>
Wed, 9 Mar 2022 13:01:09 +0000 (13:01 +0000)
This patch adds PrettyStackEntries before running passes. The entries
include the pass name and the IR unit the pass runs on.

The information is used the print additional information when a pass
crashes, including the name and a reference to the IR unit on which it
crashed. This is similar to the behavior of the legacy pass manager.

The improved stack trace now includes:

Stack dump:
0. Program arguments: bin/opt -loop-vectorize -force-vector-width=4 crash.ll
1. Running pass 'ModuleToFunctionPassAdaptor' on module 'crash.ll'
2. Running pass 'LoopVectorizePass' on function '@a'

Reviewed By: aeubanks

Differential Revision: https://reviews.llvm.org/D120993

llvm/include/llvm/IR/PassManager.h
llvm/include/llvm/Transforms/Scalar/LoopPassManager.h
llvm/lib/Analysis/CGSCCPassManager.cpp
llvm/lib/IR/PassManager.cpp
llvm/lib/Passes/PassBuilder.cpp
llvm/lib/Passes/PassRegistry.def
llvm/lib/Transforms/Scalar/LoopPassManager.cpp
llvm/test/Other/crash-cgscc.ll [new file with mode: 0644]
llvm/test/Other/crash-function.ll [new file with mode: 0644]
llvm/test/Other/crash-loop.ll [new file with mode: 0644]
llvm/test/Other/crash-module.ll [new file with mode: 0644]

index 12f9052..4f7bfdd 100644 (file)
@@ -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 <cassert>
@@ -53,6 +54,7 @@
 #include <iterator>
 #include <list>
 #include <memory>
+#include <string>
 #include <tuple>
 #include <type_traits>
 #include <utility>
@@ -451,6 +453,50 @@ getAnalysisResult(AnalysisManager<IRUnitT, AnalysisArgTs...> &AM, IRUnitT &IR,
 // 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 {
+  enum class EntryTy {
+    Module,
+    CGSCC,
+    MachineFunction,
+    Loop,
+    Value,
+  };
+
+  union {
+    Module *M;
+    MachineFunction *MF;
+    Value *V;
+    StringRef LoopName;
+    std::string CGSCCName;
+  };
+  StringRef PassName;
+  EntryTy E;
+
+public:
+  NewPassManagerPrettyStackEntry(StringRef PassName, Module &M)
+      : M(&M), PassName(PassName), E(EntryTy::Module) {}
+  NewPassManagerPrettyStackEntry(StringRef PassName, MachineFunction &MF)
+      : MF(&MF), PassName(PassName), E(EntryTy::MachineFunction) {}
+  NewPassManagerPrettyStackEntry(StringRef PassName, Value &V)
+      : V(&V), PassName(PassName), E(EntryTy::Value) {}
+  NewPassManagerPrettyStackEntry(StringRef PassName, StringRef LoopName)
+      : LoopName(LoopName), PassName(PassName), E(EntryTy::Loop) {}
+  NewPassManagerPrettyStackEntry(StringRef PassName, std::string CGSCCName)
+      : CGSCCName(CGSCCName), PassName(PassName), E(EntryTy::CGSCC) {}
+
+  ~NewPassManagerPrettyStackEntry() override {
+    if (E == EntryTy::CGSCC)
+      CGSCCName.~basic_string();
+  }
+
+  /// 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 +565,7 @@ public:
       PreservedAnalyses PassPA;
       {
         TimeTraceScope TimeScope(P->name(), IR.getName());
+        NewPassManagerPrettyStackEntry StackEntry(P->name(), IR);
         PassPA = P->run(IR, AM, ExtraArgs...);
       }
 
index 1df5104..6f70caf 100644 (file)
@@ -395,6 +395,7 @@ Optional<PreservedAnalyses> LoopPassManager::runSinglePass(
   PreservedAnalyses PA;
   {
     TimeTraceScope TimeScope(Pass->name(), IR.getName());
+    NewPassManagerPrettyStackEntry StackEntry(Pass->name(), L.getName());
     PA = Pass->run(IR, AM, AR, U);
   }
 
index 464d893..88878a1 100644 (file)
@@ -87,6 +87,7 @@ PassManager<LazyCallGraph::SCC, CGSCCAnalysisManager, LazyCallGraph &,
     PreservedAnalyses PassPA;
     {
       TimeTraceScope TimeScope(Pass->name());
+      NewPassManagerPrettyStackEntry StackEntry(Pass->name(), C->getName());
       PassPA = Pass->run(*C, AM, G, UR);
     }
 
index 3025c38..f8766b9 100644 (file)
@@ -124,6 +124,7 @@ PreservedAnalyses ModuleToFunctionPassAdaptor::run(Module &M,
     PreservedAnalyses PassPA;
     {
       TimeTraceScope TimeScope(Pass->name(), F.getName());
+      NewPassManagerPrettyStackEntry StackEntry(Pass->name(), F);
       PassPA = Pass->run(F, FAM);
     }
 
@@ -152,3 +153,43 @@ PreservedAnalyses ModuleToFunctionPassAdaptor::run(Module &M,
 AnalysisSetKey CFGAnalyses::SetKey;
 
 AnalysisSetKey PreservedAnalyses::AllAnalysesKey;
+
+void NewPassManagerPrettyStackEntry::print(raw_ostream &OS) const {
+  OS << "Running pass '" << PassName << "'";
+
+  if (E == EntryTy::Module) {
+    OS << " on module '" << M->getModuleIdentifier() << "'.\n";
+    return;
+  }
+
+  if (E == EntryTy::MachineFunction) {
+    // Printing the machine function's name would require pulling in
+    // llvm/Codegen/MachineFunction.h
+    OS << " on machine function \n";
+    return;
+  }
+
+  if (E == EntryTy::Loop) {
+    OS << " on loop '" << LoopName << "'.\n";
+    return;
+  }
+
+  if (E == EntryTy::CGSCC) {
+    OS << " on CGSCC '" << CGSCCName << "'.\n";
+    return;
+  }
+
+  assert(E == EntryTy::Value && "unexpected stack entry type");
+
+  OS << " on ";
+  if (isa<Function>(V))
+    OS << "function";
+  else if (isa<BasicBlock>(V))
+    OS << "basic block";
+  else
+    OS << "value";
+
+  OS << " '";
+  V->printAsOperand(OS, /*PrintType=*/false);
+  OS << "'\n";
+}
index 7553e1f..d12f7d4 100644 (file)
@@ -374,6 +374,31 @@ bool shouldPopulateClassToPassNames() {
 
 } // namespace
 
+/// Passes to test crash handling.
+/// DO NOT USE THIS EXCEPT FOR TESTING!
+struct CrashingFunctionPass : public PassInfoMixin<CrashingFunctionPass> {
+  PreservedAnalyses run(Function &, FunctionAnalysisManager &) {
+    llvm_unreachable("");
+  }
+};
+struct CrashingModulePass : public PassInfoMixin<CrashingModulePass> {
+  PreservedAnalyses run(Module &, ModuleAnalysisManager &) {
+    llvm_unreachable("");
+  }
+};
+struct CrashingLoopPass : public PassInfoMixin<CrashingLoopPass> {
+  PreservedAnalyses run(Loop &, LoopAnalysisManager &,
+                        LoopStandardAnalysisResults &, LPMUpdater &) {
+    llvm_unreachable("");
+  }
+};
+struct CrashingCGSCCPass : public PassInfoMixin<CrashingCGSCCPass> {
+  PreservedAnalyses run(LazyCallGraph::SCC &, CGSCCAnalysisManager &,
+                        LazyCallGraph &, CGSCCUpdateResult &) {
+    llvm_unreachable("");
+  }
+};
+
 PassBuilder::PassBuilder(TargetMachine *TM, PipelineTuningOptions PTO,
                          Optional<PGOOptions> PGOOpt,
                          PassInstrumentationCallbacks *PIC)
index 0c77509..08d4b30 100644 (file)
@@ -50,6 +50,7 @@ MODULE_PASS("canonicalize-aliases", CanonicalizeAliasesPass())
 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 @@ CGSCC_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis(PIC))
 #define CGSCC_PASS(NAME, CREATE_PASS)
 #endif
 CGSCC_PASS("argpromotion", ArgumentPromotionPass())
+CGSCC_PASS("crash-cgscc", CrashingCGSCCPass())
 CGSCC_PASS("invalidate<all>", InvalidateAllAnalysesPass())
 CGSCC_PASS("function-attrs", PostOrderFunctionAttrsPass())
 CGSCC_PASS("attributor-cgscc", AttributorCGSCCPass())
@@ -251,6 +253,7 @@ FUNCTION_PASS("coro-early", CoroEarlyPass())
 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 @@ LOOP_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis(PIC))
 #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<all>", InvalidateAllAnalysesPass())
 LOOP_PASS("licm", LICMPass())
index d20d275..b71aa2b 100644 (file)
@@ -294,6 +294,7 @@ PreservedAnalyses FunctionToLoopPassAdaptor::run(Function &F,
     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 (file)
index 0000000..2d44f96
--- /dev/null
@@ -0,0 +1,13 @@
+; 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)'.
+
+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 (file)
index 0000000..e61b206
--- /dev/null
@@ -0,0 +1,14 @@
+; 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<llvm::Function>' on function '@foo'
+; CHECK-NEXT: 3.  Running pass 'CrashingFunctionPass' on function '@foo'
+
+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 (file)
index 0000000..6d9067c
--- /dev/null
@@ -0,0 +1,20 @@
+; 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<llvm::Function>' on function '@foo'
+; CHECK-NEXT: 3.  Running pass 'FunctionToLoopPassAdaptor' on function '@foo'
+; CHECK-NEXT: 4.  Running pass 'PassManager<llvm::Loop, llvm::LoopAnalysisManager, llvm::LoopStandardAnalysisResults &, llvm::LPMUpdater &>' on loop 'loop.header'.
+; CHECK-NEXT: 5.  Running pass 'CrashingLoopPass' on loop 'loop.header'.
+
+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 (file)
index 0000000..46bfda8
--- /dev/null
@@ -0,0 +1,12 @@
+; 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
+
+define void @foo() {
+  ret void
+}