Skip to content

[Pass] Add eraseIf in pass managers and adaptors #116912

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

paperchalice
Copy link
Contributor

llc need thses methods to implement start stop options

`llc` need thses methods to implement start stop options
Copy link
Contributor Author

paperchalice commented Nov 20, 2024

@llvmbot
Copy link
Member

llvmbot commented Nov 20, 2024

@llvm/pr-subscribers-llvm-analysis
@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-llvm-ir

Author: None (paperchalice)

Changes

llc need thses methods to implement start stop options


Full diff: https://github.com/llvm/llvm-project/pull/116912.diff

6 Files Affected:

  • (modified) llvm/include/llvm/Analysis/CGSCCPassManager.h (+12-17)
  • (modified) llvm/include/llvm/CodeGen/MachinePassManager.h (+4-8)
  • (modified) llvm/include/llvm/IR/PassManager.h (+47-5)
  • (modified) llvm/include/llvm/IR/PassManagerInternal.h (+34)
  • (modified) llvm/include/llvm/Transforms/Scalar/LoopPassManager.h (+9-8)
  • (modified) llvm/lib/Transforms/Scalar/LoopPassManager.cpp (+39)
diff --git a/llvm/include/llvm/Analysis/CGSCCPassManager.h b/llvm/include/llvm/Analysis/CGSCCPassManager.h
index 15b7f226fd8283..f3cbfabe32719e 100644
--- a/llvm/include/llvm/Analysis/CGSCCPassManager.h
+++ b/llvm/include/llvm/Analysis/CGSCCPassManager.h
@@ -311,17 +311,16 @@ struct CGSCCUpdateResult {
 /// pass over the module to enable a \c FunctionAnalysisManager to be used
 /// within this run safely.
 class ModuleToPostOrderCGSCCPassAdaptor
-    : public PassInfoMixin<ModuleToPostOrderCGSCCPassAdaptor> {
+    : public PassInfoMixin<ModuleToPostOrderCGSCCPassAdaptor>,
+      public PassAdaptorMixin<
+          detail::PassConcept<LazyCallGraph::SCC, CGSCCAnalysisManager,
+                              LazyCallGraph &, CGSCCUpdateResult &>> {
 public:
-  using PassConceptT =
-      detail::PassConcept<LazyCallGraph::SCC, CGSCCAnalysisManager,
-                          LazyCallGraph &, CGSCCUpdateResult &>;
-
   explicit ModuleToPostOrderCGSCCPassAdaptor(std::unique_ptr<PassConceptT> Pass)
-      : Pass(std::move(Pass)) {}
+      : PassAdaptorMixin(std::move(Pass)) {}
 
   ModuleToPostOrderCGSCCPassAdaptor(ModuleToPostOrderCGSCCPassAdaptor &&Arg)
-      : Pass(std::move(Arg.Pass)) {}
+      : PassAdaptorMixin(std::move(Arg.Pass)) {}
 
   friend void swap(ModuleToPostOrderCGSCCPassAdaptor &LHS,
                    ModuleToPostOrderCGSCCPassAdaptor &RHS) {
@@ -345,9 +344,6 @@ class ModuleToPostOrderCGSCCPassAdaptor
   }
 
   static bool isRequired() { return true; }
-
-private:
-  std::unique_ptr<PassConceptT> Pass;
 };
 
 /// A function to deduce a function pass type and wrap it in the
@@ -441,18 +437,18 @@ LazyCallGraph::SCC &updateCGAndAnalysisManagerForCGSCCPass(
 /// pass over the SCC to enable a \c FunctionAnalysisManager to be used
 /// within this run safely.
 class CGSCCToFunctionPassAdaptor
-    : public PassInfoMixin<CGSCCToFunctionPassAdaptor> {
+    : public PassInfoMixin<CGSCCToFunctionPassAdaptor>,
+      public PassAdaptorMixin<
+          detail::PassConcept<Function, FunctionAnalysisManager>> {
 public:
-  using PassConceptT = detail::PassConcept<Function, FunctionAnalysisManager>;
-
   explicit CGSCCToFunctionPassAdaptor(std::unique_ptr<PassConceptT> Pass,
                                       bool EagerlyInvalidate, bool NoRerun)
-      : Pass(std::move(Pass)), EagerlyInvalidate(EagerlyInvalidate),
+      : PassAdaptorMixin(std::move(Pass)), EagerlyInvalidate(EagerlyInvalidate),
         NoRerun(NoRerun) {}
 
   CGSCCToFunctionPassAdaptor(CGSCCToFunctionPassAdaptor &&Arg)
-      : Pass(std::move(Arg.Pass)), EagerlyInvalidate(Arg.EagerlyInvalidate),
-        NoRerun(Arg.NoRerun) {}
+      : PassAdaptorMixin(std::move(Arg.Pass)),
+        EagerlyInvalidate(Arg.EagerlyInvalidate), NoRerun(Arg.NoRerun) {}
 
   friend void swap(CGSCCToFunctionPassAdaptor &LHS,
                    CGSCCToFunctionPassAdaptor &RHS) {
@@ -489,7 +485,6 @@ class CGSCCToFunctionPassAdaptor
   static bool isRequired() { return true; }
 
 private:
-  std::unique_ptr<PassConceptT> Pass;
   bool EagerlyInvalidate;
   bool NoRerun;
 };
diff --git a/llvm/include/llvm/CodeGen/MachinePassManager.h b/llvm/include/llvm/CodeGen/MachinePassManager.h
index 69b5f6e92940c4..aab2249a0a740e 100644
--- a/llvm/include/llvm/CodeGen/MachinePassManager.h
+++ b/llvm/include/llvm/CodeGen/MachinePassManager.h
@@ -191,14 +191,13 @@ class FunctionAnalysisManagerMachineFunctionProxy
 };
 
 class FunctionToMachineFunctionPassAdaptor
-    : public PassInfoMixin<FunctionToMachineFunctionPassAdaptor> {
+    : public PassInfoMixin<FunctionToMachineFunctionPassAdaptor>,
+      public PassAdaptorMixin<detail::PassConcept<
+          MachineFunction, MachineFunctionAnalysisManager>> {
 public:
-  using PassConceptT =
-      detail::PassConcept<MachineFunction, MachineFunctionAnalysisManager>;
-
   explicit FunctionToMachineFunctionPassAdaptor(
       std::unique_ptr<PassConceptT> Pass)
-      : Pass(std::move(Pass)) {}
+      : PassAdaptorMixin(std::move(Pass)) {}
 
   /// Runs the function pass across every function in the function.
   PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM);
@@ -206,9 +205,6 @@ class FunctionToMachineFunctionPassAdaptor
                      function_ref<StringRef(StringRef)> MapClassName2PassName);
 
   static bool isRequired() { return true; }
-
-private:
-  std::unique_ptr<PassConceptT> Pass;
 };
 
 template <typename MachineFunctionPassT>
diff --git a/llvm/include/llvm/IR/PassManager.h b/llvm/include/llvm/IR/PassManager.h
index 5dab9d0d0a7979..8f3bcde89c4e65 100644
--- a/llvm/include/llvm/IR/PassManager.h
+++ b/llvm/include/llvm/IR/PassManager.h
@@ -218,6 +218,22 @@ class PassManager : public PassInfoMixin<
 
   static bool isRequired() { return true; }
 
+  /// Erase all passes that satisfy the predicate \p Pred.
+  /// For internal use only!
+  void eraseIf(function_ref<bool(StringRef)> Pred) {
+    for (auto I = Passes.begin(); I != Passes.end();) {
+      auto &P = *I;
+      P->eraseIf(Pred);
+      bool IsSpecial = P->name().ends_with("PassAdaptor") ||
+                       P->name().contains("PassManager");
+      bool PredResult = Pred(P->name());
+      if ((!IsSpecial && PredResult) || (IsSpecial && P->isEmpty()))
+        I = Passes.erase(I);
+      else
+        ++I;
+    }
+  }
+
 protected:
   using PassConceptT =
       detail::PassConcept<IRUnitT, AnalysisManagerT, ExtraArgTs...>;
@@ -801,6 +817,32 @@ extern template class OuterAnalysisManagerProxy<ModuleAnalysisManager,
 using ModuleAnalysisManagerFunctionProxy =
     OuterAnalysisManagerProxy<ModuleAnalysisManager, Function>;
 
+/// Simple mix-in for pass adaptor. If adaptor contains only a single pass
+/// instance in it, then it can inherit this mix-in to get default `isEmpty()`
+/// and `eraseIf` implementation. This mix-in must have access to the `Pass`
+/// member in adaptor.
+template <typename InternalConceptT> struct PassAdaptorMixin {
+  using PassConceptT = InternalConceptT;
+
+  bool isEmpty() const { return Pass == nullptr; }
+
+  void eraseIf(function_ref<bool(StringRef)> Pred) {
+    StringRef PassName = Pass->name();
+    if (PassName.contains("PassManager") || PassName.ends_with("PassAdaptor")) {
+      Pass->eraseIf(Pred);
+      if (Pass->isEmpty())
+        Pass.reset();
+    } else if (Pred(PassName)) {
+      Pass.reset();
+    }
+  }
+
+protected:
+  PassAdaptorMixin(std::unique_ptr<PassConceptT> Pass)
+      : Pass(std::move(Pass)) {}
+  std::unique_ptr<PassConceptT> Pass;
+};
+
 /// Trivial adaptor that maps from a module to its functions.
 ///
 /// Designed to allow composition of a FunctionPass(Manager) and
@@ -825,13 +867,14 @@ using ModuleAnalysisManagerFunctionProxy =
 /// analyses are not invalidated while the function passes are running, so they
 /// may be stale.  Function analyses will not be stale.
 class ModuleToFunctionPassAdaptor
-    : public PassInfoMixin<ModuleToFunctionPassAdaptor> {
+    : public PassInfoMixin<ModuleToFunctionPassAdaptor>,
+      public PassAdaptorMixin<
+          detail::PassConcept<Function, FunctionAnalysisManager>> {
 public:
-  using PassConceptT = detail::PassConcept<Function, FunctionAnalysisManager>;
-
   explicit ModuleToFunctionPassAdaptor(std::unique_ptr<PassConceptT> Pass,
                                        bool EagerlyInvalidate)
-      : Pass(std::move(Pass)), EagerlyInvalidate(EagerlyInvalidate) {}
+      : PassAdaptorMixin(std::move(Pass)),
+        EagerlyInvalidate(EagerlyInvalidate) {}
 
   /// Runs the function pass across every function in the module.
   PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
@@ -841,7 +884,6 @@ class ModuleToFunctionPassAdaptor
   static bool isRequired() { return true; }
 
 private:
-  std::unique_ptr<PassConceptT> Pass;
   bool EagerlyInvalidate;
 };
 
diff --git a/llvm/include/llvm/IR/PassManagerInternal.h b/llvm/include/llvm/IR/PassManagerInternal.h
index 4ada6ee5dd6831..36caf7cd85a3d5 100644
--- a/llvm/include/llvm/IR/PassManagerInternal.h
+++ b/llvm/include/llvm/IR/PassManagerInternal.h
@@ -59,6 +59,13 @@ struct PassConcept {
   /// To opt-in, pass should implement `static bool isRequired()`. It's no-op
   /// to have `isRequired` always return false since that is the default.
   virtual bool isRequired() const = 0;
+
+  /// Polymorphic method to refurbish pass pipeline.
+  virtual void eraseIf(function_ref<bool(StringRef)> Pred) = 0;
+
+  /// There may be some empty PassManager after erasing,
+  /// use it to remove them.
+  virtual bool isEmpty() const = 0;
 };
 
 /// A template wrapper used to implement the polymorphic API.
@@ -114,6 +121,33 @@ struct PassModel : PassConcept<IRUnitT, AnalysisManagerT, ExtraArgTs...> {
 
   bool isRequired() const override { return passIsRequiredImpl<PassT>(); }
 
+  template <typename T>
+  using has_erase_if_t = decltype(std::declval<T &>().eraseIf(
+      std::declval<function_ref<bool(StringRef)>>()));
+
+  template <typename T>
+  std::enable_if_t<is_detected<has_erase_if_t, T>::value>
+  eraseIfImpl(function_ref<bool(StringRef)> Pred) {
+    Pass.eraseIf(Pred);
+  }
+
+  template <typename T>
+  std::enable_if_t<!is_detected<has_erase_if_t, T>::value>
+  eraseIfImpl(function_ref<bool(StringRef)>) {}
+
+  void eraseIf(function_ref<bool(StringRef)> Pred) override {
+    eraseIfImpl<PassT>(Pred);
+  }
+
+  template <typename T>
+  using has_is_empty_t = decltype(std::declval<T &>().isEmpty());
+
+  bool isEmpty() const override {
+    if constexpr (is_detected<has_is_empty_t, PassT>::value)
+      return Pass.isEmpty();
+    return false;
+  }
+
   PassT Pass;
 };
 
diff --git a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h
index f55022fbff07c1..42d00da93b31ca 100644
--- a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h
+++ b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h
@@ -134,6 +134,10 @@ class PassManager<Loop, LoopAnalysisManager, LoopStandardAnalysisResults &,
 
   static bool isRequired() { return true; }
 
+  /// Erase all passes that satisfy the predicate \p Pred.
+  /// For internal use only!
+  void eraseIf(function_ref<bool(StringRef)> Pred);
+
   size_t getNumLoopPasses() const { return LoopPasses.size(); }
   size_t getNumLoopNestPasses() const { return LoopNestPasses.size(); }
 
@@ -399,18 +403,17 @@ std::optional<PreservedAnalyses> LoopPassManager::runSinglePass(
 /// \fn createLoopFunctionToLoopPassAdaptor to see when loop mode and loop-nest
 /// mode are used.
 class FunctionToLoopPassAdaptor
-    : public PassInfoMixin<FunctionToLoopPassAdaptor> {
+    : public PassInfoMixin<FunctionToLoopPassAdaptor>,
+      public PassAdaptorMixin<
+          detail::PassConcept<Loop, LoopAnalysisManager,
+                              LoopStandardAnalysisResults &, LPMUpdater &>> {
 public:
-  using PassConceptT =
-      detail::PassConcept<Loop, LoopAnalysisManager,
-                          LoopStandardAnalysisResults &, LPMUpdater &>;
-
   explicit FunctionToLoopPassAdaptor(std::unique_ptr<PassConceptT> Pass,
                                      bool UseMemorySSA = false,
                                      bool UseBlockFrequencyInfo = false,
                                      bool UseBranchProbabilityInfo = false,
                                      bool LoopNestMode = false)
-      : Pass(std::move(Pass)), UseMemorySSA(UseMemorySSA),
+      : PassAdaptorMixin(std::move(Pass)), UseMemorySSA(UseMemorySSA),
         UseBlockFrequencyInfo(UseBlockFrequencyInfo),
         UseBranchProbabilityInfo(UseBranchProbabilityInfo),
         LoopNestMode(LoopNestMode) {
@@ -428,8 +431,6 @@ class FunctionToLoopPassAdaptor
   bool isLoopNestMode() const { return LoopNestMode; }
 
 private:
-  std::unique_ptr<PassConceptT> Pass;
-
   FunctionPassManager LoopCanonicalizationFPM;
 
   bool UseMemorySSA = false;
diff --git a/llvm/lib/Transforms/Scalar/LoopPassManager.cpp b/llvm/lib/Transforms/Scalar/LoopPassManager.cpp
index 9f4270f5d62f5c..edbe017dae35f4 100644
--- a/llvm/lib/Transforms/Scalar/LoopPassManager.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopPassManager.cpp
@@ -62,6 +62,45 @@ void PassManager<Loop, LoopAnalysisManager, LoopStandardAnalysisResults &,
   }
 }
 
+void PassManager<Loop, LoopAnalysisManager, LoopStandardAnalysisResults &,
+                 LPMUpdater &>::eraseIf(function_ref<bool(StringRef)> Pred) {
+  assert(LoopPasses.size() + LoopNestPasses.size() == IsLoopNestPass.size() &&
+         "Wrong precondition!");
+
+  std::vector<char> IsLoopNestPassVec(
+      static_cast<size_t>(IsLoopNestPass.size()));
+  for (unsigned Idx = 0, Sz = IsLoopNestPass.size(); Idx != Sz; ++Idx)
+    IsLoopNestPassVec[Idx] = IsLoopNestPass[Idx];
+
+  auto ILP = LoopPasses.begin();
+  auto ILNP = LoopNestPasses.begin();
+  for (auto I = IsLoopNestPassVec.begin(); I != IsLoopNestPassVec.end();) {
+    if (*I) {
+      if (Pred((*ILNP)->name())) {
+        I = IsLoopNestPassVec.erase(I);
+        ILNP = LoopNestPasses.erase(ILNP);
+        continue;
+      }
+      ++ILNP;
+    } else {
+      if (Pred((*ILP)->name())) {
+        I = IsLoopNestPassVec.erase(I);
+        ILP = LoopPasses.erase(ILP);
+        continue;
+      }
+      ++ILP;
+    }
+    ++I;
+  }
+
+  IsLoopNestPass.clear();
+  for (const auto I : IsLoopNestPassVec)
+    IsLoopNestPass.push_back(I);
+
+  assert(LoopPasses.size() + LoopNestPasses.size() == IsLoopNestPass.size() &&
+         "Wrong postcondition!");
+}
+
 // Run both loop passes and loop-nest passes on top-level loop \p L.
 PreservedAnalyses
 LoopPassManager::runWithLoopNestPasses(Loop &L, LoopAnalysisManager &AM,

@aeubanks
Copy link
Contributor

the legacy pass manager does the filtering when adding passes rather than after the fact, and I like that better than examining passes after the fact. and I thought we already handled this here. I think the description in this PR/#116913 is lacking the motivation behind over the current implementation

@paperchalice
Copy link
Contributor Author

the legacy pass manager does the filtering when adding passes rather than after the fact, and I like that better than examining passes after the fact. and I thought we already handled this here. I think the description in this PR/#116913 is lacking the motivation behind over the current implementation

Updated description in #116913.

@paperchalice
Copy link
Contributor Author

Another idea is drop the start/stop support, let user specify passes explicitly in tests, then llc needs to do some extra works, e.g. check input file type.

@optimisan
Copy link
Contributor

optimisan commented Dec 2, 2024

the legacy pass manager does the filtering when adding passes rather than after the fact, and I like that better than examining passes after the fact

Any particular reason for that? IIUC it is because adding passes in the legacy PM is costlier than in NPM?
But I don't get if there's a specific motivation in NPM to keep the same behaviour to filter only while adding passes.

@paperchalice
Copy link
Contributor Author

Ping @aeubanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants