[PM] Enable registration of out-of-tree passes with PassBuilder
authorPhilip Pfaffe <philip.pfaffe@gmail.com>
Mon, 10 Jul 2017 10:57:55 +0000 (10:57 +0000)
committerPhilip Pfaffe <philip.pfaffe@gmail.com>
Mon, 10 Jul 2017 10:57:55 +0000 (10:57 +0000)
Summary:
This patch adds a callback registration API to the PassBuilder,
enabling registering out-of-tree passes with it.

Through the Callback API, callers may register callbacks with the
various stages at which passes are added into pass managers, including
parsing of a pass pipeline as well as at extension points within the
default -O pipelines.

Registering utilities like `require<>` and `invalidate<>` needs to be
handled manually by the caller, but a helper is provided.

Additionally, adding passes at pipeline extension points is exposed
through the opt tool. This patch adds a `-passes-ep-X` commandline
option for every extension point X, which opt parses into pipelines
inserted into that extension point.

Reviewers: chandlerc

Reviewed By: chandlerc

Subscribers: lksbhm, grosser, davide, mehdi_amini, llvm-commits, mgorny

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

llvm-svn: 307532

llvm/include/llvm/Passes/PassBuilder.h
llvm/lib/Passes/PassBuilder.cpp
llvm/test/Other/new-pm-defaults.ll
llvm/test/Other/new-pm-lto-defaults.ll
llvm/tools/opt/NewPMDriver.cpp
llvm/unittests/IR/CMakeLists.txt
llvm/unittests/IR/PassBuilderCallbacksTest.cpp [new file with mode: 0644]

index e3e0301..33433f6 100644 (file)
@@ -46,6 +46,19 @@ class PassBuilder {
   Optional<PGOOptions> PGOOpt;
 
 public:
+  /// \brief A struct to capture parsed pass pipeline names.
+  ///
+  /// A pipeline is defined as a series of names, each of which may in itself
+  /// recursively contain a nested pipeline. A name is either the name of a pass
+  /// (e.g. "instcombine") or the name of a pipeline type (e.g. "cgscc"). If the
+  /// name is the name of a pass, the InnerPipeline is empty, since passes
+  /// cannot contain inner pipelines. See parsePassPipeline() for a more
+  /// detailed description of the textual pipeline format.
+  struct PipelineElement {
+    StringRef Name;
+    std::vector<PipelineElement> InnerPipeline;
+  };
+
   /// \brief LLVM-provided high-level optimization levels.
   ///
   /// This enumerates the LLVM-provided high-level optimization levels. Each
@@ -312,7 +325,8 @@ public:
   /// registered.
   AAManager buildDefaultAAPipeline();
 
-  /// \brief Parse a textual pass pipeline description into a \c ModulePassManager.
+  /// \brief Parse a textual pass pipeline description into a \c
+  /// ModulePassManager.
   ///
   /// The format of the textual pass pipeline description looks something like:
   ///
@@ -322,8 +336,8 @@ public:
   /// are comma separated. As a special shortcut, if the very first pass is not
   /// a module pass (as a module pass manager is), this will automatically form
   /// the shortest stack of pass managers that allow inserting that first pass.
-  /// So, assuming function passes 'fpassN', CGSCC passes 'cgpassN', and loop passes
-  /// 'lpassN', all of these are valid:
+  /// So, assuming function passes 'fpassN', CGSCC passes 'cgpassN', and loop
+  /// passes 'lpassN', all of these are valid:
   ///
   ///   fpass1,fpass2,fpass3
   ///   cgpass1,cgpass2,cgpass3
@@ -336,13 +350,28 @@ public:
   ///   module(function(loop(lpass1,lpass2,lpass3)))
   ///
   /// This shortcut is especially useful for debugging and testing small pass
-  /// combinations. Note that these shortcuts don't introduce any other magic. If
-  /// the sequence of passes aren't all the exact same kind of pass, it will be
-  /// an error. You cannot mix different levels implicitly, you must explicitly
-  /// form a pass manager in which to nest passes.
+  /// combinations. Note that these shortcuts don't introduce any other magic.
+  /// If the sequence of passes aren't all the exact same kind of pass, it will
+  /// be an error. You cannot mix different levels implicitly, you must
+  /// explicitly form a pass manager in which to nest passes.
   bool parsePassPipeline(ModulePassManager &MPM, StringRef PipelineText,
                          bool VerifyEachPass = true, bool DebugLogging = false);
 
+  /// {{@ Parse a textual pass pipeline description into a specific PassManager
+  ///
+  /// Automatic deduction of an appropriate pass manager stack is not supported.
+  /// For example, to insert a loop pass 'lpass' into a FunctinoPassManager,
+  /// this is the valid pipeline text:
+  ///
+  ///   function(lpass)
+  bool parsePassPipeline(CGSCCPassManager &CGPM, StringRef PipelineText,
+                         bool VerifyEachPass = true, bool DebugLogging = false);
+  bool parsePassPipeline(FunctionPassManager &FPM, StringRef PipelineText,
+                         bool VerifyEachPass = true, bool DebugLogging = false);
+  bool parsePassPipeline(LoopPassManager &LPM, StringRef PipelineText,
+                         bool VerifyEachPass = true, bool DebugLogging = false);
+  /// @}}
+
   /// Parse a textual alias analysis pipeline into the provided AA manager.
   ///
   /// The format of the textual AA pipeline is a comma separated list of AA
@@ -360,13 +389,139 @@ public:
   /// returns false.
   bool parseAAPipeline(AAManager &AA, StringRef PipelineText);
 
-private:
-  /// A struct to capture parsed pass pipeline names.
-  struct PipelineElement {
-    StringRef Name;
-    std::vector<PipelineElement> InnerPipeline;
-  };
+  /// \brief Register a callback for a default optimizer pipeline extension
+  /// point
+  ///
+  /// This extension point allows adding passes that perform peephole
+  /// optimizations similar to the instruction combiner. These passes will be
+  /// inserted after each instance of the instruction combiner pass.
+  void registerPeepholeEPCallback(
+      const std::function<void(FunctionPassManager &, OptimizationLevel)> &C) {
+    PeepholeEPCallbacks.push_back(C);
+  }
+
+  /// \brief Register a callback for a default optimizer pipeline extension
+  /// point
+  ///
+  /// This extension point allows adding late loop canonicalization and
+  /// simplification passes. This is the last point in the loop optimization
+  /// pipeline before loop deletion. Each pass added
+  /// here must be an instance of LoopPass.
+  /// This is the place to add passes that can remove loops, such as target-
+  /// specific loop idiom recognition.
+  void registerLateLoopOptimizationsEPCallback(
+      const std::function<void(LoopPassManager &, OptimizationLevel)> &C) {
+    LateLoopOptimizationsEPCallbacks.push_back(C);
+  }
+
+  /// \brief Register a callback for a default optimizer pipeline extension
+  /// point
+  ///
+  /// This extension point allows adding loop passes to the end of the loop
+  /// optimizer.
+  void registerLoopOptimizerEndEPCallback(
+      const std::function<void(LoopPassManager &, OptimizationLevel)> &C) {
+    LoopOptimizerEndEPCallbacks.push_back(C);
+  }
+
+  /// \brief Register a callback for a default optimizer pipeline extension
+  /// point
+  ///
+  /// This extension point allows adding optimization passes after most of the
+  /// main optimizations, but before the last cleanup-ish optimizations.
+  void registerScalarOptimizerLateEPCallback(
+      const std::function<void(FunctionPassManager &, OptimizationLevel)> &C) {
+    ScalarOptimizerLateEPCallbacks.push_back(C);
+  }
+
+  /// \brief Register a callback for a default optimizer pipeline extension
+  /// point
+  ///
+  /// This extension point allows adding CallGraphSCC passes at the end of the
+  /// main CallGraphSCC passes and before any function simplification passes run
+  /// by CGPassManager.
+  void registerCGSCCOptimizerLateEPCallback(
+      const std::function<void(CGSCCPassManager &, OptimizationLevel)> &C) {
+    CGSCCOptimizerLateEPCallbacks.push_back(C);
+  }
+
+  /// \brief Register a callback for a default optimizer pipeline extension
+  /// point
+  ///
+  /// This extension point allows adding optimization passes before the
+  /// vectorizer and other highly target specific optimization passes are
+  /// executed.
+  void registerVectorizerStartEPCallback(
+      const std::function<void(FunctionPassManager &, OptimizationLevel)> &C) {
+    VectorizerStartEPCallbacks.push_back(C);
+  }
+
+  /// \brief Register a callback for parsing an AliasAnalysis Name to populate
+  /// the given AAManager \p AA
+  void registerParseAACallback(
+      const std::function<bool(StringRef Name, AAManager &AA)> &C) {
+    AAParsingCallbacks.push_back(C);
+  }
+
+  /// {{@ Register callbacks for analysis registration with this PassBuilder
+  /// instance.
+  /// Callees register their analyses with the given AnalysisManager objects.
+  void registerAnalysisRegistrationCallback(
+      const std::function<void(CGSCCAnalysisManager &)> &C) {
+    CGSCCAnalysisRegistrationCallbacks.push_back(C);
+  }
+  void registerAnalysisRegistrationCallback(
+      const std::function<void(FunctionAnalysisManager &)> &C) {
+    FunctionAnalysisRegistrationCallbacks.push_back(C);
+  }
+  void registerAnalysisRegistrationCallback(
+      const std::function<void(LoopAnalysisManager &)> &C) {
+    LoopAnalysisRegistrationCallbacks.push_back(C);
+  }
+  void registerAnalysisRegistrationCallback(
+      const std::function<void(ModuleAnalysisManager &)> &C) {
+    ModuleAnalysisRegistrationCallbacks.push_back(C);
+  }
+  /// @}}
+
+  /// {{@ Register pipeline parsing callbacks with this pass builder instance.
+  /// Using these callbacks, callers can parse both a single pass name, as well
+  /// as entire sub-pipelines, and populate the PassManager instance
+  /// accordingly.
+  void registerPipelineParsingCallback(
+      const std::function<bool(StringRef Name, CGSCCPassManager &,
+                               ArrayRef<PipelineElement>)> &C) {
+    CGSCCPipelineParsingCallbacks.push_back(C);
+  }
+  void registerPipelineParsingCallback(
+      const std::function<bool(StringRef Name, FunctionPassManager &,
+                               ArrayRef<PipelineElement>)> &C) {
+    FunctionPipelineParsingCallbacks.push_back(C);
+  }
+  void registerPipelineParsingCallback(
+      const std::function<bool(StringRef Name, LoopPassManager &,
+                               ArrayRef<PipelineElement>)> &C) {
+    LoopPipelineParsingCallbacks.push_back(C);
+  }
+  void registerPipelineParsingCallback(
+      const std::function<bool(StringRef Name, ModulePassManager &,
+                               ArrayRef<PipelineElement>)> &C) {
+    ModulePipelineParsingCallbacks.push_back(C);
+  }
+  /// @}}
+
+  /// \brief Register a callback for a top-level pipeline entry.
+  ///
+  /// If the PassManager type is not given at the top level of the pipeline
+  /// text, this Callback should be used to determine the appropriate stack of
+  /// PassManagers and populate the passed ModulePassManager.
+  void registerParseTopLevelPipelineCallback(
+      const std::function<bool(ModulePassManager &, ArrayRef<PipelineElement>,
+                               bool VerifyEachPass, bool DebugLogging)> &C) {
+    TopLevelPipelineParsingCallbacks.push_back(C);
+  }
 
+private:
   static Optional<std::vector<PipelineElement>>
   parsePipelineText(StringRef Text);
 
@@ -392,7 +547,106 @@ private:
   bool parseModulePassPipeline(ModulePassManager &MPM,
                                ArrayRef<PipelineElement> Pipeline,
                                bool VerifyEachPass, bool DebugLogging);
+
+  void addPGOInstrPasses(ModulePassManager &MPM, bool DebugLogging,
+                         OptimizationLevel Level, bool RunProfileGen,
+                         std::string ProfileGenFile,
+                         std::string ProfileUseFile);
+
+  void invokePeepholeEPCallbacks(FunctionPassManager &, OptimizationLevel);
+
+  // Extension Point callbacks
+  SmallVector<std::function<void(FunctionPassManager &, OptimizationLevel)>, 2>
+      PeepholeEPCallbacks;
+  SmallVector<std::function<void(LoopPassManager &, OptimizationLevel)>, 2>
+      LateLoopOptimizationsEPCallbacks;
+  SmallVector<std::function<void(LoopPassManager &, OptimizationLevel)>, 2>
+      LoopOptimizerEndEPCallbacks;
+  SmallVector<std::function<void(FunctionPassManager &, OptimizationLevel)>, 2>
+      ScalarOptimizerLateEPCallbacks;
+  SmallVector<std::function<void(CGSCCPassManager &, OptimizationLevel)>, 2>
+      CGSCCOptimizerLateEPCallbacks;
+  SmallVector<std::function<void(FunctionPassManager &, OptimizationLevel)>, 2>
+      VectorizerStartEPCallbacks;
+  // Module callbacks
+  SmallVector<std::function<void(ModuleAnalysisManager &)>, 2>
+      ModuleAnalysisRegistrationCallbacks;
+  SmallVector<std::function<bool(StringRef, ModulePassManager &,
+                                 ArrayRef<PipelineElement>)>,
+              2>
+      ModulePipelineParsingCallbacks;
+  SmallVector<std::function<bool(ModulePassManager &, ArrayRef<PipelineElement>,
+                                 bool VerifyEachPass, bool DebugLogging)>,
+              2>
+      TopLevelPipelineParsingCallbacks;
+  // CGSCC callbacks
+  SmallVector<std::function<void(CGSCCAnalysisManager &)>, 2>
+      CGSCCAnalysisRegistrationCallbacks;
+  SmallVector<std::function<bool(StringRef, CGSCCPassManager &,
+                                 ArrayRef<PipelineElement>)>,
+              2>
+      CGSCCPipelineParsingCallbacks;
+  // Function callbacks
+  SmallVector<std::function<void(FunctionAnalysisManager &)>, 2>
+      FunctionAnalysisRegistrationCallbacks;
+  SmallVector<std::function<bool(StringRef, FunctionPassManager &,
+                                 ArrayRef<PipelineElement>)>,
+              2>
+      FunctionPipelineParsingCallbacks;
+  // Loop callbacks
+  SmallVector<std::function<void(LoopAnalysisManager &)>, 2>
+      LoopAnalysisRegistrationCallbacks;
+  SmallVector<std::function<bool(StringRef, LoopPassManager &,
+                                 ArrayRef<PipelineElement>)>,
+              2>
+      LoopPipelineParsingCallbacks;
+  // AA callbacks
+  SmallVector<std::function<bool(StringRef Name, AAManager &AA)>, 2>
+      AAParsingCallbacks;
 };
