| //===- unittests/IR/PassBuilderCallbacksTest.cpp - PB Callback Tests --===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/Testing/Support/Error.h" |
| #include <functional> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <llvm/ADT/Any.h> |
| #include <llvm/Analysis/CGSCCPassManager.h> |
| #include <llvm/Analysis/LoopAnalysisManager.h> |
| #include <llvm/AsmParser/Parser.h> |
| #include <llvm/IR/LLVMContext.h> |
| #include <llvm/IR/PassInstrumentation.h> |
| #include <llvm/IR/PassManager.h> |
| #include <llvm/Passes/PassBuilder.h> |
| #include <llvm/Support/Regex.h> |
| #include <llvm/Support/SourceMgr.h> |
| #include <llvm/Transforms/Scalar/LoopPassManager.h> |
| |
| using namespace llvm; |
| |
| namespace { |
| using testing::AnyNumber; |
| using testing::DoAll; |
| using testing::Not; |
| using testing::Return; |
| using testing::WithArgs; |
| using testing::_; |
| |
| /// 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)); |
| } |
| static StringRef getName() { return llvm::getTypeName<DerivedT>(); } |
| |
| 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(&invalidateCallback); |
| } |
| }; |
| |
| /// 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...); |
| } |
| }; |
| |
| static StringRef getName() { return llvm::getTypeName<DerivedT>(); } |
| |
| 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 &)); |
| static void invalidateLoop(Loop &L, LoopAnalysisManager &, |
| LoopStandardAnalysisResults &, |
| LPMUpdater &Updater) { |
| Updater.markLoopAsDeleted(L, L.getName()); |
| } |
| MockPassHandle() { setDefaults(); } |
| }; |
| |
| template <> |
| struct MockPassHandle<LoopNest> |
| : MockPassHandleBase<MockPassHandle<LoopNest>, LoopNest, |
| LoopAnalysisManager, LoopStandardAnalysisResults &, |
| LPMUpdater &> { |
| MOCK_METHOD4(run, |
| PreservedAnalyses(LoopNest &, LoopAnalysisManager &, |
| LoopStandardAnalysisResults &, LPMUpdater &)); |
| static void invalidateLoopNest(LoopNest &L, LoopAnalysisManager &, |
| LoopStandardAnalysisResults &, |
| LPMUpdater &Updater) { |
| Updater.markLoopAsDeleted(L.getOutermostLoop(), L.getName()); |
| } |
| 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)); |
| |
| static void invalidateSCC(LazyCallGraph::SCC &C, CGSCCAnalysisManager &, |
| LazyCallGraph &, CGSCCUpdateResult &UR) { |
| UR.InvalidatedSCCs.insert(&C); |
| } |
| |
| 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); |
| } |
| |
| /// Helper for HasName matcher that returns getName both for IRUnit and |
| /// for IRUnit pointer wrapper into llvm::Any (wrapped by PassInstrumentation). |
| template <typename IRUnitT> std::string getName(const IRUnitT &IR) { |
| return std::string(IR.getName()); |
| } |
| |
| template <> std::string getName(const StringRef &name) { |
| return std::string(name); |
| } |
| |
| template <> std::string getName(const llvm::Any &WrappedIR) { |
| if (any_isa<const Module *>(WrappedIR)) |
| return any_cast<const Module *>(WrappedIR)->getName().str(); |
| if (any_isa<const Function *>(WrappedIR)) |
| return any_cast<const Function *>(WrappedIR)->getName().str(); |
| if (any_isa<const Loop *>(WrappedIR)) |
| return any_cast<const Loop *>(WrappedIR)->getName().str(); |
| if (any_isa<const LoopNest *>(WrappedIR)) |
| return any_cast<const LoopNest *>(WrappedIR)->getName().str(); |
| if (any_isa<const LazyCallGraph::SCC *>(WrappedIR)) |
| return any_cast<const LazyCallGraph::SCC *>(WrappedIR)->getName(); |
| return "<UNKNOWN>"; |
| } |
| /// 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 '" << getName(arg) << "'"; |
| return Name == getName(arg); |
| } |
| |
| MATCHER_P(HasNameRegex, Name, "") { |
| *result_listener << "has name '" << getName(arg) << "'"; |
| llvm::Regex r(Name); |
| return r.match(getName(arg)); |
| } |
| |
| struct MockPassInstrumentationCallbacks { |
| PassInstrumentationCallbacks Callbacks; |
| |
| MockPassInstrumentationCallbacks() { |
| ON_CALL(*this, runBeforePass(_, _)).WillByDefault(Return(true)); |
| } |
| MOCK_METHOD2(runBeforePass, bool(StringRef PassID, llvm::Any)); |
| MOCK_METHOD2(runBeforeSkippedPass, void(StringRef PassID, llvm::Any)); |
| MOCK_METHOD2(runBeforeNonSkippedPass, void(StringRef PassID, llvm::Any)); |
| MOCK_METHOD3(runAfterPass, |
| void(StringRef PassID, llvm::Any, const PreservedAnalyses &PA)); |
| MOCK_METHOD2(runAfterPassInvalidated, |
| void(StringRef PassID, const PreservedAnalyses &PA)); |
| MOCK_METHOD2(runBeforeAnalysis, void(StringRef PassID, llvm::Any)); |
| MOCK_METHOD2(runAfterAnalysis, void(StringRef PassID, llvm::Any)); |
| |
| void registerPassInstrumentation() { |
| Callbacks.registerShouldRunOptionalPassCallback( |
| [this](StringRef P, llvm::Any IR) { |
| return this->runBeforePass(P, IR); |
| }); |
| Callbacks.registerBeforeSkippedPassCallback( |
| [this](StringRef P, llvm::Any IR) { |
| this->runBeforeSkippedPass(P, IR); |
| }); |
| Callbacks.registerBeforeNonSkippedPassCallback( |
| [this](StringRef P, llvm::Any IR) { |
| this->runBeforeNonSkippedPass(P, IR); |
| }); |
| Callbacks.registerAfterPassCallback( |
| [this](StringRef P, llvm::Any IR, const PreservedAnalyses &PA) { |
| this->runAfterPass(P, IR, PA); |
| }); |
| Callbacks.registerAfterPassInvalidatedCallback( |
| [this](StringRef P, const PreservedAnalyses &PA) { |
| this->runAfterPassInvalidated(P, PA); |
| }); |
| Callbacks.registerBeforeAnalysisCallback([this](StringRef P, llvm::Any IR) { |
| return this->runBeforeAnalysis(P, IR); |
| }); |
| Callbacks.registerAfterAnalysisCallback( |
| [this](StringRef P, llvm::Any IR) { this->runAfterAnalysis(P, IR); }); |
| } |
| |
| void ignoreNonMockPassInstrumentation(StringRef IRName) { |
| // Generic EXPECT_CALLs are needed to match instrumentation on unimportant |
| // parts of a pipeline that we do not care about (e.g. various passes added |
| // by default by PassBuilder - Verifier pass etc). |
| // Make sure to avoid ignoring Mock passes/analysis, we definitely want |
| // to check these explicitly. |
| EXPECT_CALL(*this, |
| runBeforePass(Not(HasNameRegex("Mock")), HasName(IRName))) |
| .Times(AnyNumber()); |
| EXPECT_CALL( |
| *this, runBeforeSkippedPass(Not(HasNameRegex("Mock")), HasName(IRName))) |
| .Times(AnyNumber()); |
| EXPECT_CALL(*this, runBeforeNonSkippedPass(Not(HasNameRegex("Mock")), |
| HasName(IRName))) |
| .Times(AnyNumber()); |
| EXPECT_CALL(*this, |
| runAfterPass(Not(HasNameRegex("Mock")), HasName(IRName), _)) |
| .Times(AnyNumber()); |
| EXPECT_CALL(*this, |
| runBeforeAnalysis(Not(HasNameRegex("Mock")), HasName(IRName))) |
| .Times(AnyNumber()); |
| EXPECT_CALL(*this, |
| runAfterAnalysis(Not(HasNameRegex("Mock")), HasName(IRName))) |
| .Times(AnyNumber()); |
| } |
| }; |
| |
| template <typename IRUnitT> |
| using ExtraMockPassHandle = |
| std::conditional_t<std::is_same<IRUnitT, Loop>::value, |
| MockPassHandle<LoopNest>, MockPassHandle<IRUnitT>>; |
| |
| 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; |
| |
| MockPassInstrumentationCallbacks CallbacksHandle; |
| |
| PassBuilder PB; |
| ModulePassManager PM; |
| LoopAnalysisManager LAM; |
| FunctionAnalysisManager FAM; |
| CGSCCAnalysisManager CGAM; |
| ModuleAnalysisManager AM; |
| |
| MockPassHandle<IRUnitT> PassHandle; |
| ExtraMockPassHandle<IRUnitT> ExtraPassHandle; |
| |
| 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")), |
| CallbacksHandle(), |
| PB(nullptr, PipelineTuningOptions(), None, &CallbacksHandle.Callbacks), |
| PM(), LAM(), FAM(), CGAM(), AM() { |
| |
| EXPECT_TRUE(&CallbacksHandle.Callbacks == |
| PB.getPassInstrumentationCallbacks()); |
| |
| /// 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()); |
| if (std::is_same<IRUnitT, Loop>::value) |
| PM.addPass(ExtraPassHandle.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); |
| } |
| }; |
| |
| 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(&getAnalysisResult); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(ModuleCallbacksTest, InstrumentedPasses) { |
| EXPECT_CALL(AnalysisHandle, run(HasName("<string>"), _)); |
| EXPECT_CALL(PassHandle, run(HasName("<string>"), _)) |
| .WillOnce(&getAnalysisResult); |
| |
| CallbacksHandle.registerPassInstrumentation(); |
| // Non-mock instrumentation not specifically mentioned below can be ignored. |
| CallbacksHandle.ignoreNonMockPassInstrumentation("<string>"); |
| |
| // PassInstrumentation calls should happen in-sequence, in the same order |
| // as passes/analyses are scheduled. |
| ::testing::Sequence PISequence; |
| EXPECT_CALL(CallbacksHandle, runBeforePass(HasNameRegex("MockPassHandle"), |
| HasName("<string>"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), |
| HasName("<string>"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), |
| HasName("<string>"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("<string>"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle"), |
| HasName("<string>"), _)) |
| .InSequence(PISequence); |
| |
| // No passes are skipped, so there should be no calls to |
| // runBeforeSkippedPass(). |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("<string>"))) |
| .Times(0); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(ModuleCallbacksTest, InstrumentedSkippedPasses) { |
| CallbacksHandle.registerPassInstrumentation(); |
| // Non-mock instrumentation run here can safely be ignored. |
| CallbacksHandle.ignoreNonMockPassInstrumentation("<string>"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); |
| |
| // 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(CallbacksHandle, |
| runBeforeSkippedPass(HasNameRegex("MockPassHandle"), _)) |
| .Times(3); |
| |
| EXPECT_CALL(AnalysisHandle, run(HasName("<string>"), _)).Times(0); |
| EXPECT_CALL(PassHandle, run(HasName("<string>"), _)).Times(0); |
| |
| // As the pass is skipped there is no nonskippedpass/afterPass, |
| // beforeAnalysis/afterAnalysis as well. |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle"), _, _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)) |
| .Times(0); |
| |
| // Order is important here. `Adaptor` expectations should be checked first |
| // because the its argument contains 'PassManager' (for example: |
| // ModuleToFunctionPassAdaptor{{.*}}PassManager{{.*}}). Check |
| // `runBeforeNonSkippedPass` and `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, |
| runBeforeNonSkippedPass(HasNameRegex("PassManager"), _)) |
| .Times(5); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("ModuleToFunctionPassAdaptor"), _)) |
| .Times(1); |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeNonSkippedPass( |
| HasNameRegex("ModuleToPostOrderCGSCCPassAdaptor"), _)) |
| .Times(1); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("CGSCCToFunctionPassAdaptor"), _)) |
| .Times(1); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("FunctionToLoopPassAdaptor"), _)) |
| .Times(1); |
| |
| // The `runAfterPass` checks are the same as these of |
| // `runBeforeNonSkippedPass`. |
| 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<PassBuilder::PipelineElement>) { |
| if (Name == "test-transform") { |
| FPM.addPass(MockPassHandle<Function>().getPass()); |
| return true; |
| } |
| return false; |
| }); |
| PB.registerPipelineParsingCallback( |
| [](StringRef Name, LoopPassManager &LPM, |
| ArrayRef<PassBuilder::PipelineElement>) { |
| if (Name == "test-transform") { |
| LPM.addPass(MockPassHandle<Loop>().getPass()); |
| return true; |
| } |
| return false; |
| }); |
| |
| StringRef PipelineText = "test-transform,function(test-transform),cgscc(" |
| "function(loop(test-transform)))"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(FunctionCallbacksTest, Passes) { |
| EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _)); |
| EXPECT_CALL(PassHandle, run(HasName("foo"), _)).WillOnce(&getAnalysisResult); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(FunctionCallbacksTest, InstrumentedPasses) { |
| CallbacksHandle.registerPassInstrumentation(); |
| // Non-mock instrumentation not specifically mentioned below can be ignored. |
| CallbacksHandle.ignoreNonMockPassInstrumentation("<string>"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); |
| |
| EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _)); |
| EXPECT_CALL(PassHandle, run(HasName("foo"), _)).WillOnce(&getAnalysisResult); |
| |
| // PassInstrumentation calls should happen in-sequence, in the same order |
| // as passes/analyses are scheduled. |
| ::testing::Sequence PISequence; |
| EXPECT_CALL(CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle"), HasName("foo"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("foo"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("foo"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("foo"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle"), HasName("foo"), _)) |
| .InSequence(PISequence); |
| |
| // No passes are skipped, so there should be no calls to |
| // runBeforeSkippedPass(). |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("foo"))) |
| .Times(0); |
| |
| // Our mock pass does not invalidate IR. |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)) |
| .Times(0); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(FunctionCallbacksTest, InstrumentedSkippedPasses) { |
| CallbacksHandle.registerPassInstrumentation(); |
| // Non-mock instrumentation run here can safely be ignored. |
| CallbacksHandle.ignoreNonMockPassInstrumentation("<string>"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); |
| |
| // Skip the pass by returning false. |
| EXPECT_CALL(CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle"), HasName("foo"))) |
| .WillOnce(Return(false)); |
| |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("foo"))) |
| .Times(1); |
| |
| EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _)).Times(0); |
| EXPECT_CALL(PassHandle, run(HasName("foo"), _)).Times(0); |
| |
| // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis |
| // as well. |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle"), _, _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle"), _, _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)) |
| .Times(0); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "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>(&getAnalysisResult)); |
| EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _)); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(LoopCallbacksTest, InstrumentedPasses) { |
| CallbacksHandle.registerPassInstrumentation(); |
| // Non-mock instrumentation not specifically mentioned below can be ignored. |
| CallbacksHandle.ignoreNonMockPassInstrumentation("<string>"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("loop"); |
| |
| EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)); |
| EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)) |
| .WillOnce(WithArgs<0, 1, 2>(&getAnalysisResult)); |
| EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _)); |
| |
| // PassInstrumentation calls should happen in-sequence, in the same order |
| // as passes/analyses are scheduled. |
| ::testing::Sequence PISequence; |
| EXPECT_CALL(CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle"), HasName("loop"), _)) |
| .InSequence(PISequence); |
| |
| EXPECT_CALL(CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>"), |
| HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeNonSkippedPass( |
| HasNameRegex("MockPassHandle<.*LoopNest>"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>"), |
| HasName("loop"), _)) |
| .InSequence(PISequence); |
| |
| // Our mock pass does not invalidate IR. |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)) |
| .Times(0); |
| |
| // No passes are skipped, so there should be no calls to |
| // runBeforeSkippedPass(). |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))) |
| .Times(0); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(LoopCallbacksTest, InstrumentedInvalidatingPasses) { |
| CallbacksHandle.registerPassInstrumentation(); |
| // Non-mock instrumentation not specifically mentioned below can be ignored. |
| CallbacksHandle.ignoreNonMockPassInstrumentation("<string>"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("loop"); |
| |
| EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)); |
| EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)) |
| .WillOnce(DoAll(WithArgs<0, 1, 2, 3>(&PassHandle.invalidateLoop), |
| WithArgs<0, 1, 2>(&getAnalysisResult))); |
| |
| // PassInstrumentation calls should happen in-sequence, in the same order |
| // as passes/analyses are scheduled. |
| ::testing::Sequence PISequence; |
| EXPECT_CALL(CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("^PassManager"), _)) |
| .InSequence(PISequence); |
| |
| // Our mock pass invalidates IR, thus normal runAfterPass is never called. |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle"), HasName("loop"), _)) |
| .Times(0); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(LoopCallbacksTest, InstrumentedInvalidatingLoopNestPasses) { |
| CallbacksHandle.registerPassInstrumentation(); |
| // Non-mock instrumentation not specifically mentioned below can be ignored. |
| CallbacksHandle.ignoreNonMockPassInstrumentation("<string>"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("loop"); |
| |
| EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)); |
| EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)) |
| .WillOnce(WithArgs<0, 1, 2>(&getAnalysisResult)); |
| EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _)) |
| .WillOnce(DoAll(&ExtraPassHandle.invalidateLoopNest, |
| [&](LoopNest &, LoopAnalysisManager &, |
| LoopStandardAnalysisResults &, |
| LPMUpdater &) { return PreservedAnalyses::all(); })); |
| |
| // PassInstrumentation calls should happen in-sequence, in the same order |
| // as passes/analyses are scheduled. |
| ::testing::Sequence PISequence; |
| EXPECT_CALL(CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle"), HasName("loop"), _)) |
| .InSequence(PISequence); |
| |
| EXPECT_CALL(CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>"), |
| HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeNonSkippedPass( |
| HasNameRegex("MockPassHandle<.*LoopNest>"), HasName("loop"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("MockPassHandle<.*LoopNest>"), _)) |
| .InSequence(PISequence); |
| |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("^PassManager"), _)) |
| .InSequence(PISequence); |
| |
| // Our mock pass invalidates IR, thus normal runAfterPass is never called. |
| EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated( |
| HasNameRegex("MockPassHandle<.*Loop>"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>"), |
| HasName("loop"), _)) |
| .Times(0); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(LoopCallbacksTest, InstrumentedSkippedPasses) { |
| CallbacksHandle.registerPassInstrumentation(); |
| // Non-mock instrumentation run here can safely be ignored. |
| CallbacksHandle.ignoreNonMockPassInstrumentation("<string>"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("loop"); |
| |
| // Skip the pass by returning false. |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle<.*Loop>"), HasName("loop"))) |
| .WillOnce(Return(false)); |
| |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeSkippedPass(HasNameRegex("MockPassHandle<.*Loop>"), |
| HasName("loop"))) |
| .Times(1); |
| |
| EXPECT_CALL(CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle<.*LoopNest>"), |
| HasName("loop"))) |
| .WillOnce(Return(false)); |
| |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeSkippedPass(HasNameRegex("MockPassHandle<.*LoopNest>"), |
| HasName("loop"))) |
| .Times(1); |
| |
| EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)).Times(0); |
| EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)).Times(0); |
| EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _)).Times(0); |
| |
| // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis |
| // as well. |
| EXPECT_CALL(CallbacksHandle, runBeforeNonSkippedPass( |
| HasNameRegex("MockPassHandle<.*Loop>"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle<.*Loop>"), _, _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, runAfterPassInvalidated( |
| HasNameRegex("MockPassHandle<.*Loop>"), _)) |
| .Times(0); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("MockPassHandle<.*LoopNest>"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle<.*LoopNest>"), _, _)) |
| .Times(0); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("MockPassHandle<.*LoopNest>"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)) |
| .Times(0); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "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>(&getAnalysisResult)); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(CGSCCCallbacksTest, InstrumentedPasses) { |
| CallbacksHandle.registerPassInstrumentation(); |
| // Non-mock instrumentation not specifically mentioned below can be ignored. |
| CallbacksHandle.ignoreNonMockPassInstrumentation("<string>"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("(foo)"); |
| |
| EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)); |
| EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)) |
| .WillOnce(WithArgs<0, 1, 2>(&getAnalysisResult)); |
| |
| // PassInstrumentation calls should happen in-sequence, in the same order |
| // as passes/analyses are scheduled. |
| ::testing::Sequence PISequence; |
| EXPECT_CALL(CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle"), HasName("(foo)"), _)) |
| .InSequence(PISequence); |
| |
| // Our mock pass does not invalidate IR. |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)) |
| .Times(0); |
| |
| // No passes are skipped, so there should be no calls to |
| // runBeforeSkippedPass(). |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) |
| .Times(0); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(CGSCCCallbacksTest, InstrumentedInvalidatingPasses) { |
| CallbacksHandle.registerPassInstrumentation(); |
| // Non-mock instrumentation not specifically mentioned below can be ignored. |
| CallbacksHandle.ignoreNonMockPassInstrumentation("<string>"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("(foo)"); |
| |
| EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)); |
| EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)) |
| .WillOnce(DoAll(WithArgs<0, 1, 2, 3>(&PassHandle.invalidateSCC), |
| WithArgs<0, 1, 2>(&getAnalysisResult))); |
| |
| // PassInstrumentation calls should happen in-sequence, in the same order |
| // as passes/analyses are scheduled. |
| ::testing::Sequence PISequence; |
| EXPECT_CALL(CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))) |
| .InSequence(PISequence); |
| EXPECT_CALL( |
| CallbacksHandle, |
| runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), HasName("(foo)"))) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)) |
| .InSequence(PISequence); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("^PassManager"), _)) |
| .InSequence(PISequence); |
| |
| // Our mock pass does invalidate IR, thus normal runAfterPass is never called. |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle"), HasName("(foo)"), _)) |
| .Times(0); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| PM.run(*M, AM); |
| } |
| |
| TEST_F(CGSCCCallbacksTest, InstrumentedSkippedPasses) { |
| CallbacksHandle.registerPassInstrumentation(); |
| // Non-mock instrumentation run here can safely be ignored. |
| CallbacksHandle.ignoreNonMockPassInstrumentation("<string>"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("foo"); |
| CallbacksHandle.ignoreNonMockPassInstrumentation("(foo)"); |
| |
| // Skip the pass by returning false. |
| EXPECT_CALL(CallbacksHandle, |
| runBeforePass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) |
| .WillOnce(Return(false)); |
| |
| EXPECT_CALL( |
| CallbacksHandle, |
| runBeforeSkippedPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) |
| .Times(1); |
| |
| // neither Analysis nor Pass are called. |
| EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)).Times(0); |
| EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)).Times(0); |
| |
| // As the pass is skipped there is no afterPass, beforeAnalysis/afterAnalysis |
| // as well. |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeNonSkippedPass(HasNameRegex("MockPassHandle"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPass(HasNameRegex("MockPassHandle"), _, _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterPassInvalidated(HasNameRegex("MockPassHandle"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), _)) |
| .Times(0); |
| EXPECT_CALL(CallbacksHandle, |
| runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), _)) |
| .Times(0); |
| |
| StringRef PipelineText = "test-transform"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "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_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "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_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "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_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "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_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "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) { |
| 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(&getAnalysisResult); |
| EXPECT_CALL(AnalysisHandle, invalidate(HasName("<string>"), _, _)); |
| |
| StringRef PipelineText = |
| "another-pipeline(test-transform,invalidate<test-analysis>)"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Succeeded()) |
| << "Pipeline was: " << PipelineText; |
| PM.run(*M, AM); |
| |
| /// Test the negative case |
| PipelineText = "another-pipeline(instcombine)"; |
| ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText), Failed()) |
| << "Pipeline was: " << PipelineText; |
| } |
| } // end anonymous namespace |