Index: llvm/trunk/include/llvm/Passes/PassBuilder.h =================================================================== --- llvm/trunk/include/llvm/Passes/PassBuilder.h +++ llvm/trunk/include/llvm/Passes/PassBuilder.h @@ -35,6 +35,92 @@ TargetMachine *TM; public: + /// \brief LLVM-provided high-level optimization levels. + /// + /// This enumerates the LLVM-provided high-level optimization levels. Each + /// level has a specific goal and rationale. + enum OptimizationLevel { + /// Disable as many optimizations as possible. This doesn't completely + /// disable the optimizer in many cases as there are correctness issues + /// such as always_inline functions. + O0, + + /// Optimize quickly without destroying debuggability. + /// + /// FIXME: The current and historical behavior of this level does *not* + /// agree with this goal, but we would like to move toward this goal in the + /// future. + /// + /// This level is tuned to produce a result from the optimizer as quickly + /// as possible and to avoid destroying debuggability. This tends to result + /// in a very good development mode where the compiled code will be + /// immediately executed as part of testing. As a consequence, where + /// possible, we would like to produce efficient-to-execute code, but not + /// if it significantly slows down compilation or would prevent even basic + /// debugging of the resulting binary. + /// + /// As an example, complex loop transformations such as versioning, + /// vectorization, or fusion might not make sense here due to the degree to + /// which the executed code would differ from the source code, and the + /// potential compile time cost. + O1, + + /// Optimize for fast execution as much as possible without triggering + /// significant incremental compile time or code size growth. + /// + /// The key idea is that optimizations at this level should "pay for + /// themselves". So if an optimization increases compile time by 5% or + /// increases code size by 5% for a particular benchmark, that benchmark + /// should also be one which sees a 5% runtime improvement. If the compile + /// time or code size penalties happen on average across a diverse range of + /// LLVM users' benchmarks, then the improvements should as well. + /// + /// And no matter what, the compile time needs to not grow superlinearly + /// with the size of input to LLVM so that users can control the runtime of + /// the optimizer in this mode. + /// + /// This is expected to be a good default optimization level for the vast + /// majority of users. + O2, + + /// Optimize for fast execution as much as possible. + /// + /// This mode is significantly more aggressive in trading off compile time + /// and code size to get execution time improvements. The core idea is that + /// this mode should include any optimization that helps execution time on + /// balance across a diverse collection of benchmarks, even if it increases + /// code size or compile time for some benchmarks without corresponding + /// improvements to execution time. + /// + /// Despite being willing to trade more compile time off to get improved + /// execution time, this mode still tries to avoid superlinear growth in + /// order to make even significantly slower compile times at least scale + /// reasonably. This does not preclude very substantial constant factor + /// costs though. + O3, + + /// Similar to \c O2 but tries to optimize for small code size instead of + /// fast execution without triggering significant incremental execution + /// time slowdowns. + /// + /// The logic here is exactly the same as \c O2, but with code size and + /// execution time metrics swapped. + /// + /// A consequence of the different core goal is that this should in general + /// produce substantially smaller executables that still run in + /// a reasonable amount of time. + Os, + + /// A very specialized mode that will optimize for code size at any and all + /// costs. + /// + /// This is useful primarily when there are absolute size limitations and + /// any effort taken to reduce the size is worth it regardless of the + /// execution time impact. You should expect this level to produce rather + /// slow, but very small, code. + Oz + }; + explicit PassBuilder(TargetMachine *TM = nullptr) : TM(TM) {} /// \brief Registers all available module analysis passes. @@ -68,6 +154,36 @@ /// additional analyses. void registerLoopAnalyses(LoopAnalysisManager &LAM); + /// \brief Add a per-module default optimization pipeline to a pass manager. + /// + /// This provides a good default optimization pipeline for per-module + /// optimization and code generation without any link-time optimization. It + /// typically correspond to frontend "-O[123]" options for optimization + /// levels \c O1, \c O2 and \c O3 resp. + void addPerModuleDefaultPipeline(ModulePassManager &MPM, + OptimizationLevel Level, + bool DebugLogging = false); + + /// \brief Add a pre-link, LTO-targeting default optimization pipeline to + /// a pass manager. + /// + /// This adds the pre-link optimizations tuned to work well with a later LTO + /// run. It works to minimize the IR which needs to be analyzed without + /// making irreversible decisions which could be made better during the LTO + /// run. + void addLTOPreLinkDefaultPipeline(ModulePassManager &MPM, + OptimizationLevel Level, + bool DebugLogging = false); + + /// \brief Add an LTO default optimization pipeline to a pass manager. + /// + /// This provides a good default optimization pipeline for link-time + /// optimization and code generation. It is particularly tuned to fit well + /// when IR coming into the LTO phase was first run through \c + /// addPreLinkLTODefaultPipeline, and the two coordinate closely. + void addLTODefaultPipeline(ModulePassManager &MPM, OptimizationLevel Level, + bool DebugLogging = false); + /// \brief Parse a textual pass pipeline description into a \c ModulePassManager. /// /// The format of the textual pass pipeline description looks something like: @@ -117,7 +233,8 @@ bool parseAAPipeline(AAManager &AA, StringRef PipelineText); private: - bool parseModulePassName(ModulePassManager &MPM, StringRef Name); + bool parseModulePassName(ModulePassManager &MPM, StringRef Name, + bool DebugLogging); bool parseCGSCCPassName(CGSCCPassManager &CGPM, StringRef Name); bool parseFunctionPassName(FunctionPassManager &FPM, StringRef Name); bool parseLoopPassName(LoopPassManager &LPM, StringRef Name); Index: llvm/trunk/lib/Passes/PassBuilder.cpp =================================================================== --- llvm/trunk/lib/Passes/PassBuilder.cpp +++ llvm/trunk/lib/Passes/PassBuilder.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Passes/PassBuilder.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/AliasAnalysisEvaluator.h" #include "llvm/Analysis/AssumptionCache.h" @@ -38,6 +39,7 @@ #include "llvm/IR/PassManager.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Regex.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Transforms/IPO/ForceFunctionAttrs.h" #include "llvm/Transforms/IPO/FunctionAttrs.h" @@ -53,6 +55,8 @@ using namespace llvm; +static Regex DefaultAliasRegex("^(default|lto-pre-link|lto)<(O[0123sz])>$"); + namespace { /// \brief No-op module pass which does nothing. @@ -135,8 +139,43 @@ #include "PassRegistry.def" } +void PassBuilder::addPerModuleDefaultPipeline(ModulePassManager &MPM, + OptimizationLevel Level, + bool DebugLogging) { + // FIXME: Finish fleshing this out to match the legacy pipelines. + FunctionPassManager EarlyFPM(DebugLogging); + EarlyFPM.addPass(SimplifyCFGPass()); + EarlyFPM.addPass(SROA()); + EarlyFPM.addPass(EarlyCSEPass()); + EarlyFPM.addPass(LowerExpectIntrinsicPass()); + + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(EarlyFPM))); +} + +void PassBuilder::addLTOPreLinkDefaultPipeline(ModulePassManager &MPM, + OptimizationLevel Level, + bool DebugLogging) { + // FIXME: We should use a customized pre-link pipeline! + addPerModuleDefaultPipeline(MPM, Level, DebugLogging); +} + +void PassBuilder::addLTODefaultPipeline(ModulePassManager &MPM, + OptimizationLevel Level, + bool DebugLogging) { + // FIXME: Finish fleshing this out to match the legacy LTO pipelines. + FunctionPassManager LateFPM(DebugLogging); + LateFPM.addPass(InstCombinePass()); + LateFPM.addPass(SimplifyCFGPass()); + + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(LateFPM))); +} + #ifndef NDEBUG static bool isModulePassName(StringRef Name) { + // Manually handle aliases for pre-configured pipeline fragments. + if (Name.startswith("default") || Name.startswith("lto")) + return DefaultAliasRegex.match(Name); + #define MODULE_PASS(NAME, CREATE_PASS) if (Name == NAME) return true; #define MODULE_ANALYSIS(NAME, CREATE_PASS) \ if (Name == "require<" NAME ">" || Name == "invalidate<" NAME ">") \ @@ -177,7 +216,34 @@ return false; } -bool PassBuilder::parseModulePassName(ModulePassManager &MPM, StringRef Name) { +bool PassBuilder::parseModulePassName(ModulePassManager &MPM, StringRef Name, + bool DebugLogging) { + // Manually handle aliases for pre-configured pipeline fragments. + if (Name.startswith("default") || Name.startswith("lto")) { + SmallVector Matches; + if (!DefaultAliasRegex.match(Name, &Matches)) + return false; + assert(Matches.size() == 3 && "Must capture two matched strings!"); + + auto L = StringSwitch(Matches[2]) + .Case("O0", O0) + .Case("O1", O1) + .Case("O2", O2) + .Case("O3", O3) + .Case("Os", Os) + .Case("Oz", Oz); + + if (Matches[1] == "default") { + addPerModuleDefaultPipeline(MPM, L, DebugLogging); + } else if (Matches[1] == "lto-pre-link") { + addLTOPreLinkDefaultPipeline(MPM, L, DebugLogging); + } else { + assert(Matches[1] == "lto" && "Not one of the matched options!"); + addLTODefaultPipeline(MPM, L, DebugLogging); + } + return true; + } + #define MODULE_PASS(NAME, CREATE_PASS) \ if (Name == NAME) { \ MPM.addPass(CREATE_PASS); \ @@ -475,7 +541,7 @@ } else { // Otherwise try to parse a pass name. size_t End = PipelineText.find_first_of(",)"); - if (!parseModulePassName(MPM, PipelineText.substr(0, End))) + if (!parseModulePassName(MPM, PipelineText.substr(0, End), DebugLogging)) return false; if (VerifyEachPass) MPM.addPass(VerifierPass()); Index: llvm/trunk/test/Other/new-pass-manager.ll =================================================================== --- llvm/trunk/test/Other/new-pass-manager.ll +++ llvm/trunk/test/Other/new-pass-manager.ll @@ -315,6 +315,37 @@ ; CHECK-AA: Running analysis: BasicAA ; CHECK-AA: Finished llvm::Module pass manager run +; RUN: opt -disable-output -disable-verify -debug-pass-manager \ +; RUN: -passes='default' %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O2 +; RUN: opt -disable-output -disable-verify -debug-pass-manager \ +; RUN: -passes='default' %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O2 +; RUN: opt -disable-output -disable-verify -debug-pass-manager \ +; RUN: -passes='default' %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O2 +; RUN: opt -disable-output -disable-verify -debug-pass-manager \ +; RUN: -passes='default' %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O2 +; RUN: opt -disable-output -disable-verify -debug-pass-manager \ +; RUN: -passes='default' %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O2 +; RUN: opt -disable-output -disable-verify -debug-pass-manager \ +; RUN: -passes='lto-pre-link' %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-O2 +; CHECK-O2: Starting llvm::Module pass manager run +; CHECK-O2: Running pass: SimplifyCFGPass +; CHECK-O2: Running pass: SROA +; CHECK-O2: Running pass: EarlyCSEPass +; CHECK-O2: Running pass: LowerExpectIntrinsicPass + +; RUN: opt -disable-output -disable-verify -debug-pass-manager \ +; RUN: -passes='lto' %s 2>&1 \ +; RUN: | FileCheck %s --check-prefix=CHECK-LTO-O2 +; CHECK-LTO-O2: Starting llvm::Module pass manager run +; CHECK-LTO-O2: Running pass: InstCombinePass +; CHECK-LTO-O2: Running pass: SimplifyCFGPass + define void @foo() { ret void }