[llvm] r262196 - [PM] Wire up optimization levels and default pipeline construction APIs

Chandler Carruth via llvm-commits llvm-commits at lists.llvm.org
Sun Feb 28 14:16:03 PST 2016


Author: chandlerc
Date: Sun Feb 28 16:16:03 2016
New Revision: 262196

URL: http://llvm.org/viewvc/llvm-project?rev=262196&view=rev
Log:
[PM] Wire up optimization levels and default pipeline construction APIs
in the PassBuilder.

These are really just stubs for now, but they give a nice API surface
that Clang or other tools can start learning about and enabling for
experimentation.

I've also wired up parsing various synthetic module pass names to
generate these set pipelines. This allows the pipelines to be combined
with other passes and have their order controlled, with clear separation
between the *kind* of canned pipeline, and the *level* of optimization
to be used within that canned pipeline.

The most interesting part of this patch is almost certainly the spec for
the different optimization levels. I don't think we can ever have hard
and fast rules that would make it easy to determine whether a particular
optimization makes sense at a particular level -- it will always be in
large part a judgement call. But hopefully this will outline the
expected rationale that should be used, and the direction that the
pipelines should be taken. Much of this was based on a long llvm-dev
discussion I started years ago to try and crystalize the intent behind
these pipelines, and now, at long long last I'm returning to the task of
actually writing it down somewhere that we can cite and try to be
consistent with.

Differential Revision: http://reviews.llvm.org/D12826

Modified:
    llvm/trunk/include/llvm/Passes/PassBuilder.h
    llvm/trunk/lib/Passes/PassBuilder.cpp
    llvm/trunk/test/Other/new-pass-manager.ll

Modified: llvm/trunk/include/llvm/Passes/PassBuilder.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Passes/PassBuilder.h?rev=262196&r1=262195&r2=262196&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Passes/PassBuilder.h (original)
+++ llvm/trunk/include/llvm/Passes/PassBuilder.h Sun Feb 28 16:16:03 2016
@@ -35,6 +35,92 @@ class PassBuilder {
   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 @@ public:
   /// 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 @@ public:
   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);

Modified: llvm/trunk/lib/Passes/PassBuilder.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Passes/PassBuilder.cpp?rev=262196&r1=262195&r2=262196&view=diff
==============================================================================
--- llvm/trunk/lib/Passes/PassBuilder.cpp (original)
+++ llvm/trunk/lib/Passes/PassBuilder.cpp Sun Feb 28 16:16:03 2016
@@ -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 @@ void PassBuilder::registerLoopAnalyses(L
 #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 @@ static bool isLoopPassName(StringRef Nam
   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<StringRef, 3> Matches;
+    if (!DefaultAliasRegex.match(Name, &Matches))
+      return false;
+    assert(Matches.size() == 3 && "Must capture two matched strings!");
+
+    auto L = StringSwitch<OptimizationLevel>(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 @@ bool PassBuilder::parseModulePassPipelin
     } 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());

Modified: llvm/trunk/test/Other/new-pass-manager.ll
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Other/new-pass-manager.ll?rev=262196&r1=262195&r2=262196&view=diff
==============================================================================
--- llvm/trunk/test/Other/new-pass-manager.ll (original)
+++ llvm/trunk/test/Other/new-pass-manager.ll Sun Feb 28 16:16:03 2016
@@ -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<O0>' %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O2
+; RUN: opt -disable-output -disable-verify -debug-pass-manager \
+; RUN:     -passes='default<O1>' %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O2
+; RUN: opt -disable-output -disable-verify -debug-pass-manager \
+; RUN:     -passes='default<O2>' %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O2
+; RUN: opt -disable-output -disable-verify -debug-pass-manager \
+; RUN:     -passes='default<Os>' %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O2
+; RUN: opt -disable-output -disable-verify -debug-pass-manager \
+; RUN:     -passes='default<Oz>' %s 2>&1 \
+; RUN:     | FileCheck %s --check-prefix=CHECK-O2
+; RUN: opt -disable-output -disable-verify -debug-pass-manager \
+; RUN:     -passes='lto-pre-link<O2>' %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<O2>' %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
 }




More information about the llvm-commits mailing list