Index: unittests/IR/PassBuilderCallbacksTest.cpp =================================================================== --- unittests/IR/PassBuilderCallbacksTest.cpp +++ unittests/IR/PassBuilderCallbacksTest.cpp @@ -7,14 +7,18 @@ // //===----------------------------------------------------------------------===// +#include #include #include +#include #include #include #include #include +#include #include #include +#include #include #include @@ -32,7 +36,11 @@ } namespace { +using testing::AnyNumber; +using testing::AtLeast; using testing::DoDefault; +using testing::Eq; +using testing::Ge; using testing::Return; using testing::Expectation; using testing::Invoke; @@ -87,6 +95,7 @@ typename Analysis::Result getResult() { return typename Analysis::Result(static_cast(*this)); } + static StringRef getName() { return llvm::getTypeName(); } protected: // FIXME: MSVC seems unable to handle a lambda argument to Invoke from within @@ -143,6 +152,8 @@ } }; + static StringRef getName() { return llvm::getTypeName(); } + Pass getPass() { return Pass(static_cast(*this)); } protected: @@ -253,11 +264,81 @@ MockAnalysisHandle() { setDefaults(); } }; +struct MockPassInstrumentationCallbacks { + MockPassInstrumentationCallbacks() { + ON_CALL(*this, runBeforePass(_, _, _)).WillByDefault(Return(true)); + EXPECT_CALL(*this, runBeforePass(_, _, Ge(0))).Times(AnyNumber()); + EXPECT_CALL(*this, runAfterPass(_, _, Ge(1))).Times(AnyNumber()); + EXPECT_CALL(*this, runBeforeAnalysis(_, _, _)).Times(AnyNumber()); + EXPECT_CALL(*this, runAfterAnalysis(_, _, _)).Times(AnyNumber()); + EXPECT_CALL(*this, startPassManager(_, _, _)).Times(AnyNumber()); + EXPECT_CALL(*this, finishPassManager(_, _, _)).Times(AnyNumber()); + } + MOCK_METHOD3(runBeforePass, + bool(PassInstanceID PassID, llvm::Any, PassExecutionLocation)); + MOCK_METHOD3(runAfterPass, + void(PassInstanceID PassID, llvm::Any, PassExecutionLocation)); + MOCK_METHOD3(runBeforeAnalysis, + void(PassInstanceID PassID, llvm::Any, PassExecutionLocation)); + MOCK_METHOD3(runAfterAnalysis, + void(PassInstanceID PassID, llvm::Any, PassExecutionLocation)); + MOCK_METHOD0(startPipeline, void()); + MOCK_METHOD0(endPipeline, void()); + MOCK_METHOD3(startPassManager, + void(PassInstanceID PM, llvm::Any, PassExecutionLocation)); + MOCK_METHOD3(finishPassManager, + void(PassInstanceID PM, llvm::Any, PassExecutionLocation)); + MOCK_METHOD3(startIteration, + void(PassInstanceID PM, int, PassExecutionLocation)); +}; + static std::unique_ptr 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 StringRef getName(const IRUnitT &IR) { + return IR.getName(); +} + +template <> StringRef getName(const llvm::Any &WrappedIR) { + if (any_isa(WrappedIR)) + return any_cast(WrappedIR)->getName(); + if (any_isa(WrappedIR)) + return any_cast(WrappedIR)->getName(); + if (any_isa(WrappedIR)) + return any_cast(WrappedIR)->getName(); + if (any_isa(WrappedIR)) + return any_cast(WrappedIR)->getName(); + return ""; +} +/// 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)); +} + template class PassBuilderCallbacksTest; /// This test fixture is shared between all the actual tests below and @@ -280,6 +361,7 @@ LLVMContext Context; std::unique_ptr M; + PassInstrumentation PI; PassBuilder PB; ModulePassManager PM; LoopAnalysisManager LAM; @@ -289,6 +371,7 @@ MockPassHandle PassHandle; MockAnalysisHandle AnalysisHandle; + MockPassInstrumentationCallbacks PassInstrumentationCallbacks; static PreservedAnalyses getAnalysisResult(IRUnitT &U, AnalysisManagerT &AM, ExtraAnalysisArgTs &&... Args) { @@ -312,7 +395,8 @@ "exit:\n" " ret void\n" "}\n")), - PM(true), LAM(true), FAM(true), CGAM(true), AM(true) { + PI(), PB(nullptr, None, &PI), PM(true), LAM(true), FAM(true), + CGAM(true), AM(true) { /// Register a callback for analysis registration. /// @@ -347,6 +431,37 @@ return false; }); + /// Register pass instrumentation + PB.registerPassInstrumentation([this](PassInstrumentation &_PI) { + using namespace std::placeholders; + _PI.registerBeforePassCallback( + std::bind(&MockPassInstrumentationCallbacks::runBeforePass, + &PassInstrumentationCallbacks, _1, _2, _3)); + _PI.registerAfterPassCallback( + std::bind(&MockPassInstrumentationCallbacks::runAfterPass, + &PassInstrumentationCallbacks, _1, _2, _3)); + _PI.registerBeforeAnalysisCallback( + std::bind(&MockPassInstrumentationCallbacks::runBeforeAnalysis, + &PassInstrumentationCallbacks, _1, _2, _3)); + _PI.registerAfterAnalysisCallback( + std::bind(&MockPassInstrumentationCallbacks::runAfterAnalysis, + &PassInstrumentationCallbacks, _1, _2, _3)); + _PI.registerStartPassManagerCallback( + std::bind(&MockPassInstrumentationCallbacks::startPassManager, + &PassInstrumentationCallbacks, _1, _2, _3)); + _PI.registerFinishPassManagerCallback( + std::bind(&MockPassInstrumentationCallbacks::finishPassManager, + &PassInstrumentationCallbacks, _1, _2, _3)); + _PI.registerStartPipelineCallback( + std::bind(&MockPassInstrumentationCallbacks::startPipeline, + &PassInstrumentationCallbacks)); + _PI.registerEndPipelineCallback( + std::bind(&MockPassInstrumentationCallbacks::endPipeline, + &PassInstrumentationCallbacks)); + _PI.registerStartIterationCallback( + std::bind(&MockPassInstrumentationCallbacks::startIteration, + &PassInstrumentationCallbacks, _1, _2, _3)); + }); /// Register builtin analyses and cross-register the analysis proxies PB.registerModuleAnalyses(AM); PB.registerCGSCCAnalyses(CGAM); @@ -356,25 +471,6 @@ } }; -/// 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; using CGSCCCallbacksTest = PassBuilderCallbacksTest; using FunctionCallbacksTest = PassBuilderCallbacksTest; @@ -391,6 +487,35 @@ StringRef PipelineText = "test-transform"; ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) << "Pipeline was: " << PipelineText; + + PM.run(*M, AM); +} + +TEST_F(ModuleCallbacksTest, InstrumentedPasses) { + EXPECT_CALL(AnalysisHandle, run(HasName(""), _)); + EXPECT_CALL(PassHandle, run(HasName(""), _)) + .WillOnce(Invoke(getAnalysisResult)); + + { + testing::InSequence InstrumentationExpectationsSequenced; + EXPECT_CALL(PassInstrumentationCallbacks, + startPassManager(_, HasName(""), Eq(0))); + EXPECT_CALL(PassInstrumentationCallbacks, + runBeforePass(_, HasName(""), Eq(1))); + EXPECT_CALL(PassInstrumentationCallbacks, + runBeforeAnalysis(_, HasName(""), Eq(1))); + EXPECT_CALL(PassInstrumentationCallbacks, + runAfterAnalysis(_, HasName(""), Eq(1))); + EXPECT_CALL(PassInstrumentationCallbacks, + runAfterPass(_, HasName(""), Eq(1))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName(""), Ge(1))); + } + + StringRef PipelineText = "test-transform"; + ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + << "Pipeline was: " << PipelineText; + PM.run(*M, AM); } @@ -405,6 +530,48 @@ PM.run(*M, AM); } +TEST_F(FunctionCallbacksTest, InstrumentedPasses) { + EXPECT_CALL(AnalysisHandle, run(HasName("foo"), _)); + EXPECT_CALL(PassHandle, run(HasName("foo"), _)) + .WillOnce(Invoke(getAnalysisResult)); + + { + testing::InSequence InstrumentationExpectationsSequenced; + EXPECT_CALL(PassInstrumentationCallbacks, + startPassManager(HasNameRegex("PassManager"), + HasName(""), Eq(0))); + EXPECT_CALL(PassInstrumentationCallbacks, + startPassManager(HasNameRegex("PassAdaptor"), + HasName(""), Eq(1))); + EXPECT_CALL( + PassInstrumentationCallbacks, + startPassManager(HasNameRegex("PassManager"), HasName("foo"), Eq(2))); + EXPECT_CALL( + PassInstrumentationCallbacks, + runBeforePass(HasNameRegex("MockPassHandle"), HasName("foo"), Ge(3))); + EXPECT_CALL(PassInstrumentationCallbacks, + runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), + HasName("foo"), Ge(3))); + EXPECT_CALL(PassInstrumentationCallbacks, + runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), + HasName("foo"), Ge(3))); + EXPECT_CALL( + PassInstrumentationCallbacks, + runAfterPass(HasNameRegex("MockPassHandle"), HasName("foo"), Ge(3))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName("foo"), Ge(3))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName(""), Ge(3))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName(""), Ge(3))); + } + + 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"), _, _, _)) @@ -416,6 +583,57 @@ PM.run(*M, AM); } +TEST_F(LoopCallbacksTest, InstrumentedPasses) { + EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)); + EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)) + .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult))); + { + testing::InSequence InstrumentationExpectationsSequenced; + EXPECT_CALL(PassInstrumentationCallbacks, + startPassManager(HasNameRegex("PassManager<.*Module.*>"), + HasName(""), Eq(0))); + EXPECT_CALL(PassInstrumentationCallbacks, + startPassManager(HasNameRegex("ModuleToFunctionPassAdaptor"), + HasName(""), Eq(1))); + EXPECT_CALL(PassInstrumentationCallbacks, + startPassManager(HasNameRegex("PassManager<.*Function.*>"), + HasName("foo"), Eq(2))); + EXPECT_CALL(PassInstrumentationCallbacks, + startPassManager(HasNameRegex("FunctionToLoopPassAdaptor"), + HasName("foo"), Eq(3))); + EXPECT_CALL(PassInstrumentationCallbacks, + startPassManager(HasNameRegex("PassManager<.*Loop.*>"), + HasName("loop"), Ge(4))); + EXPECT_CALL(PassInstrumentationCallbacks, + runBeforePass(HasNameRegex("MockPassHandle"), + HasName("loop"), Ge(5))); + EXPECT_CALL(PassInstrumentationCallbacks, + runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), + HasName("loop"), Ge(5))); + EXPECT_CALL(PassInstrumentationCallbacks, + runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), + HasName("loop"), Ge(5))); + EXPECT_CALL(PassInstrumentationCallbacks, + runAfterPass(HasNameRegex("MockPassHandle"), + HasName("loop"), Ge(5))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName("loop"), Ge(5))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName("foo"), Ge(5))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName("foo"), Ge(5))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName(""), Ge(5))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName(""), Ge(5))); + } + + 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)"), _, _, _)) @@ -427,6 +645,50 @@ PM.run(*M, AM); } +TEST_F(CGSCCCallbacksTest, InstrumentedPasses) { + EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)); + EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)) + .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult))); + + { + testing::InSequence InstrumentationExpectationsSequenced; + EXPECT_CALL(PassInstrumentationCallbacks, + startPassManager(HasNameRegex("PassManager<.*Module.*>"), + HasName(""), Eq(0))); + EXPECT_CALL( + PassInstrumentationCallbacks, + startPassManager(HasNameRegex("ModuleToPostOrderCGSCCPassAdaptor"), + HasName(""), Eq(1))); + EXPECT_CALL( + PassInstrumentationCallbacks, + startPassManager(HasNameRegex("PassManager<.*LazyCallGraph::SCC.*>"), + HasName("(foo)"), Eq(2))); + EXPECT_CALL( + PassInstrumentationCallbacks, + runBeforePass(HasNameRegex("MockPassHandle"), HasName("(foo)"), Ge(3))); + EXPECT_CALL(PassInstrumentationCallbacks, + runBeforeAnalysis(HasNameRegex("MockAnalysisHandle"), + HasName("(foo)"), Ge(3))); + EXPECT_CALL(PassInstrumentationCallbacks, + runAfterAnalysis(HasNameRegex("MockAnalysisHandle"), + HasName("(foo)"), Ge(3))); + EXPECT_CALL( + PassInstrumentationCallbacks, + runAfterPass(HasNameRegex("MockPassHandle"), HasName("(foo)"), Ge(3))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName("(foo)"), Ge(3))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName(""), Ge(3))); + EXPECT_CALL(PassInstrumentationCallbacks, + finishPassManager(_, HasName(""), Ge(3))); + } + + 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. ///