Mercurial > hg > CbC > CbC_llvm
diff unittests/IR/PassBuilderCallbacksTest.cpp @ 148:63bd29f05246
merged
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Wed, 14 Aug 2019 19:46:37 +0900 |
parents | c2174574ed3a |
children |
line wrap: on
line diff
--- a/unittests/IR/PassBuilderCallbacksTest.cpp Sun Dec 23 19:23:36 2018 +0900 +++ b/unittests/IR/PassBuilderCallbacksTest.cpp Wed Aug 14 19:46:37 2019 +0900 @@ -1,45 +1,41 @@ //===- unittests/IR/PassBuilderCallbacksTest.cpp - PB Callback Tests --===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 llvm { -/// Provide an ostream operator for StringRef. -/// -/// For convenience we provide a custom matcher below for IRUnit's and analysis -/// result's getName functions, which most of the time returns a StringRef. The -/// matcher makes use of this operator. -static std::ostream &operator<<(std::ostream &O, StringRef S) { - return O << S.str(); -} -} - namespace { +using testing::AnyNumber; +using testing::AtLeast; using testing::DoDefault; +using testing::Not; using testing::Return; using testing::Expectation; using testing::Invoke; using testing::WithArgs; using testing::_; -/// \brief A CRTP base for analysis mock handles +/// 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 @@ -87,6 +83,7 @@ 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 @@ -110,7 +107,7 @@ } }; -/// \brief A CRTP base for pass mock handles +/// 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 @@ -143,6 +140,8 @@ } }; + static StringRef getName() { return llvm::getTypeName<DerivedT>(); } + Pass getPass() { return Pass(static_cast<DerivedT &>(*this)); } protected: @@ -167,6 +166,11 @@ MOCK_METHOD4(run, PreservedAnalyses(Loop &, LoopAnalysisManager &, LoopStandardAnalysisResults &, LPMUpdater &)); + static void invalidateLoop(Loop &L, LoopAnalysisManager &, + LoopStandardAnalysisResults &, + LPMUpdater &Updater) { + Updater.markLoopAsDeleted(L, L.getName()); + } MockPassHandle() { setDefaults(); } }; @@ -187,6 +191,11 @@ PreservedAnalyses(LazyCallGraph::SCC &, CGSCCAnalysisManager &, LazyCallGraph &G, CGSCCUpdateResult &UR)); + static void invalidateSCC(LazyCallGraph::SCC &C, CGSCCAnalysisManager &, + LazyCallGraph &, CGSCCUpdateResult &UR) { + UR.InvalidatedSCCs.insert(&C); + } + MockPassHandle() { setDefaults(); } }; @@ -258,6 +267,97 @@ 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)); + MOCK_METHOD1(runAfterPassInvalidated, void(StringRef PassID)); + MOCK_METHOD2(runBeforeAnalysis, void(StringRef PassID, llvm::Any)); + MOCK_METHOD2(runAfterAnalysis, 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); }); + Callbacks.registerAfterPassInvalidatedCallback( + [this](StringRef P) { this->runAfterPassInvalidated(P); }); + 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, 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 PassManagerT> class PassBuilderCallbacksTest; /// This test fixture is shared between all the actual tests below and @@ -280,6 +380,8 @@ LLVMContext Context; std::unique_ptr<Module> M; + MockPassInstrumentationCallbacks CallbacksHandle; + PassBuilder PB; ModulePassManager PM; LoopAnalysisManager LAM; @@ -312,6 +414,8 @@ "exit:\n" " ret void\n" "}\n")), + CallbacksHandle(), + PB(nullptr, PipelineTuningOptions(), None, &CallbacksHandle.Callbacks), PM(true), LAM(true), FAM(true), CGAM(true), AM(true) { /// Register a callback for analysis registration. @@ -356,25 +460,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<ModulePassManager>; using CGSCCCallbacksTest = PassBuilderCallbacksTest<CGSCCPassManager>; using FunctionCallbacksTest = PassBuilderCallbacksTest<FunctionPassManager>; @@ -389,8 +474,74 @@ .WillOnce(Invoke(getAnalysisResult)); StringRef PipelineText = "test-transform"; - ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), 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(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, + 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); + + StringRef PipelineText = "test-transform"; + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), 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>"); + + // 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, beforeAnalysis/afterAnalysis + // as well. + 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, true), Succeeded()) + << "Pipeline was: " << PipelineText; + PM.run(*M, AM); } @@ -400,7 +551,82 @@ .WillOnce(Invoke(getAnalysisResult)); StringRef PipelineText = "test-transform"; - ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), 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(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, + 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); + + StringRef PipelineText = "test-transform"; + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), 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(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, 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, true), Succeeded()) << "Pipeline was: " << PipelineText; PM.run(*M, AM); } @@ -411,7 +637,126 @@ .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult))); StringRef PipelineText = "test-transform"; - ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), 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>(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, + 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); + + // 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, true), 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>(Invoke(PassHandle.invalidateLoop)), + 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, + 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, true), 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"), 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, beforeAnalysis/afterAnalysis + // as well. + 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, true), Succeeded()) << "Pipeline was: " << PipelineText; PM.run(*M, AM); } @@ -422,7 +767,124 @@ .WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult))); StringRef PipelineText = "test-transform"; - ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), 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)"); + + 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, + 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); + + StringRef PipelineText = "test-transform"; + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), 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)"); + + EXPECT_CALL(AnalysisHandle, run(HasName("(foo)"), _, _)); + EXPECT_CALL(PassHandle, run(HasName("(foo)"), _, _, _)) + .WillOnce(DoAll(WithArgs<0, 1, 2, 3>(Invoke(PassHandle.invalidateSCC)), + 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, + 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, true), 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)"); + + // 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, beforeAnalysis/afterAnalysis + // as well. + 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, true), Succeeded()) << "Pipeline was: " << PipelineText; PM.run(*M, AM); } @@ -437,7 +899,7 @@ EXPECT_CALL(AnalysisHandle, invalidate(HasName("<string>"), _, _)); StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>"; - ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) << "Pipeline was: " << PipelineText; PM.run(*M, AM); } @@ -447,7 +909,7 @@ EXPECT_CALL(AnalysisHandle, invalidate(HasName("(foo)"), _, _)); StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>"; - ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) << "Pipeline was: " << PipelineText; PM.run(*M, AM); } @@ -457,7 +919,7 @@ EXPECT_CALL(AnalysisHandle, invalidate(HasName("foo"), _, _)); StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>"; - ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) << "Pipeline was: " << PipelineText; PM.run(*M, AM); } @@ -468,7 +930,7 @@ StringRef PipelineText = "require<test-analysis>,invalidate<test-analysis>"; - ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) << "Pipeline was: " << PipelineText; PM.run(*M, AM); } @@ -508,13 +970,13 @@ StringRef PipelineText = "another-pipeline(test-transform,invalidate<test-analysis>)"; - ASSERT_TRUE(PB.parsePassPipeline(PM, PipelineText, true)) + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Succeeded()) << "Pipeline was: " << PipelineText; PM.run(*M, AM); /// Test the negative case PipelineText = "another-pipeline(instcombine)"; - ASSERT_FALSE(PB.parsePassPipeline(PM, PipelineText, true)) + ASSERT_THAT_ERROR(PB.parsePassPipeline(PM, PipelineText, true), Failed()) << "Pipeline was: " << PipelineText; } } // end anonymous namespace