+
+/// This utility template takes care of adding require<> and invalidate<>
+/// passes for an analysis to a given \c PassManager. It is intended to be used
+/// during parsing of a pass pipeline when parsing a single PipelineName.
+/// When registering a new function analysis FancyAnalysis with the pass
+/// pipeline name "fancy-analysis", a matching ParsePipelineCallback could look
+/// like this:
+///
+/// static bool parseFunctionPipeline(StringRef Name, FunctionPassManager &FPM,
+///                                   ArrayRef<PipelineElement> P) {
+///   if (parseAnalysisUtilityPasses<FancyAnalysis>("fancy-analysis", Name,
+///                                                 FPM))
+///     return true;
+///   return false;
+/// }
+template <typename AnalysisT, typename IRUnitT, typename AnalysisManagerT,
+          typename... ExtraArgTs>
+bool parseAnalysisUtilityPasses(
+    StringRef AnalysisName, StringRef PipelineName,
+    PassManager<IRUnitT, AnalysisManagerT, ExtraArgTs...> &PM) {
+  if (!PipelineName.endswith(">"))
+    return false;
+  // See if this is an invalidate<> pass name
+  if (PipelineName.startswith("invalidate<")) {
+    PipelineName = PipelineName.substr(11, PipelineName.size() - 12);
+    if (PipelineName != AnalysisName)
+      return false;
+    PM.addPass(InvalidateAnalysisPass<AnalysisT>());
+    return true;
+  }
+
+  // See if this is a require<> pass name
+  if (PipelineName.startswith("require<")) {
+    PipelineName = PipelineName.substr(8, PipelineName.size() - 9);
+    if (PipelineName != AnalysisName)
+      return false;
+    PM.addPass(RequireAnalysisPass<AnalysisT, IRUnitT, AnalysisManagerT,
+                                   ExtraArgTs...>());
+    return true;
+  }
+
+  return false;
+}
 }
 
 #endif
index ed04a11..9e0cf27 100644 (file)
@@ -281,28 +281,46 @@ AnalysisKey NoOpLoopAnalysis::Key;
 
 } // End anonymous namespace.
 
+void PassBuilder::invokePeepholeEPCallbacks(
+    FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) {
+  for (auto &C : PeepholeEPCallbacks)
+    C(FPM, Level);
+}
+
 void PassBuilder::registerModuleAnalyses(ModuleAnalysisManager &MAM) {
 #define MODULE_ANALYSIS(NAME, CREATE_PASS)                                     \
   MAM.registerPass([&] { return CREATE_PASS; });
 #include "PassRegistry.def"
+
+  for (auto &C : ModuleAnalysisRegistrationCallbacks)
+    C(MAM);
 }
 
 void PassBuilder::registerCGSCCAnalyses(CGSCCAnalysisManager &CGAM) {
 #define CGSCC_ANALYSIS(NAME, CREATE_PASS)                                      \
   CGAM.registerPass([&] { return CREATE_PASS; });
 #include "PassRegistry.def"
+
+  for (auto &C : CGSCCAnalysisRegistrationCallbacks)
+    C(CGAM);
 }
 
 void PassBuilder::registerFunctionAnalyses(FunctionAnalysisManager &FAM) {
 #define FUNCTION_ANALYSIS(NAME, CREATE_PASS)                                   \
   FAM.registerPass([&] { return CREATE_PASS; });
 #include "PassRegistry.def"
+
+  for (auto &C : FunctionAnalysisRegistrationCallbacks)
+    C(FAM);
 }
 
 void PassBuilder::registerLoopAnalyses(LoopAnalysisManager &LAM) {
 #define LOOP_ANALYSIS(NAME, CREATE_PASS)                                       \
   LAM.registerPass([&] { return CREATE_PASS; });
 #include "PassRegistry.def"
+
+  for (auto &C : LoopAnalysisRegistrationCallbacks)
+    C(LAM);
 }
 
 FunctionPassManager
