diff --git a/llvm/include/llvm/Analysis/CGSCCPassManager.h b/llvm/include/llvm/Analysis/CGSCCPassManager.h --- a/llvm/include/llvm/Analysis/CGSCCPassManager.h +++ b/llvm/include/llvm/Analysis/CGSCCPassManager.h @@ -355,6 +355,10 @@ /// Runs the CGSCC pass across every SCC in the module. PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM); + static bool isSkippable() { + return !std::is_base_of::value; + } + private: CGSCCPassT Pass; }; @@ -543,6 +547,10 @@ return PA; } + static bool isSkippable() { + return !std::is_base_of::value; + } + private: FunctionPassT Pass; }; diff --git a/llvm/include/llvm/IR/PassInstrumentation.h b/llvm/include/llvm/IR/PassInstrumentation.h --- a/llvm/include/llvm/IR/PassInstrumentation.h +++ b/llvm/include/llvm/IR/PassInstrumentation.h @@ -124,11 +124,41 @@ AfterAnalysisCallbacks; }; +// Template argument PassT of PassInstrumentation::runBeforePass could be two +// kinds: (1) a regular pass inherited from PassInfoMixin (happen when creating +// a adaptor pass for a regular pass); (2) a type-erased PassConcept created +// from (1). Here we want to make case (1) skippable unconditionally since they +// are regular passes. We call PassConcept::isSkippable to decide for case (2). +template struct MaybeSkippablePass { +private: + template + static constexpr auto check(T *) -> + typename std::is_same().isSkippable()), + bool>::type; + template static constexpr std::false_type check(...); + + typedef decltype(check(0)) type; + +public: + static constexpr bool value = type::value; +}; + /// This class provides instrumentation entry points for the Pass Manager, /// doing calls to callbacks registered in PassInstrumentationCallbacks. class PassInstrumentation { PassInstrumentationCallbacks *Callbacks; + template + static std::enable_if_t::value, bool> + isSkippable(const PassT &Pass) { + return Pass.isSkippable(); + } + template + static std::enable_if_t::value, bool> + isSkippable(const PassT &Pass) { + return true; + } + public: /// Callbacks object is not owned by PassInstrumentation, its life-time /// should at least match the life-time of corresponding @@ -148,6 +178,7 @@ bool ShouldRun = true; for (auto &C : Callbacks->BeforePassCallbacks) ShouldRun &= C(Pass.name(), llvm::Any(&IR)); + ShouldRun = ShouldRun || !isSkippable(Pass); return ShouldRun; } diff --git a/llvm/include/llvm/IR/PassManager.h b/llvm/include/llvm/IR/PassManager.h --- a/llvm/include/llvm/IR/PassManager.h +++ b/llvm/include/llvm/IR/PassManager.h @@ -558,6 +558,8 @@ Passes.emplace_back(new PassModelT(std::move(Pass))); } + static bool isSkippable() { return false; } + private: using PassConceptT = detail::PassConcept; @@ -1259,6 +1261,10 @@ return PA; } + static bool isSkippable() { + return !std::is_base_of::value; + } + private: FunctionPassT Pass; }; diff --git a/llvm/include/llvm/IR/PassManagerInternal.h b/llvm/include/llvm/IR/PassManagerInternal.h --- a/llvm/include/llvm/IR/PassManagerInternal.h +++ b/llvm/include/llvm/IR/PassManagerInternal.h @@ -31,6 +31,20 @@ /// Implementation details of the pass manager interfaces. namespace detail { +template struct PassHasSkippableMethod { +private: + template + static constexpr auto check(T *) -> + typename std::is_same::type; + + template static constexpr std::false_type check(...); + + typedef decltype(check(0)) type; + +public: + static constexpr bool value = type::value; +}; + /// Template for the abstract base class used to dispatch /// polymorphically over pass objects. template @@ -48,6 +62,12 @@ /// Polymorphic method to access the name of a pass. virtual StringRef name() const = 0; + + /// Polymorphic method to to let a pass optionally exempted from skipping by + /// PassInstrumentation. + /// To opt-in, pass should implement `static bool isSkippable()`. It's no-op + /// to have `isSkippable` always return true since that is the default. + virtual bool isSkippable() const = 0; }; /// A template wrapper used to implement the polymorphic API. @@ -81,6 +101,19 @@ StringRef name() const override { return PassT::name(); } + template + static std::enable_if_t::value, bool> + passIsSkippable() { + return T::isSkippable(); + } + template + static std::enable_if_t::value, bool> + passIsSkippable() { + return true; + } + + bool isSkippable() const override { return passIsSkippable(); } + PassT Pass; }; diff --git a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h --- a/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h +++ b/llvm/include/llvm/Transforms/Scalar/LoopPassManager.h @@ -366,6 +366,10 @@ return PA; } + static bool isSkippable() { + return !std::is_base_of::value; + } + private: LoopPassT Pass; diff --git a/llvm/unittests/IR/PassBuilderCallbacksTest.cpp b/llvm/unittests/IR/PassBuilderCallbacksTest.cpp --- a/llvm/unittests/IR/PassBuilderCallbacksTest.cpp +++ b/llvm/unittests/IR/PassBuilderCallbacksTest.cpp @@ -524,10 +524,10 @@ // Non-mock instrumentation run here can safely be ignored. CallbacksHandle.ignoreNonMockPassInstrumentation(""); - // Skip the pass by returning false. - EXPECT_CALL(CallbacksHandle, runBeforePass(HasNameRegex("MockPassHandle"), - HasName(""))) - .WillOnce(Return(false)); + // Skip all passes by returning false. Pass managers and adaptor passes are + // also passes that observed by the callbacks. + EXPECT_CALL(CallbacksHandle, runBeforePass(_, _)) + .WillRepeatedly(Return(false)); EXPECT_CALL(AnalysisHandle, run(HasName(""), _)).Times(0); EXPECT_CALL(PassHandle, run(HasName(""), _)).Times(0); @@ -543,7 +543,60 @@ runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)) .Times(0); - StringRef PipelineText = "test-transform"; + // Order is important here. `Adaptor` expectations should be checked first + // because the its argument contains 'PassManager' (for example: + // ModuleToFunctionPassAdaptor{{.*}}PassManager{{.*}}). Here only check + // `runAfterPass` to show that they are not skipped. + + // Pass managers are not ignored. + // 5 = (1) ModulePassManager + (2) FunctionPassMangers + (1) LoopPassManager + + // (1) CGSCCPassManager + EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("PassManager"), _)) + .Times(5); + EXPECT_CALL(CallbacksHandle, + runAfterPass(HasNameRegex("ModuleToFunctionPassAdaptor"), _)) + .Times(1); + EXPECT_CALL( + CallbacksHandle, + runAfterPass(HasNameRegex("ModuleToPostOrderCGSCCPassAdaptor"), _)) + .Times(1); + EXPECT_CALL(CallbacksHandle, + runAfterPass(HasNameRegex("CGSCCToFunctionPassAdaptor"), _)) + .Times(1); + EXPECT_CALL(CallbacksHandle, + runAfterPass(HasNameRegex("FunctionToLoopPassAdaptor"), _)) + .Times(1); + + // Ignore analyses introduced by adaptor passes. + EXPECT_CALL(CallbacksHandle, + runBeforeAnalysis(Not(HasNameRegex("MockAnalysisHandle")), _)) + .Times(AnyNumber()); + EXPECT_CALL(CallbacksHandle, + runAfterAnalysis(Not(HasNameRegex("MockAnalysisHandle")), _)) + .Times(AnyNumber()); + + // Register Funtion and Loop version of "test-transform" for testing + PB.registerPipelineParsingCallback( + [](StringRef Name, FunctionPassManager &FPM, + ArrayRef) { + if (Name == "test-transform") { + FPM.addPass(MockPassHandle().getPass()); + return true; + } + return false; + }); + PB.registerPipelineParsingCallback( + [](StringRef Name, LoopPassManager &LPM, + ArrayRef) { + if (Name == "test-transform") { + LPM.addPass(MockPassHandle().getPass()); + return true; + } + return false; + }); + + StringRef PipelineText = "test-transform,function(test-transform),cgscc(" + "function(loop(test-transform)))"; ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) << "Pipeline was: " << PipelineText;