-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[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
base: main
Are you sure you want to change the base?
[Pass] Add eraseIf
in pass managers and adaptors
#116912
Conversation
`llc` need thses methods to implement start stop options
This stack of pull requests is managed by Graphite. Learn more about stacking. |
@llvm/pr-subscribers-llvm-analysis @llvm/pr-subscribers-llvm-ir Author: None (paperchalice) Changes
Full diff: https://github.com/llvm/llvm-project/pull/116912.diff 6 Files Affected:
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,
|
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. |
Another idea is drop the |
Any particular reason for that? IIUC it is because adding passes in the legacy PM is costlier than in NPM? |
Ping @aeubanks |
llc
need thses methods to implement start stop options