@@ -341,6 +359,8 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
   if (!isOptimizingForSize(Level))
     FPM.addPass(LibCallsShrinkWrapPass());
 
+  invokePeepholeEPCallbacks(FPM, Level);
+
   FPM.addPass(TailCallElimPass());
   FPM.addPass(SimplifyCFGPass());
 
@@ -364,6 +384,10 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
   LPM1.addPass(SimpleLoopUnswitchPass());
   LPM2.addPass(IndVarSimplifyPass());
   LPM2.addPass(LoopIdiomRecognizePass());
+
+  for (auto &C : LateLoopOptimizationsEPCallbacks)
+    C(LPM2, Level);
+
   LPM2.addPass(LoopDeletionPass());
   // Do not enable unrolling in PrepareForThinLTO phase during sample PGO
   // because it changes IR to makes profile annotation in back compile
@@ -371,6 +395,9 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
   if (!PrepareForThinLTO || !PGOOpt || PGOOpt->SampleProfileFile.empty())
     LPM2.addPass(LoopUnrollPass::createFull(Level));
 
+  for (auto &C : LoopOptimizerEndEPCallbacks)
+    C(LPM2, Level);
+
   // We provide the opt remark emitter pass for LICM to use. We only need to do
   // this once as it is immutable.
   FPM.addPass(RequireAnalysisPass<OptimizationRemarkEmitterAnalysis, Function>());
@@ -405,6 +432,7 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
   // Run instcombine after redundancy and dead bit elimination to exploit
   // opportunities opened up by them.
   FPM.addPass(InstCombinePass());
+  invokePeepholeEPCallbacks(FPM, Level);
 
   // Re-consider control flow based optimizations after redundancy elimination,
   // redo DCE, etc.
@@ -413,19 +441,24 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
   FPM.addPass(DSEPass());
   FPM.addPass(createFunctionToLoopPassAdaptor(LICMPass()));
 
+  for (auto &C : ScalarOptimizerLateEPCallbacks)
+    C(FPM, Level);
+
   // Finally, do an expensive DCE pass to catch all the dead code exposed by
   // the simplifications and basic cleanup after all the simplifications.
   FPM.addPass(ADCEPass());
   FPM.addPass(SimplifyCFGPass());
   FPM.addPass(InstCombinePass());
+  invokePeepholeEPCallbacks(FPM, Level);
 
   return FPM;
 }
 
-static void addPGOInstrPasses(ModulePassManager &MPM, bool DebugLogging,
-                              PassBuilder::OptimizationLevel Level,
-                              bool RunProfileGen, std::string ProfileGenFile,
-                              std::string ProfileUseFile) {
+void PassBuilder::addPGOInstrPasses(ModulePassManager &MPM, bool DebugLogging,
+                                    PassBuilder::OptimizationLevel Level,
+                                    bool RunProfileGen,
+                                    std::string ProfileGenFile,
+                                    std::string ProfileUseFile) {
   // Generally running simplification passes and the inliner with an high
   // threshold results in smaller executables, but there may be cases where
   // the size grows, so let's be conservative here and skip this simplification
@@ -450,9 +483,8 @@ static void addPGOInstrPasses(ModulePassManager &MPM, bool DebugLogging,
     FPM.addPass(EarlyCSEPass());    // Catch trivial redundancies.
     FPM.addPass(SimplifyCFGPass()); // Merge & remove basic blocks.
     FPM.addPass(InstCombinePass()); // Combine silly sequences.
+    invokePeepholeEPCallbacks(FPM, Level);
 
-    // FIXME: Here the old pass manager inserts peephole extensions.
-    // Add them when they're supported.
     CGPipeline.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM)));
 
     MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPipeline)));
@@ -533,6 +565,8 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
   // optimizations.
   FunctionPassManager GlobalCleanupPM(DebugLogging);
   GlobalCleanupPM.addPass(InstCombinePass());
+  invokePeepholeEPCallbacks(GlobalCleanupPM, Level);
+
   GlobalCleanupPM.addPass(SimplifyCFGPass());
   MPM.addPass(createModuleToFunctionPassAdaptor(std::move(GlobalCleanupPM)));
 
@@ -597,6 +631,9 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
       buildFunctionSimplificationPipeline(Level, DebugLogging,
                                           PrepareForThinLTO)));
 
+  for (auto &C : CGSCCOptimizerLateEPCallbacks)
+    C(MainCGPipeline, Level);
+
   // We wrap the CGSCC pipeline in a devirtualization repeater. This will try
   // to detect when we devirtualize indirect calls and iterate the SCC passes
   // in that case to try and catch knock-on inlining or function attrs
@@ -655,6 +692,9 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   // rather than on each loop in an inside-out manner, and so they are actually
   // function passes.
 
+  for (auto &C : VectorizerStartEPCallbacks)
+    C(OptimizePM, Level);
+
   // First rotate loops that may have been un-rotated by prior passes.
   OptimizePM.addPass(createFunctionToLoopPassAdaptor(LoopRotatePass()));
 
@@ -883,8 +923,11 @@ ModulePassManager PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
   // simplification opportunities, and both can propagate functions through
   // function pointers.  When this happens, we often have to resolve varargs
   // calls, etc, so let instcombine do this.
-  // FIXME: add peephole extensions here as the legacy PM does.
-  MPM.addPass(createModuleToFunctionPassAdaptor(InstCombinePass()));
+  FunctionPassManager PeepholeFPM(DebugLogging);
+  PeepholeFPM.addPass(InstCombinePass());
+  invokePeepholeEPCallbacks(PeepholeFPM, Level);
+
+  MPM.addPass(createModuleToFunctionPassAdaptor(std::move(PeepholeFPM)));
 
   // Note: historically, the PruneEH pass was run first to deduce nounwind and
   // generally clean up exception handling overhead. It isn't clear this is
@@ -902,10 +945,10 @@ ModulePassManager PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
   MPM.addPass(GlobalDCEPass());
 
   FunctionPassManager FPM(DebugLogging);
-
   // The IPO Passes may leave cruft around. Clean up after them.
-  // FIXME: add peephole extensions here as the legacy PM does.
   FPM.addPass(InstCombinePass());
+  invokePeepholeEPCallbacks(FPM, Level);
+
   FPM.addPass(JumpThreadingPass());
 
   // Break up allocas
@@ -952,8 +995,11 @@ ModulePassManager PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
   MainFPM.add(AlignmentFromAssumptionsPass());
 #endif
 
-  // FIXME: add peephole extensions to the PM here.
+  // FIXME: Conditionally run LoadCombine here, after it's ported
+  // (in case we still have this pass, given its questionable usefulness).
+
   MainFPM.addPass(InstCombinePass());
+  invokePeepholeEPCallbacks(MainFPM, Level);
   MainFPM.addPass(JumpThreadingPass());
   MPM.addPass(createModuleToFunctionPassAdaptor(std::move(MainFPM)));
 
@@ -1036,7 +1082,27 @@ static bool startsWithDefaultPipelineAliasPrefix(StringRef Name) {
          Name.startswith("lto");
 }
 
