Index: include/llvm/Passes/PassBuilder.h =================================================================== --- include/llvm/Passes/PassBuilder.h +++ include/llvm/Passes/PassBuilder.h @@ -46,6 +46,19 @@ Optional 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 InnerPipeline; + }; + /// \brief LLVM-provided high-level optimization levels. /// /// This enumerates the LLVM-provided high-level optimization levels. Each @@ -302,7 +315,8 @@ /// registered. AAManager buildDefaultAAPipeline(); - /// \brief Parse a textual pass pipeline description into a \c ModulePassManager. + /// {{@ + /// \brief Parse a textual pass pipeline description into a PassManager. /// /// The format of the textual pass pipeline description looks something like: /// @@ -310,28 +324,37 @@ /// /// Pass managers have ()s describing the nest structure of passes. All passes /// 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 + /// a pass matching the pass manager, 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 + /// So, assuming function passes 'fpassN', CGSCC passes 'cgpassN', and loop + /// passes /// 'lpassN', all of these are valid: /// /// fpass1,fpass2,fpass3 /// cgpass1,cgpass2,cgpass3 /// lpass1,lpass2,lpass3 /// - /// And they are equivalent to the following (resp.): + /// And when parsing into a ModulePassMangager they are equivalent to the + /// following (resp.): /// /// module(function(fpass1,fpass2,fpass3)) /// module(cgscc(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); + 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. /// @@ -350,13 +373,169 @@ /// returns false. bool parseAAPipeline(AAManager &AA, StringRef PipelineText); -private: - /// A struct to capture parsed pass pipeline names. - struct PipelineElement { - StringRef Name; - std::vector InnerPipeline; - }; + /// \brief Register a callback for a default optimizer pipeline extension + /// point + /// + /// This extension point allows adding passes just before the main + /// module-level optimization passes. + void registerModuleOptimizerEarlyEPCallback( + const std::function &C) { + ModuleOptimizerEarlyEPCallbacks.push_back(C); + } + + /// \brief Register a callback for a default optimizer pipeline extension + /// point + /// + /// This extension point allows adding passes before any other + /// transformations, allowing them to see the code as it is coming out of the + /// frontend. + void registerEarlyAsPossibleEPCallback( + const std::function &C) { + EarlyAsPossibleEPCallbacks.push_back(C); + } + + /// \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 &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 &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 &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 &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 &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 &C) { + VectorizerStartEPCallbacks.push_back(C); + } + + /// \brief Register a callback for a default optimizer pipeline extension + /// point + /// + /// This extension point allows adding passes that run after everything else. + void registerOptimizerLastEPCallback( + const std::function &C) { + OptimizerLastEPCallbacks.push_back(C); + } + + /// \brief Register a callback for parsing an AliasAnalysis Name to populate + /// the given AAManager \p AA + void registerParseAACallback( + const std::function &C) { + AAParsingCallbacks.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 VerifyEachPass, bool DebugLogging)> &C) { + TopLevelPipelineParsingCallbacks.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 &C) { + CGSCCAnalysisRegistrationCallbacks.push_back(C); + } + void registerAnalysisRegistrationCallback( + const std::function &C) { + FunctionAnalysisRegistrationCallbacks.push_back(C); + } + void registerAnalysisRegistrationCallback( + const std::function &C) { + LoopAnalysisRegistrationCallbacks.push_back(C); + } + void registerAnalysisRegistrationCallback( + const std::function &C) { + ModuleAnalysisRegistrationCallbacks.push_back(C); + } + /// @}} + + /// {{@ Register pipeline parsing callbacks with this pass builder instance. + /// Using these callbacks, calles can parse both a single pass name, as well + /// as entire sub-pipelines, and populate the PassManager instance + /// accordingly. + void registerPipelineParsingCallback( + const std::function)> &C) { + CGSCCPipelineParsingCallbacks.push_back(C); + } + void registerPipelineParsingCallback( + const std::function)> &C) { + FunctionPipelineParsingCallbacks.push_back(C); + } + void registerPipelineParsingCallback( + const std::function)> &C) { + LoopPipelineParsingCallbacks.push_back(C); + } + void registerPipelineParsingCallback( + const std::function)> &C) { + ModulePipelineParsingCallbacks.push_back(C); + } + /// @}} +private: static Optional> parsePipelineText(StringRef Text); @@ -382,7 +561,112 @@ bool parseModulePassPipeline(ModulePassManager &MPM, ArrayRef 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); + + // EP callbacks + SmallVector, 2> + ModuleOptimizerEarlyEPCallbacks; + SmallVector, 2> + EarlyAsPossibleEPCallbacks; + SmallVector, 2> + PeepholeEPCallbacks; + SmallVector, 2> + LateLoopOptimizationsEPCallbacks; + SmallVector, 2> + LoopOptimizerEndEPCallbacks; + SmallVector, 2> + ScalarOptimizerLateEPCallbacks; + SmallVector, 2> + CGSCCOptimizerLateEPCallbacks; + SmallVector, 2> + VectorizerStartEPCallbacks; + SmallVector, 2> + OptimizerLastEPCallbacks; + // Module callbacks + SmallVector, 2> + ModuleAnalysisRegistrationCallbacks; + SmallVector)>, + 2> + ModulePipelineParsingCallbacks; + SmallVector, + bool VerifyEachPass, bool DebugLogging)>, + 2> + TopLevelPipelineParsingCallbacks; + // CGSCC callbacks + SmallVector, 2> + CGSCCAnalysisRegistrationCallbacks; + SmallVector)>, + 2> + CGSCCPipelineParsingCallbacks; + // Function callbacks + SmallVector, 2> + FunctionAnalysisRegistrationCallbacks; + SmallVector)>, + 2> + FunctionPipelineParsingCallbacks; + // Loop callbacks + SmallVector, 2> + LoopAnalysisRegistrationCallbacks; + SmallVector)>, + 2> + LoopPipelineParsingCallbacks; + // AA callbacks + SmallVector, 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 P) { +/// if (parseAnalysisUtilityPasses("fancy-analysis", Name, +/// FPM)) +/// return true; +/// return false; +/// } +template +bool parseAnalysisUtilityPasses( + StringRef AnalysisName, StringRef PipelineName, + PassManager &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()); + 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()); + return true; + } + + return false; +} } #endif Index: lib/Passes/PassBuilder.cpp =================================================================== --- lib/Passes/PassBuilder.cpp +++ lib/Passes/PassBuilder.cpp @@ -281,28 +281,46 @@ } // 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 @@ -340,6 +358,8 @@ if (!isOptimizingForSize(Level)) FPM.addPass(LibCallsShrinkWrapPass()); + invokePeepholeEPCallbacks(FPM, Level); + FPM.addPass(TailCallElimPass()); FPM.addPass(SimplifyCFGPass()); @@ -363,12 +383,19 @@ LPM1.addPass(SimpleLoopUnswitchPass()); LPM2.addPass(IndVarSimplifyPass()); LPM2.addPass(LoopIdiomRecognizePass()); + + for (auto &C : LateLoopOptimizationsEPCallbacks) + C(LPM2, Level); + LPM2.addPass(LoopDeletionPass()); // FIXME: The old pass manager has a hack to disable loop unrolling during // ThinLTO when using sample PGO. Need to either fix it or port some // workaround. 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()); @@ -404,6 +431,8 @@ // 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(JumpThreadingPass()); @@ -411,19 +440,25 @@ 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 @@ -449,8 +484,8 @@ FPM.addPass(SimplifyCFGPass()); // Merge & remove basic blocks. FPM.addPass(InstCombinePass()); // Combine silly sequences. - // FIXME: Here the old pass manager inserts peephole extensions. - // Add them when they're supported. + invokePeepholeEPCallbacks(FPM, Level); + CGPipeline.addPass(createCGSCCToFunctionPassAdaptor(std::move(FPM))); MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPipeline))); @@ -484,9 +519,16 @@ // libraries and other oracles. MPM.addPass(InferFunctionAttrsPass()); + for (auto &C : ModuleOptimizerEarlyEPCallbacks) + C(MPM, Level); + // Create an early function pass manager to cleanup the output of the // frontend. FunctionPassManager EarlyFPM(DebugLogging); + + for (auto &C : EarlyAsPossibleEPCallbacks) + C(EarlyFPM, Level); + EarlyFPM.addPass(SimplifyCFGPass()); EarlyFPM.addPass(SROA()); EarlyFPM.addPass(EarlyCSEPass()); @@ -517,6 +559,9 @@ // optimizations. FunctionPassManager GlobalCleanupPM(DebugLogging); GlobalCleanupPM.addPass(InstCombinePass()); + + invokePeepholeEPCallbacks(GlobalCleanupPM, Level); + GlobalCleanupPM.addPass(SimplifyCFGPass()); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(GlobalCleanupPM))); @@ -569,6 +614,9 @@ MainCGPipeline.addPass(createCGSCCToFunctionPassAdaptor( buildFunctionSimplificationPipeline(Level, DebugLogging))); + 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 @@ -627,6 +675,9 @@ // 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())); @@ -696,6 +747,9 @@ MPM.addPass(GlobalDCEPass()); MPM.addPass(ConstantMergePass()); + for (auto &C : OptimizerLastEPCallbacks) + C(MPM, Level); + return MPM; } @@ -855,8 +909,12 @@ // 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 @@ -873,10 +931,11 @@ 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 @@ -926,8 +985,9 @@ // FIXME: Conditionally run LoadCombine here, after it's ported // (in case we still have this pass, given its questionable usefulness). - // FIXME: add peephole extensions to the PM here. MainFPM.addPass(InstCombinePass()); + + invokePeepholeEPCallbacks(MainFPM, Level); MainFPM.addPass(JumpThreadingPass()); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(MainFPM))); @@ -1010,7 +1070,27 @@ 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 +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 +static bool isModulePassName(StringRef Name, CallbacksT &Callbacks) { // Manually handle aliases for pre-configured pipeline fragments. if (startsWithDefaultPipelineAliasPrefix(Name)) return DefaultAliasRegex.match(Name); @@ -1035,10 +1115,11 @@ return true; #include "PassRegistry.def" - return false; + return callbacksAcceptPassName(Name, Callbacks); } -static bool isCGSCCPassName(StringRef Name) { +template +static bool isCGSCCPassName(StringRef Name, CallbacksT &Callbacks) { // Explicitly handle pass manager names. if (Name == "cgscc") return true; @@ -1059,10 +1140,11 @@ return true; #include "PassRegistry.def" - return false; + return callbacksAcceptPassName(Name, Callbacks); } -static bool isFunctionPassName(StringRef Name) { +template +static bool isFunctionPassName(StringRef Name, CallbacksT &Callbacks) { // Explicitly handle pass manager names. if (Name == "function") return true; @@ -1081,10 +1163,11 @@ return true; #include "PassRegistry.def" - return false; + return callbacksAcceptPassName(Name, Callbacks); } -static bool isLoopPassName(StringRef Name) { +template +static bool isLoopPassName(StringRef Name, CallbacksT &Callbacks) { // Explicitly handle pass manager names. if (Name == "loop") return true; @@ -1101,7 +1184,7 @@ return true; #include "PassRegistry.def" - return false; + return callbacksAcceptPassName(Name, Callbacks); } Optional> @@ -1202,6 +1285,11 @@ 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; } @@ -1214,15 +1302,20 @@ assert(Matches.size() == 3 && "Must capture two matched strings!"); OptimizationLevel L = StringSwitch(Matches[2]) - .Case("O0", O0) - .Case("O1", O1) - .Case("O2", O2) - .Case("O3", O3) - .Case("Os", Os) - .Case("Oz", Oz); - if (L == O0) + .Case("O0", O0) + .Case("O1", O1) + .Case("O2", O2) + .Case("O3", O3) + .Case("Os", Os) + .Case("Oz", Oz); + if (L == O0) { + + for (auto &C : OptimizerLastEPCallbacks) + C(MPM, L); + // At O0 we do nothing at all! return true; + } if (Matches[1] == "default") { MPM.addPass(buildPerModuleDefaultPipeline(L, DebugLogging)); @@ -1259,6 +1352,9 @@ } #include "PassRegistry.def" + for (auto &C : ModulePipelineParsingCallbacks) + if (C(Name, MPM, InnerPipeline)) + return true; return false; } @@ -1306,11 +1402,16 @@ *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); \ @@ -1331,6 +1432,9 @@ } #include "PassRegistry.def" + for (auto &C : CGSCCPipelineParsingCallbacks) + if (C(Name, CGPM, InnerPipeline)) + return true; return false; } @@ -1368,11 +1472,16 @@ 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); \ @@ -1392,6 +1501,9 @@ } #include "PassRegistry.def" + for (auto &C : FunctionPipelineParsingCallbacks) + if (C(Name, FPM, InnerPipeline)) + return true; return false; } @@ -1419,11 +1531,16 @@ 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); \ @@ -1444,10 +1561,17 @@ } #include "PassRegistry.def" + for (auto &C : LoopPipelineParsingCallbacks) + if (C(Name, LPM, InnerPipeline)) + return true; return false; } bool PassBuilder::parseAAPassName(AAManager &AA, StringRef Name) { + for (auto &C : AAParsingCallbacks) + if (C(Name, AA)) + return true; + #define MODULE_ALIAS_ANALYSIS(NAME, CREATE_PASS) \ if (Name == NAME) { \ AA.registerModuleAnalysis< \ @@ -1528,7 +1652,7 @@ 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, @@ -1542,21 +1666,83 @@ // 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)) { + if (isFunctionPassName(FirstName, FunctionPipelineParsingCallbacks)) { + Pipeline = {{"function", std::move(*Pipeline)}}; + } else if (isLoopPassName(FirstName, LoopPipelineParsingCallbacks)) { + Pipeline = {{"function", {{"loop", std::move(*Pipeline)}}}}; + } else { + 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)) { + if (isLoopPassName(FirstName, LoopPipelineParsingCallbacks)) { + Pipeline = {{"loop", std::move(*Pipeline)}}; + } else { + 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. Index: test/Other/new-pm-defaults.ll =================================================================== --- test/Other/new-pm-defaults.ll +++ test/Other/new-pm-defaults.ll @@ -26,6 +26,52 @@ ; RUN: -passes='lto-pre-link' -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-module-optimizer-early='no-op-module' \ +; RUN: -passes='default' -S %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \ +; RUN: --check-prefix=CHECK-EP-ModuleEarly +; RUN: opt -disable-verify -debug-pass-manager \ +; RUN: -passes-ep-early-as-possible='no-op-function' \ +; RUN: -passes='default' -S %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \ +; RUN: --check-prefix=CHECK-EP-FunctionEarly +; RUN: opt -disable-verify -debug-pass-manager \ +; RUN: -passes-ep-peephole='no-op-function' \ +; RUN: -passes='default' -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' -S %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \ +; RUN: --check-prefix=CHECK-EP-LoopLate +; RUN: opt -disable-verify -debug-pass-manager \ +; RUN: -passes-ep-loop-optimizer-end='no-op-loop' \ +; RUN: -passes='default' -S %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \ +; RUN: --check-prefix=CHECK-EP-LoopEnd +; RUN: opt -disable-verify -debug-pass-manager \ +; RUN: -passes-ep-scalar-optimizer-late='no-op-function' \ +; RUN: -passes='default' -S %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \ +; RUN: --check-prefix=CHECK-EP-ScalarLate +; RUN: opt -disable-verify -debug-pass-manager \ +; RUN: -passes-ep-cgscc-optimizer-late='no-op-cgscc' \ +; RUN: -passes='default' -S %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \ +; RUN: --check-prefix=CHECK-EP-CGSCCLate +; RUN: opt -disable-verify -debug-pass-manager \ +; RUN: -passes-ep-vectorizer-start='no-op-function' \ +; RUN: -passes='default' -S %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \ +; RUN: --check-prefix=CHECK-EP-VectorizerStart +; RUN: opt -disable-verify -debug-pass-manager \ +; RUN: -passes-ep-optimizer-last='no-op-module' \ +; RUN: -passes='default' -S %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O --check-prefix=CHECK-O3 \ +; RUN: --check-prefix=CHECK-EP-ModuleLast + ; CHECK-O: Starting llvm::Module pass manager run. ; CHECK-O-NEXT: Running pass: PassManager<{{.*}}Module{{.*}}> ; CHECK-O-NEXT: Starting llvm::Module pass manager run. @@ -34,9 +80,11 @@ ; CHECK-O-NEXT: Starting llvm::Module pass manager run. ; CHECK-O-NEXT: Running pass: InferFunctionAttrsPass ; CHECK-O-NEXT: Running analysis: TargetLibraryAnalysis +; CHECK-EP-ModuleEarly-NEXT: Running pass: NoOpModulePass ; CHECK-O-NEXT: Running pass: ModuleToFunctionPassAdaptor<{{.*}}PassManager{{.*}}> ; CHECK-O-NEXT: Running analysis: InnerAnalysisManagerProxy ; CHECK-O-NEXT: Starting llvm::Function pass manager run. +; CHECK-EP-FunctionEarly-NEXT: Running pass: NoOpFunctionPass ; CHECK-O-NEXT: Running pass: SimplifyCFGPass ; CHECK-O-NEXT: Running analysis: TargetIRAnalysis ; CHECK-O-NEXT: Running analysis: AssumptionAnalysis @@ -53,6 +101,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 @@ -83,6 +132,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 @@ -104,8 +154,10 @@ ; CHECK-O-NEXT: Starting Loop pass manager run. ; CHECK-O-NEXT: Running pass: IndVarSimplifyPass ; CHECK-O-NEXT: Running pass: LoopIdiomRecognizePass +; CHECK-EP-LoopLate-NEXT: Running pass: NoOpLoopPass ; CHECK-O-NEXT: Running pass: LoopDeletionPass ; CHECK-O-NEXT: Running pass: LoopUnrollPass +; CHECK-EP-LoopEnd-NEXT: Running pass: NoOpLoopPass ; CHECK-O-NEXT: Finished Loop pass manager run. ; CHECK-Os-NEXT: Running pass: MergedLoadStoreMotionPass ; CHECK-Os-NEXT: Running pass: GVN @@ -125,15 +177,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-ScalarLate-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-CGSCCLate-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{{.*}}> @@ -145,6 +201,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-VectorizerStart-NEXT: Running pass: NoOpFunctionPass ; CHECK-O-NEXT: Running pass: FunctionToLoopPassAdaptor<{{.*}}LoopRotatePass ; CHECK-O-NEXT: Running pass: LoopDistributePass ; CHECK-O-NEXT: Running pass: LoopVectorizePass @@ -167,6 +224,7 @@ ; CHECK-O-NEXT: Finished llvm::Function pass manager run. ; CHECK-O-NEXT: Running pass: GlobalDCEPass ; CHECK-O-NEXT: Running pass: ConstantMergePass +; CHECK-EP-ModuleLast-NEXT: Running pass: NoOpModulePass ; CHECK-O-NEXT: Finished llvm::Module pass manager run. ; CHECK-O-NEXT: Finished llvm::Module pass manager run. ; CHECK-O-NEXT: Running pass: PrintModulePass Index: test/Other/new-pm-lto-defaults.ll =================================================================== --- test/Other/new-pm-lto-defaults.ll +++ test/Other/new-pm-lto-defaults.ll @@ -18,6 +18,11 @@ ; RUN: -passes='lto' -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' -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-O-NEXT: Starting llvm::Module pass manager run. @@ -45,13 +50,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 Index: tools/opt/NewPMDriver.cpp =================================================================== --- tools/opt/NewPMDriver.cpp +++ tools/opt/NewPMDriver.cpp @@ -48,6 +48,122 @@ "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 ModuleOptimizerEarlyEPPipeline( + "passes-ep-module-optimizer-early", + cl::desc("A textual description of the module pass pipeline inserted at " + "the ModuleOptimizerEarly extension point into default pipelines"), + cl::Hidden); +static cl::opt EarlyAsPossibleEPPipeline( + "passes-ep-early-as-possible", + cl::desc("A textual description of the function pass pipeline inserted at " + "the EarlyAsPossible extension point into default pipelines"), + cl::Hidden); +static cl::opt 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 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 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 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 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 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); +static cl::opt OptimizerLastEPPipeline( + "passes-ep-optimizer-last", + cl::desc("A textual description of the module pass pipeline inserted at " + "the ModuleOptimizerLast extension point into default pipelines"), + cl::Hidden); +/// @}} + +#ifdef LINK_POLLY_INTO_TOOLS +namespace polly { +void RegisterPollyPasses(PassBuilder &); +} +#endif + +/// 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 (!ModuleOptimizerEarlyEPPipeline.empty()) + PB.registerModuleOptimizerEarlyEPCallback( + [&](ModulePassManager &PM, PassBuilder::OptimizationLevel Level) { + return PB.parsePassPipeline(PM, ModuleOptimizerEarlyEPPipeline, + VerifyEachPass, DebugPM); + }); + if (!EarlyAsPossibleEPPipeline.empty()) + PB.registerEarlyAsPossibleEPCallback( + [&](FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { + return PB.parsePassPipeline(PM, EarlyAsPossibleEPPipeline, + VerifyEachPass, DebugPM); + }); + 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); + }); + if (!OptimizerLastEPPipeline.empty()) + PB.registerOptimizerLastEPCallback( + [&](ModulePassManager &PM, PassBuilder::OptimizationLevel Level) { + return PB.parsePassPipeline(PM, OptimizerLastEPPipeline, + VerifyEachPass, DebugPM); + }); +} + bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, tool_output_file *Out, tool_output_file *ThinLTOLinkOut, @@ -56,7 +172,13 @@ bool ShouldPreserveAssemblyUseListOrder, bool ShouldPreserveBitcodeUseListOrder, bool EmitSummaryIndex, bool EmitModuleHash) { + bool VerifyEachPass = VK == VK_VerifyEachPass; PassBuilder PB(TM); + registerEPCallbacks(PB, VerifyEachPass, DebugPM); + +#ifdef LINK_POLLY_INTO_TOOLS + polly::RegisterPollyPasses(PB); +#endif // Specially handle the alias analysis manager so that we can register // a custom pipeline of AA passes with it. @@ -85,8 +207,7 @@ 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; } Index: unittests/IR/CMakeLists.txt =================================================================== --- unittests/IR/CMakeLists.txt +++ unittests/IR/CMakeLists.txt @@ -3,6 +3,7 @@ AsmParser Core Support + Passes ) set(IRSources @@ -15,6 +16,7 @@ DebugTypeODRUniquingTest.cpp DominatorTreeTest.cpp FunctionTest.cpp + HookManagerTest.cpp IRBuilderTest.cpp InstructionsTest.cpp IntrinsicsTest.cpp Index: unittests/IR/HookManagerTest.cpp =================================================================== --- /dev/null +++ unittests/IR/HookManagerTest.cpp @@ -0,0 +1,250 @@ +//===- llvm/unittest/IR/HookManagerTest.cpp - HookManager unit tests ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; + +namespace { +/// A test analysis, counting its runs and invalidations +template +struct TestAnalysis + : public AnalysisInfoMixin> { + struct Result { + int *AnalysesInvalidated; + bool invalidate(IRUnitT &IR, const PreservedAnalyses &PA, + typename AnalysisManagerT::Invalidator &) { + auto PAC = PA.getChecker>(); + if (PAC.preserved() || + PAC.template preservedSet>()) + return false; + ++*AnalysesInvalidated; + return true; + } + }; + + TestAnalysis(int *AnalysesRun, int *AnalysesInvalidated) + : AnalysesRun(AnalysesRun), AnalysesInvalidated(AnalysesInvalidated) {} + + template + Result run(IRUnitT &M, AnalysisManagerT &, ExtraTs &&... ExtraArgs) { + ++*AnalysesRun; + return Result{AnalysesInvalidated}; + } + + static AnalysisKey Key; + int *AnalysesRun, *AnalysesInvalidated; +}; + +/// A test pass, counting its runs +template +struct TestPass : public PassInfoMixin> { + TestPass(int *PassesRun) : PassesRun(PassesRun) {} + + template + PreservedAnalyses run(IRUnitT &M, AnalysisManagerT &AM, ResultT &&R, + ExtraTs &&... ExtraArgs) { + AM.template getResult>(M, R); + ++*PassesRun; + PreservedAnalyses P; + P.preserve>(); + return P; + } + + template + PreservedAnalyses run(IRUnitT &M, AnalysisManagerT &AM) { + AM.template getResult>(M); + ++*PassesRun; + PreservedAnalyses P; + P.preserve>(); + return P; + } + + int *PassesRun; +}; + +template +AnalysisKey TestAnalysis::Key; + +/// Wrapper class for the registration and parsing callbacks +template +struct Callback { + /// Handle analysis registration + void operator()(AnalysisManagerT &AM) { + int *AnaRun = AnalysesRun; + int *AnaInv = AnalysesInvalidated; + AM.registerPass([AnaRun, AnaInv] { + return TestAnalysis(AnaRun, AnaInv); + }); + } + + /// Handle pass name parsing + bool operator()(StringRef Name, PassManagerT &PM, + ArrayRef InnerPipeline) { + if (parseAnalysisUtilityPasses>( + "test-analysis", Name, PM)) + return true; + + if (Name == "test-transform") { + PM.addPass(TestPass(PassesRun)); + return true; + } + return false; + } + + Callback(int *AnalysesRun, int *PassesRun, int *AnalysesInvalidated) + : AnalysesRun(AnalysesRun), PassesRun(PassesRun), + AnalysesInvalidated(AnalysesInvalidated) {} + int *AnalysesRun, *PassesRun, *AnalysesInvalidated; +}; + +static std::unique_ptr parseIR(LLVMContext &C, const char *IR) { + SMDiagnostic Err; + return parseAssemblyString(IR, Err, C); +} + +template class HookManagerTest {}; + +/// Test fixture +/// Template specialization to extract the IRUnit and AM types from a +/// PassManagerT +template +class HookManagerTest> + : public testing::Test { +protected: + using IRUnitT = _IRUnitT; + using PassManagerT = PassManager<_IRUnitT, _AnalysisManagerT, ExtraArgTs...>; + using AnalysisManagerT = _AnalysisManagerT; + LLVMContext Context; + std::unique_ptr M; + + PassBuilder PB; + ModulePassManager PM; + LoopAnalysisManager LAM; + FunctionAnalysisManager FAM; + CGSCCAnalysisManager CGAM; + ModuleAnalysisManager AM; + + HookManagerTest() + : M(parseIR(Context, + "declare void @bar() local_unnamed_addr\n" + "define void @foo(i32 %n) local_unnamed_addr {\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) {} + + void run() { PM.run(*M, AM); } + + /// This method should only be called once to avoid duplicate analysis + /// registrations + void registerCallbacks(int &AnalysesRun, int &PassesRun, + int &AnalysesInvalidated) { + PB.registerAnalysisRegistrationCallback( + Callback( + &AnalysesRun, &PassesRun, &AnalysesInvalidated)); + PB.registerPipelineParsingCallback( + Callback( + &AnalysesRun, &PassesRun, &AnalysesInvalidated)); + PB.registerModuleAnalyses(AM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, AM); + } +}; + +using PassManagers = ::testing::Types; + +TYPED_TEST_CASE(HookManagerTest, PassManagers); + +/// Test pass name parsing for all IRUnits, count pass and analysis runs +TYPED_TEST(HookManagerTest, Passes) { + int AnalysesRun = 0, PassesRun = 0, AnalysesInvalidated = 0; + this->registerCallbacks(AnalysesRun, PassesRun, AnalysesInvalidated); + + ASSERT_TRUE(this->PB.parsePassPipeline(this->PM, "test-transform", true)); + this->run(); + ASSERT_EQ(1, AnalysesRun); + ASSERT_EQ(1, PassesRun); + ASSERT_EQ(0, AnalysesInvalidated); +} + +/// Test analysis registration and parsing of the require<> and invalidate<> +/// utilities for all IRUnits, count analysis runs and invalidations +TYPED_TEST(HookManagerTest, Analyses) { + int AnalysesRun = 0, PassesRun = 0, AnalysesInvalidated = 0; + this->registerCallbacks(AnalysesRun, PassesRun, AnalysesInvalidated); + + ASSERT_TRUE(this->PB.parsePassPipeline( + this->PM, "require,invalidate", true)); + this->run(); + ASSERT_EQ(1, AnalysesRun); + ASSERT_EQ(0, PassesRun); + ASSERT_EQ(1, AnalysesInvalidated); +} + +/// Test fixture for testing the parsing of the top level pipeline. This is only +/// relevant for the ModulePassManager +class TopLevelPipelineParsingTest : public HookManagerTest { +}; + +TEST_F(TopLevelPipelineParsingTest, ParsePipeline) { + int AnalysesRun = 0, PassesRun = 0, AnalysesInvalidated = 0; + Callback CB{ + &AnalysesRun, &PassesRun, &AnalysesInvalidated}; + + /// This parses a pipeline named 'another-pipeline', whose only element may be + /// the test-transform pass defined above + this->PB.registerParseTopLevelPipelineCallback([&]( + ModulePassManager &MPM, ArrayRef Pipeline, + bool VerifyEachPass, bool DebugLogging) { + auto &FirstName = Pipeline.front().Name; + auto &InnerPipeline = Pipeline.front().InnerPipeline; + if (FirstName == "another-pipeline") { + for (auto &E : InnerPipeline) { + if (!CB(E.Name, MPM, E.InnerPipeline)) + return false; + return true; + } + } + return false; + }); + + this->registerCallbacks(AnalysesRun, PassesRun, AnalysesInvalidated); + + ASSERT_TRUE(this->PB.parsePassPipeline( + this->PM, "another-pipeline(test-transform)", true)); + this->run(); + ASSERT_EQ(1, AnalysesRun); + ASSERT_EQ(1, PassesRun); + ASSERT_EQ(0, AnalysesInvalidated); + + /// Test whether the callback is actually used + ASSERT_FALSE(this->PB.parsePassPipeline( + this->PM, "another-pipeline(instcombine)", true)); +} +} // end anonymous namespace