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
/// 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:
///
/// 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
/// 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
/// 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);
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
} // 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
if (!isOptimizingForSize(Level))
FPM.addPass(LibCallsShrinkWrapPass());
+ invokePeepholeEPCallbacks(FPM, Level);
+
FPM.addPass(TailCallElimPass());
FPM.addPass(SimplifyCFGPass());
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
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>());
// 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.
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
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)));
// optimizations.
FunctionPassManager GlobalCleanupPM(DebugLogging);
GlobalCleanupPM.addPass(InstCombinePass());
+ invokePeepholeEPCallbacks(GlobalCleanupPM, Level);
+
GlobalCleanupPM.addPass(SimplifyCFGPass());
MPM.addPass(createModuleToFunctionPassAdaptor(std::move(GlobalCleanupPM)));
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
// 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()));
// 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
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
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)));
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);
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;
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;
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;
return true;
#include "PassRegistry.def"
- return false;
+ return callbacksAcceptPassName<LoopPassManager>(Name, Callbacks);
}
Optional<std::vector<PassBuilder::PipelineElement>>
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;
}
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;
}
#include "PassRegistry.def"
+ for (auto &C : ModulePipelineParsingCallbacks)
+ if (C(Name, MPM, InnerPipeline))
+ return true;
return false;
}
*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); \
}
#include "PassRegistry.def"
+ for (auto &C : CGSCCPipelineParsingCallbacks)
+ if (C(Name, CGPM, InnerPipeline))
+ return true;
return false;
}
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); \
}
#include "PassRegistry.def"
+ for (auto &C : FunctionPipelineParsingCallbacks)
+ if (C(Name, FPM, InnerPipeline))
+ return true;
return false;
}
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); \
}
#include "PassRegistry.def"
+ for (auto &C : LoopPipelineParsingCallbacks)
+ if (C(Name, LPM, InnerPipeline))
+ return true;
return false;
}
}
#include "PassRegistry.def"
+ for (auto &C : AAParsingCallbacks)
+ if (C(Name, AA))
+ return true;
return false;
}
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,
// 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.
; 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.
; 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
; 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
; 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
; 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{{.*}}>
; 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
; 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
; 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
"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,
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.
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;
}
AsmParser
Core
Support
+ Passes
)
set(IRSources
DebugTypeODRUniquingTest.cpp
DominatorTreeTest.cpp
FunctionTest.cpp
+ PassBuilderCallbacksTest.cpp
IRBuilderTest.cpp
InstructionsTest.cpp
IntrinsicsTest.cpp
--- /dev/null
+//===- 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