-static bool isModulePassName(StringRef Name) {
+/// Tests whether registered callbacks will accept a given pass name.
+///
+/// When parsing a pipeline text, the type of the outermost pipeline may be
+/// omitted, in which case the type is automatically determined from the first
+/// pass name in the text. This may be a name that is handled through one of the
+/// callbacks. We check this through the oridinary parsing callbacks by setting
+/// up a dummy PassManager in order to not force the client to also handle this
+/// type of query.
+template <typename PassManagerT, typename CallbacksT>
+static bool callbacksAcceptPassName(StringRef Name, CallbacksT &Callbacks) {
+  if (!Callbacks.empty()) {
+    PassManagerT DummyPM;
+    for (auto &CB : Callbacks)
+      if (CB(Name, DummyPM, {}))
+        return true;
+  }
+  return false;
+}
+
+template <typename CallbacksT>
+static bool isModulePassName(StringRef Name, CallbacksT &Callbacks) {
   // Manually handle aliases for pre-configured pipeline fragments.
   if (startsWithDefaultPipelineAliasPrefix(Name))
     return DefaultAliasRegex.match(Name);
@@ -1061,10 +1127,11 @@ static bool isModulePassName(StringRef Name) {
     return true;
 #include "PassRegistry.def"
 
-  return false;
+  return callbacksAcceptPassName<ModulePassManager>(Name, Callbacks);
 }
 
-static bool isCGSCCPassName(StringRef Name) {
+template <typename CallbacksT>
+static bool isCGSCCPassName(StringRef Name, CallbacksT &Callbacks) {
   // Explicitly handle pass manager names.
   if (Name == "cgscc")
     return true;
@@ -1085,10 +1152,11 @@ static bool isCGSCCPassName(StringRef Name) {
     return true;
 #include "PassRegistry.def"
 
-  return false;
+  return callbacksAcceptPassName<CGSCCPassManager>(Name, Callbacks);
 }
 
-static bool isFunctionPassName(StringRef Name) {
+template <typename CallbacksT>
+static bool isFunctionPassName(StringRef Name, CallbacksT &Callbacks) {
   // Explicitly handle pass manager names.
   if (Name == "function")
     return true;
@@ -1107,10 +1175,11 @@ static bool isFunctionPassName(StringRef Name) {
     return true;
 #include "PassRegistry.def"
 
-  return false;
+  return callbacksAcceptPassName<FunctionPassManager>(Name, Callbacks);
 }
 
-static bool isLoopPassName(StringRef Name) {
+template <typename CallbacksT>
+static bool isLoopPassName(StringRef Name, CallbacksT &Callbacks) {
   // Explicitly handle pass manager names.
   if (Name == "loop")
     return true;
@@ -1127,7 +1196,7 @@ static bool isLoopPassName(StringRef Name) {
     return true;
 #include "PassRegistry.def"
 
-  return false;
+  return callbacksAcceptPassName<LoopPassManager>(Name, Callbacks);
 }
 
 Optional<std::vector<PassBuilder::PipelineElement>>
@@ -1228,6 +1297,11 @@ bool PassBuilder::parseModulePass(ModulePassManager &MPM,
       MPM.addPass(createRepeatedPass(*Count, std::move(NestedMPM)));
       return true;
     }
+
+    for (auto &C : ModulePipelineParsingCallbacks)
+      if (C(Name, MPM, InnerPipeline))
+        return true;
+
     // Normal passes can't have pipelines.
     return false;
   }
@@ -1240,12 +1314,12 @@ bool PassBuilder::parseModulePass(ModulePassManager &MPM,
     assert(Matches.size() == 3 && "Must capture two matched strings!");
 
     OptimizationLevel L = StringSwitch<OptimizationLevel>(Matches[2])
-        .Case("O0", O0)
-        .Case("O1", O1)
-        .Case("O2", O2)
-        .Case("O3", O3)
-        .Case("Os", Os)
-        .Case("Oz", Oz);
+                              .Case("O0", O0)
+                              .Case("O1", O1)
+                              .Case("O2", O2)
+                              .Case("O3", O3)
+                              .Case("Os", Os)
+                              .Case("Oz", Oz);
     if (L == O0)
       // At O0 we do nothing at all!
       return true;
@@ -1285,6 +1359,9 @@ bool PassBuilder::parseModulePass(ModulePassManager &MPM,
   }
 #include "PassRegistry.def"
 
+  for (auto &C : ModulePipelineParsingCallbacks)
+    if (C(Name, MPM, InnerPipeline))
+      return true;
   return false;
 }
 
@@ -1332,11 +1409,16 @@ bool PassBuilder::parseCGSCCPass(CGSCCPassManager &CGPM,
                                                *MaxRepetitions, DebugLogging));
       return true;
     }
+
+    for (auto &C : CGSCCPipelineParsingCallbacks)
+      if (C(Name, CGPM, InnerPipeline))
+        return true;
+
     // Normal passes can't have pipelines.
     return false;
   }
 
-  // Now expand the basic registered passes from the .inc file.
+// Now expand the basic registered passes from the .inc file.
 #define CGSCC_PASS(NAME, CREATE_PASS)                                          \
   if (Name == NAME) {                                                          \
     CGPM.addPass(CREATE_PASS);                                                 \
@@ -1357,6 +1439,9 @@ bool PassBuilder::parseCGSCCPass(CGSCCPassManager &CGPM,
   }
 #include "PassRegistry.def"
 
+  for (auto &C : CGSCCPipelineParsingCallbacks)
+    if (C(Name, CGPM, InnerPipeline))
+      return true;
   return false;
 }
 
@@ -1394,11 +1479,16 @@ bool PassBuilder::parseFunctionPass(FunctionPassManager &FPM,
       FPM.addPass(createRepeatedPass(*Count, std::move(NestedFPM)));
       return true;
     }
+
+    for (auto &C : FunctionPipelineParsingCallbacks)
+      if (C(Name, FPM, InnerPipeline))
+        return true;
+
     // Normal passes can't have pipelines.
     return false;
   }
 
-  // Now expand the basic registered passes from the .inc file.
+// Now expand the basic registered passes from the .inc file.
 #define FUNCTION_PASS(NAME, CREATE_PASS)                                       \
   if (Name == NAME) {                                                          \
     FPM.addPass(CREATE_PASS);                                                  \
@@ -1418,6 +1508,9 @@ bool PassBuilder::parseFunctionPass(FunctionPassManager &FPM,
   }
 #include "PassRegistry.def"
 
+  for (auto &C : FunctionPipelineParsingCallbacks)
+    if (C(Name, FPM, InnerPipeline))
+      return true;
   return false;
 }
 
@@ -1445,11 +1538,16 @@ bool PassBuilder::parseLoopPass(LoopPassManager &LPM, const PipelineElement &E,
       LPM.addPass(createRepeatedPass(*Count, std::move(NestedLPM)));
       return true;
     }
+
+    for (auto &C : LoopPipelineParsingCallbacks)
+      if (C(Name, LPM, InnerPipeline))
+        return true;
+
     // Normal passes can't have pipelines.
     return false;
   }
 
-  // Now expand the basic registered passes from the .inc file.
+// Now expand the basic registered passes from the .inc file.
 #define LOOP_PASS(NAME, CREATE_PASS)                                           \
   if (Name == NAME) {                                                          \
     LPM.addPass(CREATE_PASS);                                                  \
@@ -1470,6 +1568,9 @@ bool PassBuilder::parseLoopPass(LoopPassManager &LPM, const PipelineElement &E,
   }
 #include "PassRegistry.def"
 
+  for (auto &C : LoopPipelineParsingCallbacks)
+    if (C(Name, LPM, InnerPipeline))
+      return true;
   return false;
 }
 
@@ -1488,6 +1589,9 @@ bool PassBuilder::parseAAPassName(AAManager &AA, StringRef Name) {
   }
 #include "PassRegistry.def"
 
+  for (auto &C : AAParsingCallbacks)
+    if (C(Name, AA))
+      return true;
   return false;
 }
 
@@ -1554,7 +1658,7 @@ bool PassBuilder::parseModulePassPipeline(ModulePassManager &MPM,
   return true;
 }
 
-// Primary pass pipeline description parsing routine.
+// Primary pass pipeline description parsing routine for a \c ModulePassManager
 // FIXME: Should this routine accept a TargetMachine or require the caller to
 // pre-populate the analysis managers with target-specific stuff?
 bool PassBuilder::parsePassPipeline(ModulePassManager &MPM,
@@ -1568,21 +1672,70 @@ bool PassBuilder::parsePassPipeline(ModulePassManager &MPM,
   // automatically.
   StringRef FirstName = Pipeline->front().Name;
 
-  if (!isModulePassName(FirstName)) {
-    if (isCGSCCPassName(FirstName))
+  if (!isModulePassName(FirstName, ModulePipelineParsingCallbacks)) {
+    if (isCGSCCPassName(FirstName, CGSCCPipelineParsingCallbacks)) {
       Pipeline = {{"cgscc", std::move(*Pipeline)}};
-    else if (isFunctionPassName(FirstName))
+    } else if (isFunctionPassName(FirstName,
+                                  FunctionPipelineParsingCallbacks)) {
       Pipeline = {{"function", std::move(*Pipeline)}};
-    else if (isLoopPassName(FirstName))
+    } else if (isLoopPassName(FirstName, LoopPipelineParsingCallbacks)) {
       Pipeline = {{"function", {{"loop", std::move(*Pipeline)}}}};
-    else
+    } else {
+      for (auto &C : TopLevelPipelineParsingCallbacks)
+        if (C(MPM, *Pipeline, VerifyEachPass, DebugLogging))
+          return true;
+
       // Unknown pass name!
       return false;
+    }
   }
 
   return parseModulePassPipeline(MPM, *Pipeline, VerifyEachPass, DebugLogging);
 }
 
+// Primary pass pipeline description parsing routine for a \c CGSCCPassManager
+bool PassBuilder::parsePassPipeline(CGSCCPassManager &CGPM,
+                                    StringRef PipelineText, bool VerifyEachPass,
+                                    bool DebugLogging) {
+  auto Pipeline = parsePipelineText(PipelineText);
+  if (!Pipeline || Pipeline->empty())
+    return false;
+
+  StringRef FirstName = Pipeline->front().Name;
+  if (!isCGSCCPassName(FirstName, CGSCCPipelineParsingCallbacks))
+    return false;
+
+  return parseCGSCCPassPipeline(CGPM, *Pipeline, VerifyEachPass, DebugLogging);
+}
+
+// Primary pass pipeline description parsing routine for a \c
+// FunctionPassManager
+bool PassBuilder::parsePassPipeline(FunctionPassManager &FPM,
+                                    StringRef PipelineText, bool VerifyEachPass,
+                                    bool DebugLogging) {
+  auto Pipeline = parsePipelineText(PipelineText);
+  if (!Pipeline || Pipeline->empty())
+    return false;
+
+  StringRef FirstName = Pipeline->front().Name;
+  if (!isFunctionPassName(FirstName, FunctionPipelineParsingCallbacks))
+    return false;
+
+  return parseFunctionPassPipeline(FPM, *Pipeline, VerifyEachPass,
+                                   DebugLogging);
+}
+
+// Primary pass pipeline description parsing routine for a \c LoopPassManager
+bool PassBuilder::parsePassPipeline(LoopPassManager &CGPM,
+                                    StringRef PipelineText, bool VerifyEachPass,
+                                    bool DebugLogging) {
+  auto Pipeline = parsePipelineText(PipelineText);
+  if (!Pipeline || Pipeline->empty())
+    return false;
+
+  return parseLoopPassPipeline(CGPM, *Pipeline, VerifyEachPass, DebugLogging);
+}
+
 bool PassBuilder::parseAAPipeline(AAManager &AA, StringRef PipelineText) {
   // If the pipeline just consists of the word 'default' just replace the AA
   // manager with our default one.
index fbecb34..a0658c1 100644 (file)
 ; RUN:     -passes='lto-pre-link<O2>' -S %s 2>&1 \
 ; RUN:     | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O2
 
+; RUN: opt -disable-verify -debug-pass-manager \
+; RUN:     -passes-ep-peephole='no-op-function' \
+; RUN:     -passes='default<O3>' -S  %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
+; RUN:     --check-prefix=CHECK-EP-PEEPHOLE
+; RUN: opt -disable-verify -debug-pass-manager \
+; RUN:     -passes-ep-late-loop-optimizations='no-op-loop' \
+; RUN:     -passes='default<O3>' -S  %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
+; RUN:     --check-prefix=CHECK-EP-LOOP-LATE
+; RUN: opt -disable-verify -debug-pass-manager \
+; RUN:     -passes-ep-loop-optimizer-end='no-op-loop' \
+; RUN:     -passes='default<O3>' -S  %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
+; RUN:     --check-prefix=CHECK-EP-LOOP-END
+; RUN: opt -disable-verify -debug-pass-manager \
+; RUN:     -passes-ep-scalar-optimizer-late='no-op-function' \
+; RUN:     -passes='default<O3>' -S  %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
+; RUN:     --check-prefix=CHECK-EP-SCALAR-LATE
+; RUN: opt -disable-verify -debug-pass-manager \
+; RUN:     -passes-ep-cgscc-optimizer-late='no-op-cgscc' \
+; RUN:     -passes='default<O3>' -S  %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
+; RUN:     --check-prefix=CHECK-EP-CGSCC-LATE
+; RUN: opt -disable-verify -debug-pass-manager \
+; RUN:     -passes-ep-vectorizer-start='no-op-function' \
+; RUN:     -passes='default<O3>' -S  %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
+; RUN:     --check-prefix=CHECK-EP-VECTORIZER-START
+
 ; CHECK-O: Starting llvm::Module pass manager run.
 ; CHECK-O-NEXT: Running pass: PassManager<{{.*}}Module{{.*}}>
 ; CHECK-O-NEXT: Starting llvm::Module pass manager run.
@@ -53,6 +84,7 @@
 ; CHECK-O-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PassManager{{.*}}>
 ; CHECK-O-NEXT: Starting llvm::Function pass manager run.
 ; CHECK-O-NEXT: Running pass: InstCombinePass
+; CHECK-EP-PEEPHOLE-NEXT: Running pass: NoOpFunctionPass
 ; CHECK-O-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O-NEXT: Finished llvm::Function pass manager run.
 ; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}GlobalsAA
 ; CHECK-O1-NEXT: Running pass: LibCallsShrinkWrapPass
 ; CHECK-O2-NEXT: Running pass: LibCallsShrinkWrapPass
 ; CHECK-O3-NEXT: Running pass: LibCallsShrinkWrapPass
+; CHECK-EP-PEEPHOLE-NEXT: Running pass: NoOpFunctionPass
 ; CHECK-O-NEXT: Running pass: TailCallElimPass
 ; CHECK-O-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O-NEXT: Running pass: ReassociatePass
 ; CHECK-O-NEXT: Starting Loop pass manager run.
 ; CHECK-O-NEXT: Running pass: IndVarSimplifyPass
 ; CHECK-O-NEXT: Running pass: LoopIdiomRecognizePass
+; CHECK-EP-LOOP-LATE-NEXT: Running pass: NoOpLoopPass
 ; CHECK-O-NEXT: Running pass: LoopDeletionPass
 ; CHECK-O-NEXT: Running pass: LoopUnrollPass
+; CHECK-EP-LOOP-END-NEXT: Running pass: NoOpLoopPass
 ; CHECK-O-NEXT: Finished Loop pass manager run.
 ; CHECK-Os-NEXT: Running pass: MergedLoadStoreMotionPass
 ; CHECK-Os-NEXT: Running pass: GVN
 ; CHECK-O-NEXT: Running pass: BDCEPass
 ; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis
 ; CHECK-O-NEXT: Running pass: InstCombinePass
+; CHECK-EP-PEEPHOLE-NEXT: Running pass: NoOpFunctionPass
 ; CHECK-O-NEXT: Running pass: JumpThreadingPass
 ; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass
 ; CHECK-O-NEXT: Running pass: DSEPass
 ; CHECK-O-NEXT: Running pass: FunctionToLoopPassAdaptor<{{.*}}LICMPass{{.*}}>
+; CHECK-EP-SCALAR-LATE-NEXT: Running pass: NoOpFunctionPass
 ; CHECK-O-NEXT: Running pass: ADCEPass
 ; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis
 ; CHECK-O-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O-NEXT: Running pass: InstCombinePass
+; CHECK-EP-PEEPHOLE-NEXT: Running pass: NoOpFunctionPass
 ; CHECK-O-NEXT: Finished llvm::Function pass manager run.
+; CHECK-EP-CGSCC-LATE-NEXT: Running pass: NoOpCGSCCPass
 ; CHECK-O-NEXT: Finished CGSCC pass manager run.
 ; CHECK-O-NEXT: Finished llvm::Module pass manager run.
 ; CHECK-O-NEXT: Running pass: PassManager<{{.*}}Module{{.*}}>
 ; CHECK-O-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PassManager{{.*}}>
 ; CHECK-O-NEXT: Starting llvm::Function pass manager run.
 ; CHECK-O-NEXT: Running pass: Float2IntPass
+; CHECK-EP-VECTORIZER-START-NEXT: Running pass: NoOpFunctionPass
 ; CHECK-O-NEXT: Running pass: FunctionToLoopPassAdaptor<{{.*}}LoopRotatePass
 ; CHECK-O-NEXT: Running pass: LoopDistributePass
 ; CHECK-O-NEXT: Running pass: LoopVectorizePass
index dfd2983..cab3965 100644 (file)
 ; RUN: opt -disable-verify -debug-pass-manager \
 ; RUN:     -passes='lto<Oz>' -S %s 2>&1 \
 ; RUN:     | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O2
+; RUN: opt -disable-verify -debug-pass-manager \
+; RUN:     -passes='lto<O3>' -S  %s -passes-ep-peephole='no-op-function' 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O2 \
+; RUN:     --check-prefix=CHECK-EP-Peephole
 
 ; CHECK-O: Starting llvm::Module pass manager run.
 ; CHECK-O-NEXT: Running pass: PassManager<{{.*}}Module
 ; CHECK-O2-NEXT: Running analysis: AssumptionAnalysis
 ; CHECK-O2-NEXT: Running pass: ConstantMergePass
 ; CHECK-O2-NEXT: Running pass: DeadArgumentEliminationPass
-; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}InstCombinePass>
+; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PassManager{{.*}}>
+; CHECK-O2-NEXT: Starting llvm::Function pass manager run.
+; CHECK-O2-NEXT: Running pass: InstCombinePass
+; CHECK-EP-Peephole-NEXT: Running pass: NoOpFunctionPass
+; CHECK-O2-NEXT: Finished llvm::Function pass manager run.
 ; CHECK-O2-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor<{{.*}}InlinerPass>
 ; CHECK-O2-NEXT: Running pass: GlobalOptPass
 ; CHECK-O2-NEXT: Running pass: GlobalDCEPass
 ; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PassManager{{.*}}>
 ; CHECK-O2-NEXT: Starting llvm::Function pass manager run.
 ; CHECK-O2-NEXT: Running pass: InstCombinePass
+; CHECK-EP-Peephole-NEXT: Running pass: NoOpFunctionPass
 ; CHECK-O2-NEXT: Running pass: JumpThreadingPass
 ; CHECK-O2-NEXT: Running analysis: LazyValueAnalysis
 ; CHECK-O2-NEXT: Running pass: SROA on foo
index 58e9cae..9672ce7 100644 (file)
@@ -48,6 +48,83 @@ static cl::opt<std::string>
                         "pipeline for handling managed aliasing queries"),
                cl::Hidden);
 
