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 @@ -381,6 +381,13 @@ Name = Name.drop_front(strlen("llvm::")); return Name; } + + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName) { + auto ClassName = name(); + auto PassName = MapClassName2PassName(ClassName); + OS << PassName; + } }; /// A CRTP mix-in that provides informational APIs needed for analysis passes. @@ -480,6 +487,16 @@ return *this; } + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName) { + for (unsigned Idx = 0, Size = Passes.size(); Idx != Size; ++Idx) { + auto *P = Passes[Idx].get(); + P->printPipeline(OS, MapClassName2PassName); + if (Idx + 1 < Size) + OS << ","; + } + } + /// Run all of the passes in this manager over the given unit of IR. /// ExtraArgs are passed to each pass. PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM, @@ -1195,6 +1212,8 @@ /// Runs the function pass across every function in the module. PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName); static bool isRequired() { return true; } @@ -1243,6 +1262,12 @@ return PreservedAnalyses::all(); } + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName) { + auto ClassName = AnalysisT::name(); + auto PassName = MapClassName2PassName(ClassName); + OS << "require<" << PassName << ">"; + } static bool isRequired() { return true; } }; @@ -1263,6 +1288,12 @@ PA.abandon(); return PA; } + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName) { + auto ClassName = AnalysisT::name(); + auto PassName = MapClassName2PassName(ClassName); + OS << "invalidate<" << PassName << ">"; + } }; /// A utility pass that does nothing, but preserves no analyses. @@ -1312,6 +1343,13 @@ return PA; } + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName) { + OS << "repeat<" << Count << ">("; + P.printPipeline(OS, MapClassName2PassName); + OS << ")"; + } + private: int Count; PassT P; diff --git a/llvm/include/llvm/IR/PassManagerInternal.h b/llvm/include/llvm/IR/PassManagerInternal.h --- a/llvm/include/llvm/IR/PassManagerInternal.h +++ b/llvm/include/llvm/IR/PassManagerInternal.h @@ -46,6 +46,9 @@ virtual PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM, ExtraArgTs... ExtraArgs) = 0; + virtual void + printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName) = 0; /// Polymorphic method to access the name of a pass. virtual StringRef name() const = 0; @@ -85,6 +88,12 @@ return Pass.run(IR, AM, ExtraArgs...); } + void printPipeline( + raw_ostream &OS, + function_ref MapClassName2PassName) override { + Pass.printPipeline(OS, MapClassName2PassName); + } + StringRef name() const override { return PassT::name(); } template 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 @@ -94,6 +94,8 @@ PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM, LoopStandardAnalysisResults &AR, LPMUpdater &U); + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName); /// Add either a loop pass or a loop-nest pass to the pass manager. Append \p /// Pass to the list of loop passes if it has a dedicated \fn run() method for /// loops and to the list of loop-nest passes if the \fn run() method is for @@ -424,6 +426,8 @@ /// Runs the loop passes across every loop in the function. PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName); static bool isRequired() { return true; } diff --git a/llvm/include/llvm/Transforms/Scalar/SimplifyCFG.h b/llvm/include/llvm/Transforms/Scalar/SimplifyCFG.h --- a/llvm/include/llvm/Transforms/Scalar/SimplifyCFG.h +++ b/llvm/include/llvm/Transforms/Scalar/SimplifyCFG.h @@ -41,6 +41,9 @@ /// Run the pass over the function. PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM); + + void printPipeline(raw_ostream &OS, + function_ref MapClassName2PassName); }; } 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 @@ -91,6 +91,13 @@ } } // namespace llvm +void ModuleToFunctionPassAdaptor::printPipeline( + raw_ostream &OS, function_ref MapClassName2PassName) { + OS << "function("; + Pass->printPipeline(OS, MapClassName2PassName); + OS << ")"; +} + PreservedAnalyses ModuleToFunctionPassAdaptor::run(Module &M, ModuleAnalysisManager &AM) { FunctionAnalysisManager &FAM = 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 @@ -291,6 +291,11 @@ } namespace llvm { +cl::opt PrintPipelinePasses( + "print-pipeline-passes", + cl::desc("Print a '-passes' compatible string describing the pipeline " + "(best-effort only).")); + extern cl::opt MaxDevirtIterations; extern cl::opt EnableConstraintElimination; extern cl::opt EnableFunctionSpecialization; @@ -439,7 +444,8 @@ /// it. This should be updated if new pass instrumentation wants to use the map. /// We currently only use this for --print-before/after. bool shouldPopulateClassToPassNames() { - return !printBeforePasses().empty() || !printAfterPasses().empty(); + return PrintPipelinePasses || !printBeforePasses().empty() || + !printAfterPasses().empty(); } } // namespace 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 @@ -44,6 +44,18 @@ return PA; } +void PassManager::printPipeline(raw_ostream &OS, + function_ref + MapClassName2PassName) { + for (unsigned Idx = 0, Size = LoopPasses.size(); Idx != Size; ++Idx) { + auto *P = LoopPasses[Idx].get(); + P->printPipeline(OS, MapClassName2PassName); + if (Idx + 1 < Size) + OS << ","; + } +} + // Run both loop passes and loop-nest passes on top-level loop \p L. PreservedAnalyses LoopPassManager::runWithLoopNestPasses(Loop &L, LoopAnalysisManager &AM, @@ -172,6 +184,12 @@ } } // namespace llvm +void FunctionToLoopPassAdaptor::printPipeline( + raw_ostream &OS, function_ref MapClassName2PassName) { + OS << (UseMemorySSA ? "loop-mssa(" : "loop("); + Pass->printPipeline(OS, MapClassName2PassName); + OS << ")"; +} PreservedAnalyses FunctionToLoopPassAdaptor::run(Function &F, FunctionAnalysisManager &AM) { // Before we even compute any loop analyses, first run a miniature function diff --git a/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp b/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp --- a/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp +++ b/llvm/lib/Transforms/Scalar/SimplifyCFGPass.cpp @@ -319,6 +319,21 @@ applyCommandLineOverridesToOptions(Options); } +void SimplifyCFGPass::printPipeline( + raw_ostream &OS, function_ref MapClassName2PassName) { + static_cast *>(this)->printPipeline( + OS, MapClassName2PassName); + OS << "<"; + OS << "bonus-inst-threshold=" << Options.BonusInstThreshold << ";"; + OS << (Options.ForwardSwitchCondToPhi ? "" : "no-") << "forward-switch-cond;"; + OS << (Options.ConvertSwitchToLookupTable ? "" : "no-") + << "switch-to-lookup;"; + OS << (Options.NeedCanonicalLoop ? "" : "no-") << "keep-loops;"; + OS << (Options.HoistCommonInsts ? "" : "no-") << "hoist-common-insts;"; + OS << (Options.SinkCommonInsts ? "" : "no-") << "sink-common-insts"; + OS << ">"; +} + PreservedAnalyses SimplifyCFGPass::run(Function &F, FunctionAnalysisManager &AM) { auto &TTI = AM.getResult(F); diff --git a/llvm/test/Other/new-pm-print-pipeline.ll b/llvm/test/Other/new-pm-print-pipeline.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Other/new-pm-print-pipeline.ll @@ -0,0 +1,17 @@ +;; Test that the -print-pipeline-passes option correctly prints some explicitly specified pipelines. + +; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='function(adce),function(simplifycfg)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-0 +; CHECK-0: function(adce),function(simplifycfg) + +; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='module(rpo-function-attrs,require,function(float2int,lower-constant-intrinsics,loop(loop-rotate)),invalidate)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-1 +; CHECK-1: rpo-function-attrs,require,function(float2int,lower-constant-intrinsics,loop(loop-rotate)),invalidate + +; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='repeat<5>(function(mem2reg)),invalidate' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-2 +; CHECK-2: repeat<5>(function(mem2reg)),invalidate + +;; Test that we get ClassName printed when there is no ClassName to pass-name mapping (as is the case for the BitcodeWriterPass). +; RUN: opt -o /dev/null -disable-verify -print-pipeline-passes -passes='function(mem2reg)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-3 +; CHECK-3: function(mem2reg),BitcodeWriterPass + +; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='function(loop-mssa(indvars))' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-4 +; CHECK-4: function(loop-mssa(indvars)) diff --git a/llvm/tools/opt/NewPMDriver.cpp b/llvm/tools/opt/NewPMDriver.cpp --- a/llvm/tools/opt/NewPMDriver.cpp +++ b/llvm/tools/opt/NewPMDriver.cpp @@ -137,6 +137,7 @@ extern cl::opt CSPGOKindFlag; extern cl::opt CSProfileGenFile; extern cl::opt DisableBasicAA; +extern cl::opt PrintPipelinePasses; } // namespace llvm static cl::opt @@ -473,6 +474,17 @@ // Before executing passes, print the final values of the LLVM options. cl::PrintOptionValues(); + // Print a textual, '-passes=' compatible, representation of pipeline if + // requested. + if (PrintPipelinePasses) { + MPM.printPipeline(outs(), [&PIC](StringRef ClassName) { + auto PassName = PIC.getPassNameForClassName(ClassName); + return PassName.empty() ? ClassName : PassName; + }); + outs() << "\n"; + return true; + } + // Now that we have all of the passes ready, run them. MPM.run(M, MAM);