mirror of
https://github.com/RPCSX/llvm.git
synced 2026-01-31 01:05:23 +01:00
[PM] Enable registration of out-of-tree passes with PassBuilder
Summary: This patch adds a callback registration API to the PassBuilder, enabling registering out-of-tree passes with it. Through the Callback API, callers may register callbacks with the various stages at which passes are added into pass managers, including parsing of a pass pipeline as well as at extension points within the default -O pipelines. Registering utilities like `require<>` and `invalidate<>` needs to be handled manually by the caller, but a helper is provided. Additionally, adding passes at pipeline extension points is exposed through the opt tool. This patch adds a `-passes-ep-X` commandline option for every extension point X, which opt parses into pipelines inserted into that extension point. Reviewers: chandlerc Reviewed By: chandlerc Subscribers: lksbhm, grosser, davide, mehdi_amini, llvm-commits, mgorny Differential Revision: https://reviews.llvm.org/D33464 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@307532 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -46,6 +46,19 @@ class PassBuilder {
|
||||
Optional<PGOOptions> PGOOpt;
|
||||
|
||||
public:
|
||||
/// \brief A struct to capture parsed pass pipeline names.
|
||||
///
|
||||
/// A pipeline is defined as a series of names, each of which may in itself
|
||||
/// recursively contain a nested pipeline. A name is either the name of a pass
|
||||
/// (e.g. "instcombine") or the name of a pipeline type (e.g. "cgscc"). If the
|
||||
/// name is the name of a pass, the InnerPipeline is empty, since passes
|
||||
/// cannot contain inner pipelines. See parsePassPipeline() for a more
|
||||
/// detailed description of the textual pipeline format.
|
||||
struct PipelineElement {
|
||||
StringRef Name;
|
||||
std::vector<PipelineElement> InnerPipeline;
|
||||
};
|
||||
|
||||
/// \brief LLVM-provided high-level optimization levels.
|
||||
///
|
||||
/// This enumerates the LLVM-provided high-level optimization levels. Each
|
||||
@@ -312,7 +325,8 @@ public:
|
||||
/// registered.
|
||||
AAManager buildDefaultAAPipeline();
|
||||
|
||||
/// \brief Parse a textual pass pipeline description into a \c ModulePassManager.
|
||||
/// \brief Parse a textual pass pipeline description into a \c
|
||||
/// ModulePassManager.
|
||||
///
|
||||
/// The format of the textual pass pipeline description looks something like:
|
||||
///
|
||||
@@ -322,8 +336,8 @@ public:
|
||||
/// are comma separated. As a special shortcut, if the very first pass is not
|
||||
/// a module pass (as a module pass manager is), this will automatically form
|
||||
/// the shortest stack of pass managers that allow inserting that first pass.
|
||||
/// So, assuming function passes 'fpassN', CGSCC passes 'cgpassN', and loop passes
|
||||
/// 'lpassN', all of these are valid:
|
||||
/// So, assuming function passes 'fpassN', CGSCC passes 'cgpassN', and loop
|
||||
/// passes 'lpassN', all of these are valid:
|
||||
///
|
||||
/// fpass1,fpass2,fpass3
|
||||
/// cgpass1,cgpass2,cgpass3
|
||||
@@ -336,13 +350,28 @@ public:
|
||||
/// module(function(loop(lpass1,lpass2,lpass3)))
|
||||
///
|
||||
/// This shortcut is especially useful for debugging and testing small pass
|
||||
/// combinations. Note that these shortcuts don't introduce any other magic. If
|
||||
/// the sequence of passes aren't all the exact same kind of pass, it will be
|
||||
/// an error. You cannot mix different levels implicitly, you must explicitly
|
||||
/// form a pass manager in which to nest passes.
|
||||
/// combinations. Note that these shortcuts don't introduce any other magic.
|
||||
/// If the sequence of passes aren't all the exact same kind of pass, it will
|
||||
/// be an error. You cannot mix different levels implicitly, you must
|
||||
/// explicitly form a pass manager in which to nest passes.
|
||||
bool parsePassPipeline(ModulePassManager &MPM, StringRef PipelineText,
|
||||
bool VerifyEachPass = true, bool DebugLogging = false);
|
||||
|
||||
/// {{@ Parse a textual pass pipeline description into a specific PassManager
|
||||
///
|
||||
/// Automatic deduction of an appropriate pass manager stack is not supported.
|
||||
/// For example, to insert a loop pass 'lpass' into a FunctinoPassManager,
|
||||
/// this is the valid pipeline text:
|
||||
///
|
||||
/// function(lpass)
|
||||
bool parsePassPipeline(CGSCCPassManager &CGPM, StringRef PipelineText,
|
||||
bool VerifyEachPass = true, bool DebugLogging = false);
|
||||
bool parsePassPipeline(FunctionPassManager &FPM, StringRef PipelineText,
|
||||
bool VerifyEachPass = true, bool DebugLogging = false);
|
||||
bool parsePassPipeline(LoopPassManager &LPM, StringRef PipelineText,
|
||||
bool VerifyEachPass = true, bool DebugLogging = false);
|
||||
/// @}}
|
||||
|
||||
/// Parse a textual alias analysis pipeline into the provided AA manager.
|
||||
///
|
||||
/// The format of the textual AA pipeline is a comma separated list of AA
|
||||
@@ -360,13 +389,139 @@ public:
|
||||
/// returns false.
|
||||
bool parseAAPipeline(AAManager &AA, StringRef PipelineText);
|
||||
|
||||
private:
|
||||
/// A struct to capture parsed pass pipeline names.
|
||||
struct PipelineElement {
|
||||
StringRef Name;
|
||||
std::vector<PipelineElement> InnerPipeline;
|
||||
};
|
||||
/// \brief Register a callback for a default optimizer pipeline extension
|
||||
/// point
|
||||
///
|
||||
/// This extension point allows adding passes that perform peephole
|
||||
/// optimizations similar to the instruction combiner. These passes will be
|
||||
/// inserted after each instance of the instruction combiner pass.
|
||||
void registerPeepholeEPCallback(
|
||||
const std::function<void(FunctionPassManager &, OptimizationLevel)> &C) {
|
||||
PeepholeEPCallbacks.push_back(C);
|
||||
}
|
||||
|
||||
/// \brief Register a callback for a default optimizer pipeline extension
|
||||
/// point
|
||||
///
|
||||
/// This extension point allows adding late loop canonicalization and
|
||||
/// simplification passes. This is the last point in the loop optimization
|
||||
/// pipeline before loop deletion. Each pass added
|
||||
/// here must be an instance of LoopPass.
|
||||
/// This is the place to add passes that can remove loops, such as target-
|
||||
/// specific loop idiom recognition.
|
||||
void registerLateLoopOptimizationsEPCallback(
|
||||
const std::function<void(LoopPassManager &, OptimizationLevel)> &C) {
|
||||
LateLoopOptimizationsEPCallbacks.push_back(C);
|
||||
}
|
||||
|
||||
/// \brief Register a callback for a default optimizer pipeline extension
|
||||
/// point
|
||||
///
|
||||
/// This extension point allows adding loop passes to the end of the loop
|
||||
/// optimizer.
|
||||
void registerLoopOptimizerEndEPCallback(
|
||||
const std::function<void(LoopPassManager &, OptimizationLevel)> &C) {
|
||||
LoopOptimizerEndEPCallbacks.push_back(C);
|
||||
}
|
||||
|
||||
/// \brief Register a callback for a default optimizer pipeline extension
|
||||
/// point
|
||||
///
|
||||
/// This extension point allows adding optimization passes after most of the
|
||||
/// main optimizations, but before the last cleanup-ish optimizations.
|
||||
void registerScalarOptimizerLateEPCallback(
|
||||
const std::function<void(FunctionPassManager &, OptimizationLevel)> &C) {
|
||||
ScalarOptimizerLateEPCallbacks.push_back(C);
|
||||
}
|
||||
|
||||
/// \brief Register a callback for a default optimizer pipeline extension
|
||||
/// point
|
||||
///
|
||||
/// This extension point allows adding CallGraphSCC passes at the end of the
|
||||
/// main CallGraphSCC passes and before any function simplification passes run
|
||||
/// by CGPassManager.
|
||||
void registerCGSCCOptimizerLateEPCallback(
|
||||
const std::function<void(CGSCCPassManager &, OptimizationLevel)> &C) {
|
||||
CGSCCOptimizerLateEPCallbacks.push_back(C);
|
||||
}
|
||||
|
||||
/// \brief Register a callback for a default optimizer pipeline extension
|
||||
/// point
|
||||
///
|
||||
/// This extension point allows adding optimization passes before the
|
||||
/// vectorizer and other highly target specific optimization passes are
|
||||
/// executed.
|
||||
void registerVectorizerStartEPCallback(
|
||||
const std::function<void(FunctionPassManager &, OptimizationLevel)> &C) {
|
||||
VectorizerStartEPCallbacks.push_back(C);
|
||||
}
|
||||
|
||||
/// \brief Register a callback for parsing an AliasAnalysis Name to populate
|
||||
/// the given AAManager \p AA
|
||||
void registerParseAACallback(
|
||||
const std::function<bool(StringRef Name, AAManager &AA)> &C) {
|
||||
AAParsingCallbacks.push_back(C);
|
||||
}
|
||||
|
||||
/// {{@ Register callbacks for analysis registration with this PassBuilder
|
||||
/// instance.
|
||||
/// Callees register their analyses with the given AnalysisManager objects.
|
||||
void registerAnalysisRegistrationCallback(
|
||||
const std::function<void(CGSCCAnalysisManager &)> &C) {
|
||||
CGSCCAnalysisRegistrationCallbacks.push_back(C);
|
||||
}
|
||||
void registerAnalysisRegistrationCallback(
|
||||
const std::function<void(FunctionAnalysisManager &)> &C) {
|
||||
FunctionAnalysisRegistrationCallbacks.push_back(C);
|
||||
}
|
||||
void registerAnalysisRegistrationCallback(
|
||||
const std::function<void(LoopAnalysisManager &)> &C) {
|
||||
LoopAnalysisRegistrationCallbacks.push_back(C);
|
||||
}
|
||||
void registerAnalysisRegistrationCallback(
|
||||
const std::function<void(ModuleAnalysisManager &)> &C) {
|
||||
ModuleAnalysisRegistrationCallbacks.push_back(C);
|
||||
}
|
||||
/// @}}
|
||||
|
||||
/// {{@ Register pipeline parsing callbacks with this pass builder instance.
|
||||
/// Using these callbacks, callers can parse both a single pass name, as well
|
||||
/// as entire sub-pipelines, and populate the PassManager instance
|
||||
/// accordingly.
|
||||
void registerPipelineParsingCallback(
|
||||
const std::function<bool(StringRef Name, CGSCCPassManager &,
|
||||
ArrayRef<PipelineElement>)> &C) {
|
||||
CGSCCPipelineParsingCallbacks.push_back(C);
|
||||
}
|
||||
void registerPipelineParsingCallback(
|
||||
const std::function<bool(StringRef Name, FunctionPassManager &,
|
||||
ArrayRef<PipelineElement>)> &C) {
|
||||
FunctionPipelineParsingCallbacks.push_back(C);
|
||||
}
|
||||
void registerPipelineParsingCallback(
|
||||
const std::function<bool(StringRef Name, LoopPassManager &,
|
||||
ArrayRef<PipelineElement>)> &C) {
|
||||
LoopPipelineParsingCallbacks.push_back(C);
|
||||
}
|
||||
void registerPipelineParsingCallback(
|
||||
const std::function<bool(StringRef Name, ModulePassManager &,
|
||||
ArrayRef<PipelineElement>)> &C) {
|
||||
ModulePipelineParsingCallbacks.push_back(C);
|
||||
}
|
||||
/// @}}
|
||||
|
||||
/// \brief Register a callback for a top-level pipeline entry.
|
||||
///
|
||||
/// If the PassManager type is not given at the top level of the pipeline
|
||||
/// text, this Callback should be used to determine the appropriate stack of
|
||||
/// PassManagers and populate the passed ModulePassManager.
|
||||
void registerParseTopLevelPipelineCallback(
|
||||
const std::function<bool(ModulePassManager &, ArrayRef<PipelineElement>,
|
||||
bool VerifyEachPass, bool DebugLogging)> &C) {
|
||||
TopLevelPipelineParsingCallbacks.push_back(C);
|
||||
}
|
||||
|
||||
private:
|
||||
static Optional<std::vector<PipelineElement>>
|
||||
parsePipelineText(StringRef Text);
|
||||
|
||||
@@ -392,7 +547,106 @@ private:
|
||||
bool parseModulePassPipeline(ModulePassManager &MPM,
|
||||
ArrayRef<PipelineElement> Pipeline,
|
||||
bool VerifyEachPass, bool DebugLogging);
|
||||
|
||||
void addPGOInstrPasses(ModulePassManager &MPM, bool DebugLogging,
|
||||
OptimizationLevel Level, bool RunProfileGen,
|
||||
std::string ProfileGenFile,
|
||||
std::string ProfileUseFile);
|
||||
|
||||
void invokePeepholeEPCallbacks(FunctionPassManager &, OptimizationLevel);
|
||||
|
||||
// Extension Point callbacks
|
||||
SmallVector<std::function<void(FunctionPassManager &, OptimizationLevel)>, 2>
|
||||
PeepholeEPCallbacks;
|
||||
SmallVector<std::function<void(LoopPassManager &, OptimizationLevel)>, 2>
|
||||
LateLoopOptimizationsEPCallbacks;
|
||||
SmallVector<std::function<void(LoopPassManager &, OptimizationLevel)>, 2>
|
||||
LoopOptimizerEndEPCallbacks;
|
||||
SmallVector<std::function<void(FunctionPassManager &, OptimizationLevel)>, 2>
|
||||
ScalarOptimizerLateEPCallbacks;
|
||||
SmallVector<std::function<void(CGSCCPassManager &, OptimizationLevel)>, 2>
|
||||
CGSCCOptimizerLateEPCallbacks;
|
||||
SmallVector<std::function<void(FunctionPassManager &, OptimizationLevel)>, 2>
|
||||
VectorizerStartEPCallbacks;
|
||||
// Module callbacks
|
||||
SmallVector<std::function<void(ModuleAnalysisManager &)>, 2>
|
||||
ModuleAnalysisRegistrationCallbacks;
|
||||
SmallVector<std::function<bool(StringRef, ModulePassManager &,
|
||||
ArrayRef<PipelineElement>)>,
|
||||
2>
|
||||
ModulePipelineParsingCallbacks;
|
||||
SmallVector<std::function<bool(ModulePassManager &, ArrayRef<PipelineElement>,
|
||||
bool VerifyEachPass, bool DebugLogging)>,
|
||||
2>
|
||||
TopLevelPipelineParsingCallbacks;
|
||||
// CGSCC callbacks
|
||||
SmallVector<std::function<void(CGSCCAnalysisManager &)>, 2>
|
||||
CGSCCAnalysisRegistrationCallbacks;
|
||||
SmallVector<std::function<bool(StringRef, CGSCCPassManager &,
|
||||
ArrayRef<PipelineElement>)>,
|
||||
2>
|
||||
CGSCCPipelineParsingCallbacks;
|
||||
// Function callbacks
|
||||
SmallVector<std::function<void(FunctionAnalysisManager &)>, 2>
|
||||
FunctionAnalysisRegistrationCallbacks;
|
||||
SmallVector<std::function<bool(StringRef, FunctionPassManager &,
|
||||
ArrayRef<PipelineElement>)>,
|
||||
2>
|
||||
FunctionPipelineParsingCallbacks;
|
||||
// Loop callbacks
|
||||
SmallVector<std::function<void(LoopAnalysisManager &)>, 2>
|
||||
LoopAnalysisRegistrationCallbacks;
|
||||
SmallVector<std::function<bool(StringRef, LoopPassManager &,
|
||||
ArrayRef<PipelineElement>)>,
|
||||
2>
|
||||
LoopPipelineParsingCallbacks;
|
||||
// AA callbacks
|
||||
SmallVector<std::function<bool(StringRef Name, AAManager &AA)>, 2>
|
||||
AAParsingCallbacks;
|
||||
};
|
||||
|
||||
/// This utility template takes care of adding require<> and invalidate<>
|
||||
/// passes for an analysis to a given \c PassManager. It is intended to be used
|
||||
/// during parsing of a pass pipeline when parsing a single PipelineName.
|
||||
/// When registering a new function analysis FancyAnalysis with the pass
|
||||
/// pipeline name "fancy-analysis", a matching ParsePipelineCallback could look
|
||||
/// like this:
|
||||
///
|
||||
/// static bool parseFunctionPipeline(StringRef Name, FunctionPassManager &FPM,
|
||||
/// ArrayRef<PipelineElement> P) {
|
||||
/// if (parseAnalysisUtilityPasses<FancyAnalysis>("fancy-analysis", Name,
|
||||
/// FPM))
|
||||
/// return true;
|
||||
/// return false;
|
||||
/// }
|
||||
template <typename AnalysisT, typename IRUnitT, typename AnalysisManagerT,
|
||||
typename... ExtraArgTs>
|
||||
bool parseAnalysisUtilityPasses(
|
||||
StringRef AnalysisName, StringRef PipelineName,
|
||||
PassManager<IRUnitT, AnalysisManagerT, ExtraArgTs...> &PM) {
|
||||
if (!PipelineName.endswith(">"))
|
||||
return false;
|
||||
// See if this is an invalidate<> pass name
|
||||
if (PipelineName.startswith("invalidate<")) {
|
||||
PipelineName = PipelineName.substr(11, PipelineName.size() - 12);
|
||||
if (PipelineName != AnalysisName)
|
||||
return false;
|
||||
PM.addPass(InvalidateAnalysisPass<AnalysisT>());
|
||||
return true;
|
||||
}
|
||||
|
||||
// See if this is a require<> pass name
|
||||
if (PipelineName.startswith("require<")) {
|
||||
PipelineName = PipelineName.substr(8, PipelineName.size() - 9);
|
||||
if (PipelineName != AnalysisName)
|
||||
return false;
|
||||
PM.addPass(RequireAnalysisPass<AnalysisT, IRUnitT, AnalysisManagerT,
|
||||
ExtraArgTs...>());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -281,28 +281,46 @@ AnalysisKey NoOpLoopAnalysis::Key;
|
||||
|
||||
} // End anonymous namespace.
|
||||
|
||||
void PassBuilder::invokePeepholeEPCallbacks(
|
||||
FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) {
|
||||
for (auto &C : PeepholeEPCallbacks)
|
||||
C(FPM, Level);
|
||||
}
|
||||
|
||||
void PassBuilder::registerModuleAnalyses(ModuleAnalysisManager &MAM) {
|
||||
#define MODULE_ANALYSIS(NAME, CREATE_PASS) \
|
||||
MAM.registerPass([&] { return CREATE_PASS; });
|
||||
#include "PassRegistry.def"
|
||||
|
||||
for (auto &C : ModuleAnalysisRegistrationCallbacks)
|
||||
C(MAM);
|
||||
}
|
||||
|
||||
void PassBuilder::registerCGSCCAnalyses(CGSCCAnalysisManager &CGAM) {
|
||||
#define CGSCC_ANALYSIS(NAME, CREATE_PASS) \
|
||||
CGAM.registerPass([&] { return CREATE_PASS; });
|
||||
#include "PassRegistry.def"
|
||||
|
||||
for (auto &C : CGSCCAnalysisRegistrationCallbacks)
|
||||
C(CGAM);
|
||||
}
|
||||
|
||||
void PassBuilder::registerFunctionAnalyses(FunctionAnalysisManager &FAM) {
|
||||
#define FUNCTION_ANALYSIS(NAME, CREATE_PASS) \
|
||||
FAM.registerPass([&] { return CREATE_PASS; });
|
||||
#include "PassRegistry.def"
|
||||
|
||||
for (auto &C : FunctionAnalysisRegistrationCallbacks)
|
||||
C(FAM);
|
||||
}
|
||||
|
||||
void PassBuilder::registerLoopAnalyses(LoopAnalysisManager &LAM) {
|
||||
#define LOOP_ANALYSIS(NAME, CREATE_PASS) \
|
||||
LAM.registerPass([&] { return CREATE_PASS; });
|
||||
#include "PassRegistry.def"
|
||||
|
||||
for (auto &C : LoopAnalysisRegistrationCallbacks)
|
||||
C(LAM);
|
||||
}
|
||||
|
||||
FunctionPassManager
|
||||
@@ -341,6 +359,8 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
|
||||
if (!isOptimizingForSize(Level))
|
||||
FPM.addPass(LibCallsShrinkWrapPass());
|
||||
|
||||
invokePeepholeEPCallbacks(FPM, Level);
|
||||
|
||||
FPM.addPass(TailCallElimPass());
|
||||
FPM.addPass(SimplifyCFGPass());
|
||||
|
||||
@@ -364,6 +384,10 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
|
||||
LPM1.addPass(SimpleLoopUnswitchPass());
|
||||
LPM2.addPass(IndVarSimplifyPass());
|
||||
LPM2.addPass(LoopIdiomRecognizePass());
|
||||
|
||||
for (auto &C : LateLoopOptimizationsEPCallbacks)
|
||||
C(LPM2, Level);
|
||||
|
||||
LPM2.addPass(LoopDeletionPass());
|
||||
// Do not enable unrolling in PrepareForThinLTO phase during sample PGO
|
||||
// because it changes IR to makes profile annotation in back compile
|
||||
@@ -371,6 +395,9 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
|
||||
if (!PrepareForThinLTO || !PGOOpt || PGOOpt->SampleProfileFile.empty())
|
||||
LPM2.addPass(LoopUnrollPass::createFull(Level));
|
||||
|
||||
for (auto &C : LoopOptimizerEndEPCallbacks)
|
||||
C(LPM2, Level);
|
||||
|
||||
// We provide the opt remark emitter pass for LICM to use. We only need to do
|
||||
// this once as it is immutable.
|
||||
FPM.addPass(RequireAnalysisPass<OptimizationRemarkEmitterAnalysis, Function>());
|
||||
@@ -405,6 +432,7 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
|
||||
// Run instcombine after redundancy and dead bit elimination to exploit
|
||||
// opportunities opened up by them.
|
||||
FPM.addPass(InstCombinePass());
|
||||
invokePeepholeEPCallbacks(FPM, Level);
|
||||
|
||||
// Re-consider control flow based optimizations after redundancy elimination,
|
||||
// redo DCE, etc.
|
||||
@@ -413,19 +441,24 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
|
||||
FPM.addPass(DSEPass());
|
||||
FPM.addPass(createFunctionToLoopPassAdaptor(LICMPass()));
|
||||
|
||||
for (auto &C : ScalarOptimizerLateEPCallbacks)
|
||||
C(FPM, Level);
|
||||
|
||||
// Finally, do an expensive DCE pass to catch all the dead code exposed by
|
||||
// the simplifications and basic cleanup after all the simplifications.
|
||||
FPM.addPass(ADCEPass());
|
||||
FPM.addPass(SimplifyCFGPass());
|
||||
FPM.addPass(InstCombinePass());
|
||||
invokePeepholeEPCallbacks(FPM, Level);
|
||||
|
||||
return FPM;
|
||||
}
|
||||
|
||||
static void addPGOInstrPasses(ModulePassManager &MPM, bool DebugLogging,
|
||||
PassBuilder::OptimizationLevel Level,
|
||||
bool RunProfileGen, std::string ProfileGenFile,
|
||||
std::string ProfileUseFile) {
|
||||
void PassBuilder::addPGOInstrPasses(ModulePassManager &MPM, bool DebugLogging,
|
||||
PassBuilder::OptimizationLevel Level,
|
||||
bool RunProfileGen,
|
||||
std::string ProfileGenFile,
|
||||
std::string ProfileUseFile) {
|
||||
// Generally running simplification passes and the inliner with an high
|
||||
// threshold results in smaller executables, but there may be cases where
|
||||
// the size grows, so let's be conservative here and skip this simplification
|
||||
@@ -450,9 +483,8 @@ static void addPGOInstrPasses(ModulePassManager &MPM, bool DebugLogging,
|
||||
FPM.addPass(EarlyCSEPass()); // Catch trivial redundancies.
|
||||
FPM.addPass(SimplifyCFGPass()); // Merge & remove basic blocks.
|
||||
FPM.addPass(InstCombinePass()); // Combine silly sequences.
|
||||
invokePeepholeEPCallbacks(FPM, Level);
|
||||
|
||||
// FIXME: Here the old pass manager inserts peephole extensions.
|
||||
// Add them when they're supported.
|
||||
CGPipeline.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM)));
|
||||
|
||||
MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPipeline)));
|
||||
@@ -533,6 +565,8 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
|
||||
// optimizations.
|
||||
FunctionPassManager GlobalCleanupPM(DebugLogging);
|
||||
GlobalCleanupPM.addPass(InstCombinePass());
|
||||
invokePeepholeEPCallbacks(GlobalCleanupPM, Level);
|
||||
|
||||
GlobalCleanupPM.addPass(SimplifyCFGPass());
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(GlobalCleanupPM)));
|
||||
|
||||
@@ -597,6 +631,9 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
|
||||
buildFunctionSimplificationPipeline(Level, DebugLogging,
|
||||
PrepareForThinLTO)));
|
||||
|
||||
for (auto &C : CGSCCOptimizerLateEPCallbacks)
|
||||
C(MainCGPipeline, Level);
|
||||
|
||||
// We wrap the CGSCC pipeline in a devirtualization repeater. This will try
|
||||
// to detect when we devirtualize indirect calls and iterate the SCC passes
|
||||
// in that case to try and catch knock-on inlining or function attrs
|
||||
@@ -655,6 +692,9 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
|
||||
// rather than on each loop in an inside-out manner, and so they are actually
|
||||
// function passes.
|
||||
|
||||
for (auto &C : VectorizerStartEPCallbacks)
|
||||
C(OptimizePM, Level);
|
||||
|
||||
// First rotate loops that may have been un-rotated by prior passes.
|
||||
OptimizePM.addPass(createFunctionToLoopPassAdaptor(LoopRotatePass()));
|
||||
|
||||
@@ -883,8 +923,11 @@ ModulePassManager PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
|
||||
// simplification opportunities, and both can propagate functions through
|
||||
// function pointers. When this happens, we often have to resolve varargs
|
||||
// calls, etc, so let instcombine do this.
|
||||
// FIXME: add peephole extensions here as the legacy PM does.
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(InstCombinePass()));
|
||||
FunctionPassManager PeepholeFPM(DebugLogging);
|
||||
PeepholeFPM.addPass(InstCombinePass());
|
||||
invokePeepholeEPCallbacks(PeepholeFPM, Level);
|
||||
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(PeepholeFPM)));
|
||||
|
||||
// Note: historically, the PruneEH pass was run first to deduce nounwind and
|
||||
// generally clean up exception handling overhead. It isn't clear this is
|
||||
@@ -902,10 +945,10 @@ ModulePassManager PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
|
||||
MPM.addPass(GlobalDCEPass());
|
||||
|
||||
FunctionPassManager FPM(DebugLogging);
|
||||
|
||||
// The IPO Passes may leave cruft around. Clean up after them.
|
||||
// FIXME: add peephole extensions here as the legacy PM does.
|
||||
FPM.addPass(InstCombinePass());
|
||||
invokePeepholeEPCallbacks(FPM, Level);
|
||||
|
||||
FPM.addPass(JumpThreadingPass());
|
||||
|
||||
// Break up allocas
|
||||
@@ -952,8 +995,11 @@ ModulePassManager PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
|
||||
MainFPM.add(AlignmentFromAssumptionsPass());
|
||||
#endif
|
||||
|
||||
// FIXME: add peephole extensions to the PM here.
|
||||
// FIXME: Conditionally run LoadCombine here, after it's ported
|
||||
// (in case we still have this pass, given its questionable usefulness).
|
||||
|
||||
MainFPM.addPass(InstCombinePass());
|
||||
invokePeepholeEPCallbacks(MainFPM, Level);
|
||||
MainFPM.addPass(JumpThreadingPass());
|
||||
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(MainFPM)));
|
||||
|
||||
@@ -1036,7 +1082,27 @@ static bool startsWithDefaultPipelineAliasPrefix(StringRef Name) {
|
||||
Name.startswith("lto");
|
||||
}
|
||||
|
||||
static bool isModulePassName(StringRef Name) {
|
||||
/// Tests whether registered callbacks will accept a given pass name.
|
||||
///
|
||||
/// When parsing a pipeline text, the type of the outermost pipeline may be
|
||||
/// omitted, in which case the type is automatically determined from the first
|
||||
/// pass name in the text. This may be a name that is handled through one of the
|
||||
/// callbacks. We check this through the oridinary parsing callbacks by setting
|
||||
/// up a dummy PassManager in order to not force the client to also handle this
|
||||
/// type of query.
|
||||
template <typename PassManagerT, typename CallbacksT>
|
||||
static bool callbacksAcceptPassName(StringRef Name, CallbacksT &Callbacks) {
|
||||
if (!Callbacks.empty()) {
|
||||
PassManagerT DummyPM;
|
||||
for (auto &CB : Callbacks)
|
||||
if (CB(Name, DummyPM, {}))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename CallbacksT>
|
||||
static bool isModulePassName(StringRef Name, CallbacksT &Callbacks) {
|
||||
// Manually handle aliases for pre-configured pipeline fragments.
|
||||
if (startsWithDefaultPipelineAliasPrefix(Name))
|
||||
return DefaultAliasRegex.match(Name);
|
||||
@@ -1061,10 +1127,11 @@ static bool isModulePassName(StringRef Name) {
|
||||
return true;
|
||||
#include "PassRegistry.def"
|
||||
|
||||
return false;
|
||||
return callbacksAcceptPassName<ModulePassManager>(Name, Callbacks);
|
||||
}
|
||||
|
||||
static bool isCGSCCPassName(StringRef Name) {
|
||||
template <typename CallbacksT>
|
||||
static bool isCGSCCPassName(StringRef Name, CallbacksT &Callbacks) {
|
||||
// Explicitly handle pass manager names.
|
||||
if (Name == "cgscc")
|
||||
return true;
|
||||
@@ -1085,10 +1152,11 @@ static bool isCGSCCPassName(StringRef Name) {
|
||||
return true;
|
||||
#include "PassRegistry.def"
|
||||
|
||||
return false;
|
||||
return callbacksAcceptPassName<CGSCCPassManager>(Name, Callbacks);
|
||||
}
|
||||
|
||||
static bool isFunctionPassName(StringRef Name) {
|
||||
template <typename CallbacksT>
|
||||
static bool isFunctionPassName(StringRef Name, CallbacksT &Callbacks) {
|
||||
// Explicitly handle pass manager names.
|
||||
if (Name == "function")
|
||||
return true;
|
||||
@@ -1107,10 +1175,11 @@ static bool isFunctionPassName(StringRef Name) {
|
||||
return true;
|
||||
#include "PassRegistry.def"
|
||||
|
||||
return false;
|
||||
return callbacksAcceptPassName<FunctionPassManager>(Name, Callbacks);
|
||||
}
|
||||
|
||||
static bool isLoopPassName(StringRef Name) {
|
||||
template <typename CallbacksT>
|
||||
static bool isLoopPassName(StringRef Name, CallbacksT &Callbacks) {
|
||||
// Explicitly handle pass manager names.
|
||||
if (Name == "loop")
|
||||
return true;
|
||||
@@ -1127,7 +1196,7 @@ static bool isLoopPassName(StringRef Name) {
|
||||
return true;
|
||||
#include "PassRegistry.def"
|
||||
|
||||
return false;
|
||||
return callbacksAcceptPassName<LoopPassManager>(Name, Callbacks);
|
||||
}
|
||||
|
||||
Optional<std::vector<PassBuilder::PipelineElement>>
|
||||
@@ -1228,6 +1297,11 @@ bool PassBuilder::parseModulePass(ModulePassManager &MPM,
|
||||
MPM.addPass(createRepeatedPass(*Count, std::move(NestedMPM)));
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto &C : ModulePipelineParsingCallbacks)
|
||||
if (C(Name, MPM, InnerPipeline))
|
||||
return true;
|
||||
|
||||
// Normal passes can't have pipelines.
|
||||
return false;
|
||||
}
|
||||
@@ -1240,12 +1314,12 @@ bool PassBuilder::parseModulePass(ModulePassManager &MPM,
|
||||
assert(Matches.size() == 3 && "Must capture two matched strings!");
|
||||
|
||||
OptimizationLevel L = StringSwitch<OptimizationLevel>(Matches[2])
|
||||
.Case("O0", O0)
|
||||
.Case("O1", O1)
|
||||
.Case("O2", O2)
|
||||
.Case("O3", O3)
|
||||
.Case("Os", Os)
|
||||
.Case("Oz", Oz);
|
||||
.Case("O0", O0)
|
||||
.Case("O1", O1)
|
||||
.Case("O2", O2)
|
||||
.Case("O3", O3)
|
||||
.Case("Os", Os)
|
||||
.Case("Oz", Oz);
|
||||
if (L == O0)
|
||||
// At O0 we do nothing at all!
|
||||
return true;
|
||||
@@ -1285,6 +1359,9 @@ bool PassBuilder::parseModulePass(ModulePassManager &MPM,
|
||||
}
|
||||
#include "PassRegistry.def"
|
||||
|
||||
for (auto &C : ModulePipelineParsingCallbacks)
|
||||
if (C(Name, MPM, InnerPipeline))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1332,11 +1409,16 @@ bool PassBuilder::parseCGSCCPass(CGSCCPassManager &CGPM,
|
||||
*MaxRepetitions, DebugLogging));
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto &C : CGSCCPipelineParsingCallbacks)
|
||||
if (C(Name, CGPM, InnerPipeline))
|
||||
return true;
|
||||
|
||||
// Normal passes can't have pipelines.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now expand the basic registered passes from the .inc file.
|
||||
// Now expand the basic registered passes from the .inc file.
|
||||
#define CGSCC_PASS(NAME, CREATE_PASS) \
|
||||
if (Name == NAME) { \
|
||||
CGPM.addPass(CREATE_PASS); \
|
||||
@@ -1357,6 +1439,9 @@ bool PassBuilder::parseCGSCCPass(CGSCCPassManager &CGPM,
|
||||
}
|
||||
#include "PassRegistry.def"
|
||||
|
||||
for (auto &C : CGSCCPipelineParsingCallbacks)
|
||||
if (C(Name, CGPM, InnerPipeline))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1394,11 +1479,16 @@ bool PassBuilder::parseFunctionPass(FunctionPassManager &FPM,
|
||||
FPM.addPass(createRepeatedPass(*Count, std::move(NestedFPM)));
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto &C : FunctionPipelineParsingCallbacks)
|
||||
if (C(Name, FPM, InnerPipeline))
|
||||
return true;
|
||||
|
||||
// Normal passes can't have pipelines.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now expand the basic registered passes from the .inc file.
|
||||
// Now expand the basic registered passes from the .inc file.
|
||||
#define FUNCTION_PASS(NAME, CREATE_PASS) \
|
||||
if (Name == NAME) { \
|
||||
FPM.addPass(CREATE_PASS); \
|
||||
@@ -1418,6 +1508,9 @@ bool PassBuilder::parseFunctionPass(FunctionPassManager &FPM,
|
||||
}
|
||||
#include "PassRegistry.def"
|
||||
|
||||
for (auto &C : FunctionPipelineParsingCallbacks)
|
||||
if (C(Name, FPM, InnerPipeline))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1445,11 +1538,16 @@ bool PassBuilder::parseLoopPass(LoopPassManager &LPM, const PipelineElement &E,
|
||||
LPM.addPass(createRepeatedPass(*Count, std::move(NestedLPM)));
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto &C : LoopPipelineParsingCallbacks)
|
||||
if (C(Name, LPM, InnerPipeline))
|
||||
return true;
|
||||
|
||||
// Normal passes can't have pipelines.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now expand the basic registered passes from the .inc file.
|
||||
// Now expand the basic registered passes from the .inc file.
|
||||
#define LOOP_PASS(NAME, CREATE_PASS) \
|
||||
if (Name == NAME) { \
|
||||
LPM.addPass(CREATE_PASS); \
|
||||
@@ -1470,6 +1568,9 @@ bool PassBuilder::parseLoopPass(LoopPassManager &LPM, const PipelineElement &E,
|
||||
}
|
||||
#include "PassRegistry.def"
|
||||
|
||||
for (auto &C : LoopPipelineParsingCallbacks)
|
||||
if (C(Name, LPM, InnerPipeline))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1488,6 +1589,9 @@ bool PassBuilder::parseAAPassName(AAManager &AA, StringRef Name) {
|
||||
}
|
||||
#include "PassRegistry.def"
|
||||
|
||||
for (auto &C : AAParsingCallbacks)
|
||||
if (C(Name, AA))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1554,7 +1658,7 @@ bool PassBuilder::parseModulePassPipeline(ModulePassManager &MPM,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Primary pass pipeline description parsing routine.
|
||||
// Primary pass pipeline description parsing routine for a \c ModulePassManager
|
||||
// FIXME: Should this routine accept a TargetMachine or require the caller to
|
||||
// pre-populate the analysis managers with target-specific stuff?
|
||||
bool PassBuilder::parsePassPipeline(ModulePassManager &MPM,
|
||||
@@ -1568,21 +1672,70 @@ bool PassBuilder::parsePassPipeline(ModulePassManager &MPM,
|
||||
// automatically.
|
||||
StringRef FirstName = Pipeline->front().Name;
|
||||
|
||||
if (!isModulePassName(FirstName)) {
|
||||
if (isCGSCCPassName(FirstName))
|
||||
if (!isModulePassName(FirstName, ModulePipelineParsingCallbacks)) {
|
||||
if (isCGSCCPassName(FirstName, CGSCCPipelineParsingCallbacks)) {
|
||||
Pipeline = {{"cgscc", std::move(*Pipeline)}};
|
||||
else if (isFunctionPassName(FirstName))
|
||||
} else if (isFunctionPassName(FirstName,
|
||||
FunctionPipelineParsingCallbacks)) {
|
||||
Pipeline = {{"function", std::move(*Pipeline)}};
|
||||
else if (isLoopPassName(FirstName))
|
||||
} else if (isLoopPassName(FirstName, LoopPipelineParsingCallbacks)) {
|
||||
Pipeline = {{"function", {{"loop", std::move(*Pipeline)}}}};
|
||||
else
|
||||
} else {
|
||||
for (auto &C : TopLevelPipelineParsingCallbacks)
|
||||
if (C(MPM, *Pipeline, VerifyEachPass, DebugLogging))
|
||||
return true;
|
||||
|
||||
// Unknown pass name!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return parseModulePassPipeline(MPM, *Pipeline, VerifyEachPass, DebugLogging);
|
||||
}
|
||||
|
||||
// Primary pass pipeline description parsing routine for a \c CGSCCPassManager
|
||||
bool PassBuilder::parsePassPipeline(CGSCCPassManager &CGPM,
|
||||
StringRef PipelineText, bool VerifyEachPass,
|
||||
bool DebugLogging) {
|
||||
auto Pipeline = parsePipelineText(PipelineText);
|
||||
if (!Pipeline || Pipeline->empty())
|
||||
return false;
|
||||
|
||||
StringRef FirstName = Pipeline->front().Name;
|
||||
if (!isCGSCCPassName(FirstName, CGSCCPipelineParsingCallbacks))
|
||||
return false;
|
||||
|
||||
return parseCGSCCPassPipeline(CGPM, *Pipeline, VerifyEachPass, DebugLogging);
|
||||
}
|
||||
|
||||
// Primary pass pipeline description parsing routine for a \c
|
||||
// FunctionPassManager
|
||||
bool PassBuilder::parsePassPipeline(FunctionPassManager &FPM,
|
||||
StringRef PipelineText, bool VerifyEachPass,
|
||||
bool DebugLogging) {
|
||||
auto Pipeline = parsePipelineText(PipelineText);
|
||||
if (!Pipeline || Pipeline->empty())
|
||||
return false;
|
||||
|
||||
StringRef FirstName = Pipeline->front().Name;
|
||||
if (!isFunctionPassName(FirstName, FunctionPipelineParsingCallbacks))
|
||||
return false;
|
||||
|
||||
return parseFunctionPassPipeline(FPM, *Pipeline, VerifyEachPass,
|
||||
DebugLogging);
|
||||
}
|
||||
|
||||
// Primary pass pipeline description parsing routine for a \c LoopPassManager
|
||||
bool PassBuilder::parsePassPipeline(LoopPassManager &CGPM,
|
||||
StringRef PipelineText, bool VerifyEachPass,
|
||||
bool DebugLogging) {
|
||||
auto Pipeline = parsePipelineText(PipelineText);
|
||||
if (!Pipeline || Pipeline->empty())
|
||||
return false;
|
||||
|
||||
return parseLoopPassPipeline(CGPM, *Pipeline, VerifyEachPass, DebugLogging);
|
||||
}
|
||||
|
||||
bool PassBuilder::parseAAPipeline(AAManager &AA, StringRef PipelineText) {
|
||||
// If the pipeline just consists of the word 'default' just replace the AA
|
||||
// manager with our default one.
|
||||
|
||||
@@ -26,6 +26,37 @@
|
||||
; RUN: -passes='lto-pre-link<O2>' -S %s 2>&1 \
|
||||
; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O2
|
||||
|
||||
; RUN: opt -disable-verify -debug-pass-manager \
|
||||
; RUN: -passes-ep-peephole='no-op-function' \
|
||||
; RUN: -passes='default<O3>' -S %s 2>&1 \
|
||||
; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
|
||||
; RUN: --check-prefix=CHECK-EP-PEEPHOLE
|
||||
; RUN: opt -disable-verify -debug-pass-manager \
|
||||
; RUN: -passes-ep-late-loop-optimizations='no-op-loop' \
|
||||
; RUN: -passes='default<O3>' -S %s 2>&1 \
|
||||
; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
|
||||
; RUN: --check-prefix=CHECK-EP-LOOP-LATE
|
||||
; RUN: opt -disable-verify -debug-pass-manager \
|
||||
; RUN: -passes-ep-loop-optimizer-end='no-op-loop' \
|
||||
; RUN: -passes='default<O3>' -S %s 2>&1 \
|
||||
; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
|
||||
; RUN: --check-prefix=CHECK-EP-LOOP-END
|
||||
; RUN: opt -disable-verify -debug-pass-manager \
|
||||
; RUN: -passes-ep-scalar-optimizer-late='no-op-function' \
|
||||
; RUN: -passes='default<O3>' -S %s 2>&1 \
|
||||
; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
|
||||
; RUN: --check-prefix=CHECK-EP-SCALAR-LATE
|
||||
; RUN: opt -disable-verify -debug-pass-manager \
|
||||
; RUN: -passes-ep-cgscc-optimizer-late='no-op-cgscc' \
|
||||
; RUN: -passes='default<O3>' -S %s 2>&1 \
|
||||
; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
|
||||
; RUN: --check-prefix=CHECK-EP-CGSCC-LATE
|
||||
; RUN: opt -disable-verify -debug-pass-manager \
|
||||
; RUN: -passes-ep-vectorizer-start='no-op-function' \
|
||||
; RUN: -passes='default<O3>' -S %s 2>&1 \
|
||||
; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \
|
||||
; RUN: --check-prefix=CHECK-EP-VECTORIZER-START
|
||||
|
||||
; CHECK-O: Starting llvm::Module pass manager run.
|
||||
; CHECK-O-NEXT: Running pass: PassManager<{{.*}}Module{{.*}}>
|
||||
; CHECK-O-NEXT: Starting llvm::Module pass manager run.
|
||||
@@ -53,6 +84,7 @@
|
||||
; CHECK-O-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PassManager{{.*}}>
|
||||
; CHECK-O-NEXT: Starting llvm::Function pass manager run.
|
||||
; CHECK-O-NEXT: Running pass: InstCombinePass
|
||||
; CHECK-EP-PEEPHOLE-NEXT: Running pass: NoOpFunctionPass
|
||||
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
|
||||
; CHECK-O-NEXT: Finished llvm::Function pass manager run.
|
||||
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}GlobalsAA
|
||||
@@ -84,6 +116,7 @@
|
||||
; CHECK-O1-NEXT: Running pass: LibCallsShrinkWrapPass
|
||||
; CHECK-O2-NEXT: Running pass: LibCallsShrinkWrapPass
|
||||
; CHECK-O3-NEXT: Running pass: LibCallsShrinkWrapPass
|
||||
; CHECK-EP-PEEPHOLE-NEXT: Running pass: NoOpFunctionPass
|
||||
; CHECK-O-NEXT: Running pass: TailCallElimPass
|
||||
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
|
||||
; CHECK-O-NEXT: Running pass: ReassociatePass
|
||||
@@ -105,8 +138,10 @@
|
||||
; CHECK-O-NEXT: Starting Loop pass manager run.
|
||||
; CHECK-O-NEXT: Running pass: IndVarSimplifyPass
|
||||
; CHECK-O-NEXT: Running pass: LoopIdiomRecognizePass
|
||||
; CHECK-EP-LOOP-LATE-NEXT: Running pass: NoOpLoopPass
|
||||
; CHECK-O-NEXT: Running pass: LoopDeletionPass
|
||||
; CHECK-O-NEXT: Running pass: LoopUnrollPass
|
||||
; CHECK-EP-LOOP-END-NEXT: Running pass: NoOpLoopPass
|
||||
; CHECK-O-NEXT: Finished Loop pass manager run.
|
||||
; CHECK-Os-NEXT: Running pass: MergedLoadStoreMotionPass
|
||||
; CHECK-Os-NEXT: Running pass: GVN
|
||||
@@ -126,15 +161,19 @@
|
||||
; CHECK-O-NEXT: Running pass: BDCEPass
|
||||
; CHECK-O-NEXT: Running analysis: DemandedBitsAnalysis
|
||||
; CHECK-O-NEXT: Running pass: InstCombinePass
|
||||
; CHECK-EP-PEEPHOLE-NEXT: Running pass: NoOpFunctionPass
|
||||
; CHECK-O-NEXT: Running pass: JumpThreadingPass
|
||||
; CHECK-O-NEXT: Running pass: CorrelatedValuePropagationPass
|
||||
; CHECK-O-NEXT: Running pass: DSEPass
|
||||
; CHECK-O-NEXT: Running pass: FunctionToLoopPassAdaptor<{{.*}}LICMPass{{.*}}>
|
||||
; CHECK-EP-SCALAR-LATE-NEXT: Running pass: NoOpFunctionPass
|
||||
; CHECK-O-NEXT: Running pass: ADCEPass
|
||||
; CHECK-O-NEXT: Running analysis: PostDominatorTreeAnalysis
|
||||
; CHECK-O-NEXT: Running pass: SimplifyCFGPass
|
||||
; CHECK-O-NEXT: Running pass: InstCombinePass
|
||||
; CHECK-EP-PEEPHOLE-NEXT: Running pass: NoOpFunctionPass
|
||||
; CHECK-O-NEXT: Finished llvm::Function pass manager run.
|
||||
; CHECK-EP-CGSCC-LATE-NEXT: Running pass: NoOpCGSCCPass
|
||||
; CHECK-O-NEXT: Finished CGSCC pass manager run.
|
||||
; CHECK-O-NEXT: Finished llvm::Module pass manager run.
|
||||
; CHECK-O-NEXT: Running pass: PassManager<{{.*}}Module{{.*}}>
|
||||
@@ -146,6 +185,7 @@
|
||||
; CHECK-O-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PassManager{{.*}}>
|
||||
; CHECK-O-NEXT: Starting llvm::Function pass manager run.
|
||||
; CHECK-O-NEXT: Running pass: Float2IntPass
|
||||
; CHECK-EP-VECTORIZER-START-NEXT: Running pass: NoOpFunctionPass
|
||||
; CHECK-O-NEXT: Running pass: FunctionToLoopPassAdaptor<{{.*}}LoopRotatePass
|
||||
; CHECK-O-NEXT: Running pass: LoopDistributePass
|
||||
; CHECK-O-NEXT: Running pass: LoopVectorizePass
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
; RUN: opt -disable-verify -debug-pass-manager \
|
||||
; RUN: -passes='lto<Oz>' -S %s 2>&1 \
|
||||
; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O2
|
||||
; RUN: opt -disable-verify -debug-pass-manager \
|
||||
; RUN: -passes='lto<O3>' -S %s -passes-ep-peephole='no-op-function' 2>&1 \
|
||||
; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O2 \
|
||||
; RUN: --check-prefix=CHECK-EP-Peephole
|
||||
|
||||
; CHECK-O: Starting llvm::Module pass manager run.
|
||||
; CHECK-O-NEXT: Running pass: PassManager<{{.*}}Module
|
||||
@@ -45,13 +49,18 @@
|
||||
; CHECK-O2-NEXT: Running analysis: AssumptionAnalysis
|
||||
; CHECK-O2-NEXT: Running pass: ConstantMergePass
|
||||
; CHECK-O2-NEXT: Running pass: DeadArgumentEliminationPass
|
||||
; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}InstCombinePass>
|
||||
; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PassManager{{.*}}>
|
||||
; CHECK-O2-NEXT: Starting llvm::Function pass manager run.
|
||||
; CHECK-O2-NEXT: Running pass: InstCombinePass
|
||||
; CHECK-EP-Peephole-NEXT: Running pass: NoOpFunctionPass
|
||||
; CHECK-O2-NEXT: Finished llvm::Function pass manager run.
|
||||
; CHECK-O2-NEXT: Running pass: ModuleToPostOrderCGSCCPassAdaptor<{{.*}}InlinerPass>
|
||||
; CHECK-O2-NEXT: Running pass: GlobalOptPass
|
||||
; CHECK-O2-NEXT: Running pass: GlobalDCEPass
|
||||
; CHECK-O2-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PassManager{{.*}}>
|
||||
; CHECK-O2-NEXT: Starting llvm::Function pass manager run.
|
||||
; CHECK-O2-NEXT: Running pass: InstCombinePass
|
||||
; CHECK-EP-Peephole-NEXT: Running pass: NoOpFunctionPass
|
||||
; CHECK-O2-NEXT: Running pass: JumpThreadingPass
|
||||
; CHECK-O2-NEXT: Running analysis: LazyValueAnalysis
|
||||
; CHECK-O2-NEXT: Running pass: SROA on foo
|
||||
|
||||
@@ -48,6 +48,83 @@ static cl::opt<std::string>
|
||||
"pipeline for handling managed aliasing queries"),
|
||||
cl::Hidden);
|
||||
|
||||
/// {{@ These options accept textual pipeline descriptions which will be
|
||||
/// inserted into default pipelines at the respective extension points
|
||||
static cl::opt<std::string> PeepholeEPPipeline(
|
||||
"passes-ep-peephole",
|
||||
cl::desc("A textual description of the function pass pipeline inserted at "
|
||||
"the Peephole extension points into default pipelines"),
|
||||
cl::Hidden);
|
||||
static cl::opt<std::string> LateLoopOptimizationsEPPipeline(
|
||||
"passes-ep-late-loop-optimizations",
|
||||
cl::desc(
|
||||
"A textual description of the loop pass pipeline inserted at "
|
||||
"the LateLoopOptimizations extension point into default pipelines"),
|
||||
cl::Hidden);
|
||||
static cl::opt<std::string> LoopOptimizerEndEPPipeline(
|
||||
"passes-ep-loop-optimizer-end",
|
||||
cl::desc("A textual description of the loop pass pipeline inserted at "
|
||||
"the LoopOptimizerEnd extension point into default pipelines"),
|
||||
cl::Hidden);
|
||||
static cl::opt<std::string> ScalarOptimizerLateEPPipeline(
|
||||
"passes-ep-scalar-optimizer-late",
|
||||
cl::desc("A textual description of the function pass pipeline inserted at "
|
||||
"the ScalarOptimizerLate extension point into default pipelines"),
|
||||
cl::Hidden);
|
||||
static cl::opt<std::string> CGSCCOptimizerLateEPPipeline(
|
||||
"passes-ep-cgscc-optimizer-late",
|
||||
cl::desc("A textual description of the cgscc pass pipeline inserted at "
|
||||
"the CGSCCOptimizerLate extension point into default pipelines"),
|
||||
cl::Hidden);
|
||||
static cl::opt<std::string> VectorizerStartEPPipeline(
|
||||
"passes-ep-vectorizer-start",
|
||||
cl::desc("A textual description of the function pass pipeline inserted at "
|
||||
"the VectorizerStart extension point into default pipelines"),
|
||||
cl::Hidden);
|
||||
/// @}}
|
||||
|
||||
/// If one of the EPPipeline command line options was given, register callbacks
|
||||
/// for parsing and inserting the given pipeline
|
||||
static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass,
|
||||
bool DebugLogging) {
|
||||
if (!PeepholeEPPipeline.empty())
|
||||
PB.registerPeepholeEPCallback(
|
||||
[&](FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
|
||||
return PB.parsePassPipeline(PM, PeepholeEPPipeline, VerifyEachPass,
|
||||
DebugPM);
|
||||
});
|
||||
if (!LateLoopOptimizationsEPPipeline.empty())
|
||||
PB.registerLateLoopOptimizationsEPCallback(
|
||||
[&](LoopPassManager &PM, PassBuilder::OptimizationLevel Level) {
|
||||
return PB.parsePassPipeline(PM, LateLoopOptimizationsEPPipeline,
|
||||
VerifyEachPass, DebugPM);
|
||||
});
|
||||
if (!LoopOptimizerEndEPPipeline.empty())
|
||||
PB.registerLoopOptimizerEndEPCallback(
|
||||
[&](LoopPassManager &PM, PassBuilder::OptimizationLevel Level) {
|
||||
return PB.parsePassPipeline(PM, LoopOptimizerEndEPPipeline,
|
||||
VerifyEachPass, DebugPM);
|
||||
});
|
||||
if (!ScalarOptimizerLateEPPipeline.empty())
|
||||
PB.registerScalarOptimizerLateEPCallback(
|
||||
[&](FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
|
||||
return PB.parsePassPipeline(PM, ScalarOptimizerLateEPPipeline,
|
||||
VerifyEachPass, DebugPM);
|
||||
});
|
||||
if (!CGSCCOptimizerLateEPPipeline.empty())
|
||||
PB.registerCGSCCOptimizerLateEPCallback(
|
||||
[&](CGSCCPassManager &PM, PassBuilder::OptimizationLevel Level) {
|
||||
return PB.parsePassPipeline(PM, CGSCCOptimizerLateEPPipeline,
|
||||
VerifyEachPass, DebugPM);
|
||||
});
|
||||
if (!VectorizerStartEPPipeline.empty())
|
||||
PB.registerVectorizerStartEPCallback(
|
||||
[&](FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) {
|
||||
return PB.parsePassPipeline(PM, VectorizerStartEPPipeline,
|
||||
VerifyEachPass, DebugPM);
|
||||
});
|
||||
}
|
||||
|
||||
bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
|
||||
tool_output_file *Out,
|
||||
tool_output_file *ThinLTOLinkOut,
|
||||
@@ -56,7 +133,9 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
|
||||
bool ShouldPreserveAssemblyUseListOrder,
|
||||
bool ShouldPreserveBitcodeUseListOrder,
|
||||
bool EmitSummaryIndex, bool EmitModuleHash) {
|
||||
bool VerifyEachPass = VK == VK_VerifyEachPass;
|
||||
PassBuilder PB(TM);
|
||||
registerEPCallbacks(PB, VerifyEachPass, DebugPM);
|
||||
|
||||
// Specially handle the alias analysis manager so that we can register
|
||||
// a custom pipeline of AA passes with it.
|
||||
@@ -85,8 +164,7 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
|
||||
if (VK > VK_NoVerifier)
|
||||
MPM.addPass(VerifierPass());
|
||||
|
||||
if (!PB.parsePassPipeline(MPM, PassPipeline, VK == VK_VerifyEachPass,
|
||||
DebugPM)) {
|
||||
if (!PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) {
|
||||
errs() << Arg0 << ": unable to parse pass pipeline description.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
AsmParser
|
||||
Core
|
||||
Support
|
||||
Passes
|
||||
)
|
||||
|
||||
set(IRSources
|
||||
@@ -15,6 +16,7 @@ set(IRSources
|
||||
DebugTypeODRUniquingTest.cpp
|
||||
DominatorTreeTest.cpp
|
||||
FunctionTest.cpp
|
||||
PassBuilderCallbacksTest.cpp
|
||||
IRBuilderTest.cpp
|
||||
InstructionsTest.cpp
|
||||
IntrinsicsTest.cpp
|
||||
|
||||
520
unittests/IR/PassBuilderCallbacksTest.cpp
Normal file
520
unittests/IR/PassBuilderCallbacksTest.cpp
Normal file
@@ -0,0 +1,520 @@
|
||||
//===- 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.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <llvm/Analysis/CGSCCPassManager.h>
|
||||
#include <llvm/Analysis/LoopAnalysisManager.h>
|
||||
#include <llvm/AsmParser/Parser.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/PassManager.h>
|
||||
#include <llvm/Passes/PassBuilder.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::DoDefault;
|
||||
using testing::Return;
|
||||
using testing::Expectation;
|
||||
using testing::Invoke;
|
||||
using testing::WithArgs;
|
||||
using testing::_;
|
||||
|
||||
/// \brief 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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief 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...);
|
||||
}
|
||||
};
|
||||
|
||||
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 &));
|
||||
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));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
PassBuilder PB;
|
||||
ModulePassManager PM;
|
||||
LoopAnalysisManager LAM;
|
||||
FunctionAnalysisManager FAM;
|
||||
CGSCCAnalysisManager CGAM;
|
||||
ModuleAnalysisManager AM;
|
||||
|
||||
MockPassHandle<IRUnitT> PassHandle;
|
||||
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")),
|
||||
PM(true), LAM(true), FAM(true), CGAM(true), AM(true) {
|
||||
|
||||
/// 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());
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
/// 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>;
|
||||
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_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
|
||||
<< "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_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"), _, _, _))
|
||||
.WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult)));
|
||||
|
||||
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)"), _, _, _))
|
||||
.WillOnce(WithArgs<0, 1, 2>(Invoke(getAnalysisResult)));
|
||||
|
||||
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.
|
||||
///
|
||||
/// 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_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
|
||||
<< "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_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
|
||||
<< "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_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
|
||||
<< "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_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
|
||||
<< "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 VerifyEachPass, 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_TRUE(PB.parsePassPipeline(PM, PipelineText, true))
|
||||
<< "Pipeline was: " << PipelineText;
|
||||
PM.run(*M, AM);
|
||||
|
||||
/// Test the negative case
|
||||
PipelineText = "another-pipeline(instcombine)";
|
||||
ASSERT_FALSE(PB.parsePassPipeline(PM, PipelineText, true))
|
||||
<< "Pipeline was: " << PipelineText;
|
||||
}
|
||||
} // end anonymous namespace
|
||||
Reference in New Issue
Block a user