+/// {{@ These options accept textual pipeline descriptions which will be
+/// inserted into default pipelines at the respective extension points
+static cl::opt<std::string> PeepholeEPPipeline(
+    "passes-ep-peephole",
+    cl::desc("A textual description of the function pass pipeline inserted at "
+             "the Peephole extension points into default pipelines"),
+    cl::Hidden);
+static cl::opt<std::string> LateLoopOptimizationsEPPipeline(
+    "passes-ep-late-loop-optimizations",
+    cl::desc(
+        "A textual description of the loop pass pipeline inserted at "
+        "the LateLoopOptimizations extension point into default pipelines"),
+    cl::Hidden);
+static cl::opt<std::string> LoopOptimizerEndEPPipeline(
+    "passes-ep-loop-optimizer-end",
+    cl::desc("A textual description of the loop pass pipeline inserted at "
+             "the LoopOptimizerEnd extension point into default pipelines"),
+    cl::Hidden);
+static cl::opt<std::string> ScalarOptimizerLateEPPipeline(
+    "passes-ep-scalar-optimizer-late",
+    cl::desc("A textual description of the function pass pipeline inserted at "
+             "the ScalarOptimizerLate extension point into default pipelines"),
+    cl::Hidden);
+static cl::opt<std::string> CGSCCOptimizerLateEPPipeline(
+    "passes-ep-cgscc-optimizer-late",
+    cl::desc("A textual description of the cgscc pass pipeline inserted at "
+             "the CGSCCOptimizerLate extension point into default pipelines"),
+    cl::Hidden);
+static cl::opt<std::string> VectorizerStartEPPipeline(
+    "passes-ep-vectorizer-start",
+    cl::desc("A textual description of the function pass pipeline inserted at "
+             "the VectorizerStart extension point into default pipelines"),
+    cl::Hidden);
+/// @}}
+
+/// If one of the EPPipeline command line options was given, register callbacks
+/// for parsing and inserting the given pipeline
+static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass,
+                                bool DebugLogging) {
+  if (!PeepholeEPPipeline.empty())
+    PB.registerPeepholeEPCallback(
+        [&](FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
+          return PB.parsePassPipeline(PM, PeepholeEPPipeline, VerifyEachPass,
+                                      DebugPM);
+        });
+  if (!LateLoopOptimizationsEPPipeline.empty())
+    PB.registerLateLoopOptimizationsEPCallback(
+        [&](LoopPassManager &PM, PassBuilder::OptimizationLevel Level) {
+          return PB.parsePassPipeline(PM, LateLoopOptimizationsEPPipeline,
+                                      VerifyEachPass, DebugPM);
+        });
+  if (!LoopOptimizerEndEPPipeline.empty())
+    PB.registerLoopOptimizerEndEPCallback(
+        [&](LoopPassManager &PM, PassBuilder::OptimizationLevel Level) {
+          return PB.parsePassPipeline(PM, LoopOptimizerEndEPPipeline,
+                                      VerifyEachPass, DebugPM);
+        });
+  if (!ScalarOptimizerLateEPPipeline.empty())
+    PB.registerScalarOptimizerLateEPCallback(
+        [&](FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
+          return PB.parsePassPipeline(PM, ScalarOptimizerLateEPPipeline,
+                                      VerifyEachPass, DebugPM);
+        });
+  if (!CGSCCOptimizerLateEPPipeline.empty())
+    PB.registerCGSCCOptimizerLateEPCallback(
+        [&](CGSCCPassManager &PM, PassBuilder::OptimizationLevel Level) {
+          return PB.parsePassPipeline(PM, CGSCCOptimizerLateEPPipeline,
+                                      VerifyEachPass, DebugPM);
+        });
+  if (!VectorizerStartEPPipeline.empty())
+    PB.registerVectorizerStartEPCallback(
+        [&](FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
+          return PB.parsePassPipeline(PM, VectorizerStartEPPipeline,
+                                      VerifyEachPass, DebugPM);
+        });
+}
+
 bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
                            tool_output_file *Out,
                            tool_output_file *ThinLTOLinkOut,
