diff options
Diffstat (limited to 'llvm/unittests/IR/PassBuilderCallbacksTest.cpp')
-rw-r--r-- | llvm/unittests/IR/PassBuilderCallbacksTest.cpp | 311 |
1 files changed, 292 insertions, 19 deletions
diff --git a/llvm/unittests/IR/PassBuilderCallbacksTest.cpp b/llvm/unittests/IR/PassBuilderCallbacksTest.cpp index e46fc178150..024e7d601b2 100644 --- a/llvm/unittests/IR/PassBuilderCallbacksTest.cpp +++ b/llvm/unittests/IR/PassBuilderCallbacksTest.cpp @@ -7,14 +7,18 @@ // //===----------------------------------------------------------------------===// +#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> @@ -32,7 +36,10 @@ static std::ostream &operator<<(std::ostream &O, StringRef S) { } namespace { +using testing::AnyNumber; +using testing::AtLeast; using testing::DoDefault; +using testing::Not; using testing::Return; using testing::Expectation; using testing::Invoke; @@ -87,6 +94,7 @@ public: 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 @@ -143,6 +151,8 @@ public: } }; + static StringRef getName() { return llvm::getTypeName<DerivedT>(); } + Pass getPass() { return Pass(static_cast<DerivedT &>(*this)); } protected: @@ -258,6 +268,81 @@ static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) { 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 IR.getName(); +} + +template <> std::string getName(const StringRef &name) { return 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 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(runAfterPass, void(StringRef PassID, llvm::Any)); + + void registerPassInstrumentation() { + Callbacks.registerBeforePassCallback([this](StringRef P, llvm::Any IR) { + return this->runBeforePass(P, IR); + }); + Callbacks.registerAfterPassCallback( + [this](StringRef P, llvm::Any IR) { this->runAfterPass(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, runAfterPass(Not(HasNameRegex("Mock")), HasName(IRName))) + .Times(AnyNumber()); + } +}; + template <typename PassManagerT> class PassBuilderCallbacksTest; /// This test fixture is shared between all the actual tests below and @@ -280,6 +365,8 @@ protected: LLVMContext Context; std::unique_ptr<Module> M; + MockPassInstrumentationCallbacks CallbacksHandle; + PassBuilder PB; ModulePassManager PM; LoopAnalysisManager LAM; @@ -312,6 +399,7 @@ protected: "exit:\n" " ret void\n" "}\n")), + CallbacksHandle(), PB(nullptr, None, &CallbacksHandle.Callbacks), PM(true), LAM(true), FAM(true), CGAM(true), AM(true) { /// Register a callback for analysis registration. @@ -356,25 +444,6 @@ protected: } }; -/// 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<ModulePassManager>; using CGSCCCallbacksTest = PassBuilderCallbacksTest<CGSCCPassManager>; using FunctionCallbacksTest = PassBuilderCallbacksTest<FunctionPassManager>; @@ -391,6 +460,57 @@ TEST_F(ModuleCallbacksTest, Passes) { 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("<string>"), _)); + EXPECT_CALL(PassHandle, run(HasName("<string>"), _)) + .WillOnce(Invoke(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, + runAfterPass(HasNameRegex("MockPassHandle"), HasName("<string>"))) + .InSequence(PISequence); + + StringRef PipelineText = "test-transform"; + ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + << "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>"); + + // Skip the pass by returning false. + EXPECT_CALL(CallbacksHandle, runBeforePass(HasNameRegex("MockPassHandle"), + HasName("<string>"))) + .WillOnce(Return(false)); + + EXPECT_CALL(AnalysisHandle, run(HasName("<string>"), _)).Times(0); + EXPECT_CALL(PassHandle, run(HasName("<string>"), _)).Times(0); + + // As the pass is skipped there is no afterPass as well. + EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle"), _)) + .Times(0); + + StringRef PipelineText = "test-transform"; + ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + << "Pipeline was: " << PipelineText; + PM.run(*M, AM); } @@ -405,6 +525,56 @@ TEST_F(FunctionCallbacksTest, Passes) { 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(Invoke(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, + runAfterPass(HasNameRegex("MockPassHandle"), HasName("foo"))) + .InSequence(PISequence); + + StringRef PipelineText = "test-transform"; + ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + << "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(AnalysisHandle, run(HasName("foo"), _)).Times(0); + EXPECT_CALL(PassHandle, run(HasName("foo"), _)).Times(0); + + // As the pass is skipped there is no afterPass as well. + EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle"), _)) + .Times(0); + + 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 +586,58 @@ TEST_F(LoopCallbacksTest, Passes) { 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>(Invoke(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, + runAfterPass(HasNameRegex("MockPassHandle"), HasName("loop"))) + .InSequence(PISequence); + + StringRef PipelineText = "test-transform"; + ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + << "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"), HasName("loop"))) + .WillOnce(Return(false)); + + EXPECT_CALL(AnalysisHandle, run(HasName("loop"), _, _)).Times(0); + EXPECT_CALL(PassHandle, run(HasName("loop"), _, _, _)).Times(0); + + // As the pass is skipped there is no afterPass as well. + EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle"), _)) + .Times(0); + + 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 +649,57 @@ TEST_F(CGSCCCallbacksTest, Passes) { 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)"); + + EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)); + EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)) + .WillOnce(WithArgs<0, 1, 2>(Invoke(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, + runAfterPass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) + .InSequence(PISequence); + + StringRef PipelineText = "test-transform"; + ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + << "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)"); + + // Skip the pass by returning false. + EXPECT_CALL(CallbacksHandle, + runBeforePass(HasNameRegex("MockPassHandle"), HasName("(foo)"))) + .WillOnce(Return(false)); + + // 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 as well. + EXPECT_CALL(CallbacksHandle, runAfterPass(HasNameRegex("MockPassHandle"), _)) + .Times(0); + + 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. /// |