1318 lines
50 KiB
C++
1318 lines
50 KiB
C++
//===- 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::AtLeast;
|
|
using testing::DoDefault;
|
|
using testing::Not;
|
|
using testing::Return;
|
|
using testing::Expectation;
|
|
using testing::Invoke;
|
|
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(Invoke(&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(false, nullptr, PipelineTuningOptions(), None,
|
|
&CallbacksHandle.Callbacks),
|
|
PM(true), LAM(true), FAM(true), CGAM(true), AM(true) {
|
|
|
|
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(Invoke(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(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,
|
|
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(Invoke(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(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,
|
|
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>(Invoke(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>(Invoke(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>(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,
|
|
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>(Invoke(getAnalysisResult)));
|
|
EXPECT_CALL(ExtraPassHandle, run(HasName("loop"), _, _, _))
|
|
.WillOnce(DoAll(Invoke(ExtraPassHandle.invalidateLoopNest),
|
|
Invoke([&](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>(Invoke(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>(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,
|
|
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>(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,
|
|
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,
|
|
bool DebugLogging) {
|
|
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(Invoke(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
|