@@ -56,7 +133,9 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
                            bool ShouldPreserveAssemblyUseListOrder,
                            bool ShouldPreserveBitcodeUseListOrder,
                            bool EmitSummaryIndex, bool EmitModuleHash) {
+  bool VerifyEachPass = VK == VK_VerifyEachPass;
   PassBuilder PB(TM);
+  registerEPCallbacks(PB, VerifyEachPass, DebugPM);
 
   // Specially handle the alias analysis manager so that we can register
   // a custom pipeline of AA passes with it.
@@ -85,8 +164,7 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
   if (VK > VK_NoVerifier)
     MPM.addPass(VerifierPass());
 
-  if (!PB.parsePassPipeline(MPM, PassPipeline, VK == VK_VerifyEachPass,
-                            DebugPM)) {
+  if (!PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) {
     errs() << Arg0 << ": unable to parse pass pipeline description.\n";
     return false;
   }
index 6734de8..d76ebfa 100644 (file)
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS
   AsmParser
   Core
   Support
+  Passes
   )
 
 set(IRSources
@@ -15,6 +16,7 @@ set(IRSources
   DebugTypeODRUniquingTest.cpp
   DominatorTreeTest.cpp
   FunctionTest.cpp
+  PassBuilderCallbacksTest.cpp
   IRBuilderTest.cpp
   InstructionsTest.cpp
   IntrinsicsTest.cpp
diff --git a/llvm/unittests/IR/PassBuilderCallbacksTest.cpp b/llvm/unittests/IR/PassBuilderCallbacksTest.cpp
new file mode 100644 (file)
index 0000000..df0b11f
--- /dev/null
@@ -0,0 +1,520 @@
+//===- unittests/IR/PassBuilderCallbacksTest.cpp - PB Callback Tests --===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <llvm/Analysis/CGSCCPassManager.h>
+#include <llvm/Analysis/LoopAnalysisManager.h>
+#include <llvm/AsmParser/Parser.h>
+#include <llvm/IR/LLVMContext.h>
+#include <llvm/IR/PassManager.h>
+#include <llvm/Passes/PassBuilder.h>
+#include <llvm/Support/SourceMgr.h>
+#include <llvm/Transforms/Scalar/LoopPassManager.h>
+
+using namespace llvm;
+
+namespace llvm {
+/// Provide an ostream operator for StringRef.
+///
+/// For convenience we provide a custom matcher below for IRUnit's and analysis
+/// result's getName functions, which most of the time returns a StringRef. The
+/// matcher makes use of this operator.
+static std::ostream &operator<<(std::ostream &O, StringRef S) {
+  return O << S.str();
+}
+}
+
+namespace {
+using testing::DoDefault;
+using testing::Return;
+using testing::Expectation;
+using testing::Invoke;
+using testing::WithArgs;
+using testing::_;
+
+/// \brief A CRTP base for analysis mock handles
+///
+/// This class reconciles mocking with the value semantics implementation of the
+/// AnalysisManager. Analysis mock handles should derive from this class and
+/// call \c setDefault() in their constroctur for wiring up the defaults defined
+/// by this base with their mock run() and invalidate() implementations.
+template <typename DerivedT, typename IRUnitT,
+          typename AnalysisManagerT = AnalysisManager<IRUnitT>,
+          typename... ExtraArgTs>
+class MockAnalysisHandleBase {
+public:
+  class Analysis : public AnalysisInfoMixin<Analysis> {
+    friend AnalysisInfoMixin<Analysis>;
+    friend MockAnalysisHandleBase;
+    static AnalysisKey Key;
+
+    DerivedT *Handle;
+
+    Analysis(DerivedT &Handle) : Handle(&Handle) {
+      static_assert(std::is_base_of<MockAnalysisHandleBase, DerivedT>::value,
+                    "Must pass the derived type to this template!");
+    }
+
+  public:
+    class Result {
+      friend MockAnalysisHandleBase;
+
+      DerivedT *Handle;
+
+      Result(DerivedT &Handle) : Handle(&Handle) {}
+
+    public:
+      // Forward invalidation events to the mock handle.
+      bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA,
+                      typename AnalysisManagerT::Invalidator &Inv) {
+        return Handle->invalidate(IR, PA, Inv);
+      }
+    };
+
+    Result run(IRUnitT &IR, AnalysisManagerT &AM, ExtraArgTs... ExtraArgs) {
+      return Handle->run(IR, AM, ExtraArgs...);
+    }
+  };
+
+  Analysis getAnalysis() { return Analysis(static_cast<DerivedT &>(*this)); }
+  typename Analysis::Result getResult() {
+    return typename Analysis::Result(static_cast<DerivedT &>(*this));
+  }
+
+protected:
+  // FIXME: MSVC seems unable to handle a lambda argument to Invoke from within
+  // the template, so we use a boring static function.
+  static bool invalidateCallback(IRUnitT &IR, const PreservedAnalyses &PA,
+                                 typename AnalysisManagerT::Invalidator &Inv) {
+    auto PAC = PA.template getChecker<Analysis>();
+    return !PAC.preserved() &&
+           !PAC.template preservedSet<AllAnalysesOn<IRUnitT>>();
+  }
+
+  /// Derived classes should call this in their constructor to set up default
+  /// mock actions. (We can't do this in our constructor because this has to
+  /// run after the DerivedT is constructed.)
+  void setDefaults() {
+    ON_CALL(static_cast<DerivedT &>(*this),
+            run(_, _, testing::Matcher<ExtraArgTs>(_)...))
+        .WillByDefault(Return(this->getResult()));
+    ON_CALL(static_cast<DerivedT &>(*this), invalidate(_, _, _))
+        .WillByDefault(Invoke(&invalidateCallback));
+  }
+};
+
+/// \brief A CRTP base for pass mock handles
+///
+/// This class reconciles mocking with the value semantics implementation of the
+/// PassManager. Pass mock handles should derive from this class and
+/// call \c setDefault() in their constroctur for wiring up the defaults defined
+/// by this base with their mock run() and invalidate() implementations.
+template <typename DerivedT, typename IRUnitT, typename AnalysisManagerT,
+          typename... ExtraArgTs>
+AnalysisKey MockAnalysisHandleBase<DerivedT, IRUnitT, AnalysisManagerT,
+                                   ExtraArgTs...>::Analysis::Key;
+
+template <typename DerivedT, typename IRUnitT,
+          typename AnalysisManagerT = AnalysisManager<IRUnitT>,
+          typename... ExtraArgTs>
+class MockPassHandleBase {
+public:
+  class Pass : public PassInfoMixin<Pass> {
+    friend MockPassHandleBase;
+
+    DerivedT *Handle;
+
+    Pass(DerivedT &Handle) : Handle(&Handle) {
+      static_assert(std::is_base_of<MockPassHandleBase, DerivedT>::value,
+                    "Must pass the derived type to this template!");
+    }
+
+  public:
+    PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM,
+                          ExtraArgTs... ExtraArgs) {
+      return Handle->run(IR, AM, ExtraArgs...);
+    }
+  };
+
+  Pass getPass() { return Pass(static_cast<DerivedT &>(*this)); }
+
+protected:
+  /// Derived classes should call this in their constructor to set up default
+  /// mock actions. (We can't do this in our constructor because this has to
+  /// run after the DerivedT is constructed.)
+  void setDefaults() {
+    ON_CALL(static_cast<DerivedT &>(*this),
+            run(_, _, testing::Matcher<ExtraArgTs>(_)...))
+        .WillByDefault(Return(PreservedAnalyses::all()));
+  }
+};
+
+/// Mock handles for passes for the IRUnits Module, CGSCC, Function, Loop.
+/// These handles define the appropriate run() mock interface for the respective
+/// IRUnit type.
+template <typename IRUnitT> struct MockPassHandle;
+template <>
+struct MockPassHandle<Loop>
+    : MockPassHandleBase<MockPassHandle<Loop>, Loop, LoopAnalysisManager,
+                         LoopStandardAnalysisResults &, LPMUpdater &> {
+  MOCK_METHOD4(run,
+               PreservedAnalyses(Loop &, LoopAnalysisManager &,
+                                 LoopStandardAnalysisResults &, LPMUpdater &));
+  MockPassHandle() { setDefaults(); }
+};
+
+template <>
+struct MockPassHandle<Function>
+    : MockPassHandleBase<MockPassHandle<Function>, Function> {
+  MOCK_METHOD2(run, PreservedAnalyses(Function &, FunctionAnalysisManager &));
+
+  MockPassHandle() { setDefaults(); }
+};
+
+template <>
+struct MockPassHandle<LazyCallGraph::SCC>
+    : MockPassHandleBase<MockPassHandle<LazyCallGraph::SCC>, LazyCallGraph::SCC,
+                         CGSCCAnalysisManager, LazyCallGraph &,
+                         CGSCCUpdateResult &> {
+  MOCK_METHOD4(run,
+               PreservedAnalyses(LazyCallGraph::SCC &, CGSCCAnalysisManager &,
+                                 LazyCallGraph &G, CGSCCUpdateResult &UR));
+
+  MockPassHandle() { setDefaults(); }
+};
+
+template <>
+struct MockPassHandle<Module>
+    : MockPassHandleBase<MockPassHandle<Module>, Module> {
+  MOCK_METHOD2(run, PreservedAnalyses(Module &, ModuleAnalysisManager &));
+
+  MockPassHandle() { setDefaults(); }
+};
+
+/// Mock handles for analyses for the IRUnits Module, CGSCC, Function, Loop.
+/// These handles define the appropriate run() and invalidate() mock interfaces
+/// for the respective IRUnit type.
+template <typename IRUnitT> struct MockAnalysisHandle;
+template <>
+struct MockAnalysisHandle<Loop>
+    : MockAnalysisHandleBase<MockAnalysisHandle<Loop>, Loop,
+                             LoopAnalysisManager,
+                             LoopStandardAnalysisResults &> {
+
+  MOCK_METHOD3_T(run, typename Analysis::Result(Loop &, LoopAnalysisManager &,
+                                                LoopStandardAnalysisResults &));
+
+  MOCK_METHOD3_T(invalidate, bool(Loop &, const PreservedAnalyses &,
+                                  LoopAnalysisManager::Invalidator &));
+
+  MockAnalysisHandle<Loop>() { this->setDefaults(); }
+};
+
+template <>
+struct MockAnalysisHandle<Function>
+    : MockAnalysisHandleBase<MockAnalysisHandle<Function>, Function> {
+  MOCK_METHOD2(run, Analysis::Result(Function &, FunctionAnalysisManager &));
+
+  MOCK_METHOD3(invalidate, bool(Function &, const PreservedAnalyses &,
+                                FunctionAnalysisManager::Invalidator &));
+
+  MockAnalysisHandle<Function>() { setDefaults(); }
+};
+
+template <>
+struct MockAnalysisHandle<LazyCallGraph::SCC>
+    : MockAnalysisHandleBase<MockAnalysisHandle<LazyCallGraph::SCC>,
+                             LazyCallGraph::SCC, CGSCCAnalysisManager,
+                             LazyCallGraph &> {
+  MOCK_METHOD3(run, Analysis::Result(LazyCallGraph::SCC &,
+                                     CGSCCAnalysisManager &, LazyCallGraph &));
+
+  MOCK_METHOD3(invalidate, bool(LazyCallGraph::SCC &, const PreservedAnalyses &,
+                                CGSCCAnalysisManager::Invalidator &));
+
+  MockAnalysisHandle<LazyCallGraph::SCC>() { setDefaults(); }
+};
+
+template <>
+struct MockAnalysisHandle<Module>
+    : MockAnalysisHandleBase<MockAnalysisHandle<Module>, Module> {
+  MOCK_METHOD2(run, Analysis::Result(Module &, ModuleAnalysisManager &));
+
+  MOCK_METHOD3(invalidate, bool(Module &, const PreservedAnalyses &,
+                                ModuleAnalysisManager::Invalidator &));
+
+  MockAnalysisHandle<Module>() { setDefaults(); }
+};
+
+static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
+  SMDiagnostic Err;
+  return parseAssemblyString(IR, Err, C);
+}
+
+template <typename PassManagerT> class PassBuilderCallbacksTest;
+
+/// This test fixture is shared between all the actual tests below and
+/// takes care of setting up appropriate defaults.
+///
+/// The template specialization serves to extract the IRUnit and AM types from
+/// the given PassManagerT.
+template <typename TestIRUnitT, typename... ExtraPassArgTs,
+          typename... ExtraAnalysisArgTs>
+class PassBuilderCallbacksTest<PassManager<
+    TestIRUnitT, AnalysisManager<TestIRUnitT, ExtraAnalysisArgTs...>,
+    ExtraPassArgTs...>> : public testing::Test {
+protected:
+  using IRUnitT = TestIRUnitT;
+  using AnalysisManagerT = AnalysisManager<TestIRUnitT, ExtraAnalysisArgTs...>;
+  using PassManagerT =
+      PassManager<TestIRUnitT, AnalysisManagerT, ExtraPassArgTs...>;
+  using AnalysisT = typename MockAnalysisHandle<IRUnitT>::Analysis;
+
+  LLVMContext Context;
+  std::unique_ptr<Module> M;
+
+  PassBuilder PB;
+  ModulePassManager PM;
+  LoopAnalysisManager LAM;
+  FunctionAnalysisManager FAM;
+  CGSCCAnalysisManager CGAM;
+  ModuleAnalysisManager AM;
+
+  MockPassHandle<IRUnitT> PassHandle;
+  MockAnalysisHandle<IRUnitT> AnalysisHandle;
+
+  static PreservedAnalyses getAnalysisResult(IRUnitT &U, AnalysisManagerT &AM,
+                                             ExtraAnalysisArgTs &&... Args) {
+    (void)AM.template getResult<AnalysisT>(
+        U, std::forward<ExtraAnalysisArgTs>(Args)...);
+    return PreservedAnalyses::all();
+  }
+
+  PassBuilderCallbacksTest()
+      : M(parseIR(Context,
+                  "declare void @bar()\n"
+                  "define void @foo(i32 %n) {\n"
+                  "entry:\n"
+                  "  br label %loop\n"
+                  "loop:\n"
+                  "  %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]\n"
+                  "  %iv.next = add i32 %iv, 1\n"
+                  "  tail call void @bar()\n"
+                  "  %cmp = icmp eq i32 %iv, %n\n"
+                  "  br i1 %cmp, label %exit, label %loop\n"
+                  "exit:\n"
+                  "  ret void\n"
+                  "}\n")),
+        PM(true), LAM(true), FAM(true), CGAM(true), AM(true) {
+
+    /// Register a callback for analysis registration.
+    ///
+    /// The callback is a function taking a reference to an AnalyisManager
+    /// object. When called, the callee gets to register its own analyses with
+    /// this PassBuilder instance.
+    PB.registerAnalysisRegistrationCallback([this](AnalysisManagerT &AM) {
+      // Register our mock analysis
+      AM.registerPass([this] { return AnalysisHandle.getAnalysis(); });
+    });
+
+    /// Register a callback for pipeline parsing.
+    ///
+    /// During parsing of a textual pipeline, the PassBuilder will call these
+    /// callbacks for each encountered pass name that it does not know. This
+    /// includes both simple pass names as well as names of sub-pipelines. In
+    /// the latter case, the InnerPipeline is not empty.
+    PB.registerPipelineParsingCallback(
+        [this](StringRef Name, PassManagerT &PM,
+               ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
+          /// Handle parsing of the names of analysis utilities such as
+          /// require<test-analysis> and invalidate<test-analysis> for our
+          /// analysis mock handle
+          if (parseAnalysisUtilityPasses<AnalysisT>("test-analysis", Name, PM))
+            return true;
+
+          /// Parse the name of our pass mock handle
+          if (Name == "test-transform") {
+            PM.addPass(PassHandle.getPass());
+            return true;
+          }
+          return false;
+        });
+
+    /// Register builtin analyses and cross-register the analysis proxies
+    PB.registerModuleAnalyses(AM);
+    PB.registerCGSCCAnalyses(CGAM);
+    PB.registerFunctionAnalyses(FAM);
+    PB.registerLoopAnalyses(LAM);
+    PB.crossRegisterProxies(LAM, FAM, CGAM, AM);
+  }
+};
+
+/// Define a custom matcher for objects which support a 'getName' method.
+///
+/// LLVM often has IR objects or analysis objects which expose a name
+/// and in tests it is convenient to match these by name for readability.
+/// Usually, this name is either a StringRef or a plain std::string. This
+/// matcher supports any type exposing a getName() method of this form whose
+/// return value is compatible with an std::ostream. For StringRef, this uses
+/// the shift operator defined above.
+///
+/// It should be used as:
+///
+///   HasName("my_function")
+///
+/// No namespace or other qualification is required.
+MATCHER_P(HasName, Name, "") {
+  *result_listener << "has name '" << arg.getName() << "'";
+  return Name == arg.getName();
+}
+
+using ModuleCallbacksTest = PassBuilderCallbacksTest<ModulePassManager>;
+using CGSCCCallbacksTest = PassBuilderCallbacksTest<CGSCCPassManager>;
+using FunctionCallbacksTest = PassBuilderCallbacksTest<FunctionPassManager>;
+using LoopCallbacksTest = PassBuilderCallbacksTest<LoopPassManager>;
+
+/// Test parsing of the name of our mock pass for all IRUnits.
+///
+/// The pass should by default run our mock analysis and then preserve it.
+TEST_F(ModuleCallbacksTest, Passes) {
+  EXPECT_CALL(AnalysisHandle, run(HasName("<string>"), _));
+  EXPECT_CALL(PassHandle, run(HasName("<string>"), _))
+      .WillOnce(Invoke(getAnalysisResult));
+
+  StringRef PipelineText = "test-transform";
+  ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
+      << "Pipeline was: " << PipelineText;
+  PM.run(*M, AM);
+}
+
+TEST_F(FunctionCallbacksTest, Passes) {
+  EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _));
+  EXPECT_CALL(PassHandle, run(HasName("foo"), _))
+      .WillOnce(Invoke(getAnalysisResult));
+
+  StringRef PipelineText = "test-transform";
+  ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
+      << "Pipeline was: " << PipelineText;
+  PM.run(*M, AM);
+}
+
+TEST_F(LoopCallbacksTest, Passes) {
+  EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _));
+  EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _))
+      .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult)));
+
+  StringRef PipelineText = "test-transform";
+  ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
+      << "Pipeline was: " << PipelineText;
+  PM.run(*M, AM);
+}
+
+TEST_F(CGSCCCallbacksTest, Passes) {
+  EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _));
+  EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _))
+      .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult)));
+
+  StringRef PipelineText = "test-transform";
+  ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
+      << "Pipeline was: " << PipelineText;
+  PM.run(*M, AM);
+}
+
+/// Test parsing of the names of analysis utilities for our mock analysis
+/// for all IRUnits.
+///
+/// We first require<>, then invalidate<> it, expecting the analysis to be run
+/// once and subsequently invalidated.
+TEST_F(ModuleCallbacksTest, AnalysisUtilities) {
+  EXPECT_CALL(AnalysisHandle, run(HasName("<string>"), _));
+  EXPECT_CALL(AnalysisHandle, invalidate(HasName("<string>"), _, _));
+
+  StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>";
+  ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
+      << "Pipeline was: " << PipelineText;
+  PM.run(*M, AM);
+}
+
+TEST_F(CGSCCCallbacksTest, PassUtilities) {
+  EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _));
+  EXPECT_CALL(AnalysisHandle, invalidate(HasName("(foo)"), _, _));
+
+  StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>";
+  ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
+      << "Pipeline was: " << PipelineText;
+  PM.run(*M, AM);
+}
+
+TEST_F(FunctionCallbacksTest, AnalysisUtilities) {
+  EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _));
+  EXPECT_CALL(AnalysisHandle, invalidate(HasName("foo"), _, _));
+
+  StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>";
+  ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
+      << "Pipeline was: " << PipelineText;
+  PM.run(*M, AM);
+}
+
+TEST_F(LoopCallbacksTest, PassUtilities) {
+  EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _));
+  EXPECT_CALL(AnalysisHandle, invalidate(HasName("loop"), _, _));
+
+  StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>";
+
+  ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
+      << "Pipeline was: " << PipelineText;
+  PM.run(*M, AM);
+}
+
+/// Test parsing of the top-level pipeline.
+///
+/// The ParseTopLevelPipeline callback takes over parsing of the entire pipeline
+/// from PassBuilder if it encounters an unknown pipeline entry at the top level
+/// (i.e., the first entry on the pipeline).
+/// This test parses a pipeline named 'another-pipeline', whose only elements
+/// may be the test-transform pass or the analysis utilities
+TEST_F(ModuleCallbacksTest, ParseTopLevelPipeline) {
+  PB.registerParseTopLevelPipelineCallback([this](
+      ModulePassManager &MPM, ArrayRef<PassBuilder::PipelineElement> Pipeline,
+      bool VerifyEachPass, bool DebugLogging) {
+    auto &FirstName = Pipeline.front().Name;
+    auto &InnerPipeline = Pipeline.front().InnerPipeline;
+    if (FirstName == "another-pipeline") {
+      for (auto &E : InnerPipeline) {
+        if (parseAnalysisUtilityPasses<AnalysisT>("test-analysis", E.Name, PM))
+          continue;
+
+        if (E.Name == "test-transform") {
+          PM.addPass(PassHandle.getPass());
+          continue;
+        }
+        return false;
+      }
+    }
+    return true;
+  });
+
+  EXPECT_CALL(AnalysisHandle, run(HasName("<string>"), _));
+  EXPECT_CALL(PassHandle, run(HasName("<string>"), _))
+      .WillOnce(Invoke(getAnalysisResult));
+  EXPECT_CALL(AnalysisHandle, invalidate(HasName("<string>"), _, _));
+
+  StringRef PipelineText =
+      "another-pipeline(test-transform,invalidate<test-analysis>)";
+  ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
+      << "Pipeline was: " << PipelineText;
+  PM.run(*M, AM);
+
+  /// Test the negative case
+  PipelineText = "another-pipeline(instcombine)";
+  ASSERT_FALSE(PB.parsePassPipeline(PM, PipelineText, true))
+      << "Pipeline was: " << PipelineText;
+}
+} // end anonymous namespace