xref: /llvm-project/mlir/lib/ExecutionEngine/OptUtils.cpp (revision d9cc3c31cc8a779b73f44e97d14b417ff068572c)
1 //===- OptUtils.cpp - MLIR Execution Engine optimization pass utilities ---===//
2 //
3 // Copyright 2019 The MLIR Authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //   http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 // =============================================================================
17 //
18 // This file implements the utility functions to trigger LLVM optimizations from
19 // MLIR Execution Engine.
20 //
21 //===----------------------------------------------------------------------===//
22 
23 #include "mlir/ExecutionEngine/OptUtils.h"
24 
25 #include "llvm/IR/LegacyPassManager.h"
26 #include "llvm/IR/LegacyPassNameParser.h"
27 #include "llvm/IR/Module.h"
28 #include "llvm/InitializePasses.h"
29 #include "llvm/Pass.h"
30 #include "llvm/Support/Allocator.h"
31 #include "llvm/Support/CommandLine.h"
32 #include "llvm/Support/Error.h"
33 #include "llvm/Support/StringSaver.h"
34 #include "llvm/Transforms/IPO.h"
35 #include "llvm/Transforms/IPO/PassManagerBuilder.h"
36 #include <climits>
37 #include <mutex>
38 
39 // A category for options that should be passed to -llvm-opts.
40 static llvm::cl::OptionCategory
41     optFlags("opt-like flags (pass to -llvm-ops=\"\")");
42 
43 // LLVM pass configuration CLI flag.
44 static llvm::cl::list<const llvm::PassInfo *, bool, llvm::PassNameParser>
45     llvmPasses(llvm::cl::desc("LLVM optimizing passes to run"),
46                llvm::cl::cat(optFlags));
47 
48 // CLI variables for -On options.
49 static llvm::cl::opt<bool> optO0("O0", llvm::cl::desc("Run opt O0 passes"),
50                                  llvm::cl::cat(optFlags));
51 static llvm::cl::opt<bool> optO1("O1", llvm::cl::desc("Run opt O1 passes"),
52                                  llvm::cl::cat(optFlags));
53 static llvm::cl::opt<bool> optO2("O2", llvm::cl::desc("Run opt O2 passes"),
54                                  llvm::cl::cat(optFlags));
55 static llvm::cl::opt<bool> optO3("O3", llvm::cl::desc("Run opt O3 passes"),
56                                  llvm::cl::cat(optFlags));
57 
58 // Run the module and function passes managed by the module manager.
59 static void runPasses(llvm::legacy::PassManager &modulePM,
60                       llvm::legacy::FunctionPassManager &funcPM,
61                       llvm::Module &m) {
62   funcPM.doInitialization();
63   for (auto &func : m) {
64     funcPM.run(func);
65   }
66   funcPM.doFinalization();
67   modulePM.run(m);
68 }
69 
70 // Initialize basic LLVM transformation passes under lock.
71 void mlir::initializeLLVMPasses() {
72   static std::mutex mutex;
73   std::lock_guard<std::mutex> lock(mutex);
74 
75   auto &registry = *llvm::PassRegistry::getPassRegistry();
76   llvm::initializeCore(registry);
77   llvm::initializeTransformUtils(registry);
78   llvm::initializeScalarOpts(registry);
79   llvm::initializeIPO(registry);
80   llvm::initializeInstCombine(registry);
81   llvm::initializeAggressiveInstCombine(registry);
82   llvm::initializeAnalysis(registry);
83   llvm::initializeVectorization(registry);
84 }
85 
86 // Populate pass managers according to the optimization and size levels.
87 // This behaves similarly to LLVM opt.
88 static void populatePassManagers(llvm::legacy::PassManager &modulePM,
89                                  llvm::legacy::FunctionPassManager &funcPM,
90                                  unsigned optLevel, unsigned sizeLevel) {
91   llvm::PassManagerBuilder builder;
92   builder.OptLevel = optLevel;
93   builder.SizeLevel = sizeLevel;
94   builder.Inliner = llvm::createFunctionInliningPass(
95       optLevel, sizeLevel, /*DisableInlineHotCallSite=*/false);
96   builder.LoopVectorize = optLevel > 1 && sizeLevel < 2;
97   builder.SLPVectorize = optLevel > 1 && sizeLevel < 2;
98   builder.DisableUnrollLoops = (optLevel == 0);
99 
100   builder.populateModulePassManager(modulePM);
101   builder.populateFunctionPassManager(funcPM);
102 }
103 
104 // Create and return a lambda that uses LLVM pass manager builder to set up
105 // optimizations based on the given level.
106 std::function<llvm::Error(llvm::Module *)>
107 mlir::makeOptimizingTransformer(unsigned optLevel, unsigned sizeLevel) {
108   return [optLevel, sizeLevel](llvm::Module *m) -> llvm::Error {
109 
110     llvm::legacy::PassManager modulePM;
111     llvm::legacy::FunctionPassManager funcPM(m);
112     populatePassManagers(modulePM, funcPM, optLevel, sizeLevel);
113     runPasses(modulePM, funcPM, *m);
114 
115     return llvm::Error::success();
116   };
117 }
118 
119 // Check if the opt flag is set and if it was located before `pos`.  If so,
120 // popuplate the module and the function pass managers with the passes
121 // corresponding to the `level` of optimization and reset the flag.
122 static void
123 populatePassManagersOptLevel(llvm::cl::opt<bool> &opt, unsigned level,
124                              unsigned pos, llvm::legacy::PassManager &modulePM,
125                              llvm::legacy::FunctionPassManager &funcPM) {
126   if (opt && opt.getPosition() < pos) {
127     opt = false;
128     populatePassManagers(modulePM, funcPM, level, /*sizeLevel=*/0);
129   }
130 }
131 
132 // Create and return a lambda that leverages LLVM PassInfo command line parser
133 // to construct passes given the command line flags that come from the given
134 // string rather than from the command line.
135 std::function<llvm::Error(llvm::Module *)>
136 mlir::makeLLVMPassesTransformer(std::string config) {
137   return [config](llvm::Module *m) -> llvm::Error {
138     // Storage for -On flags, the index in this array corresponds to the
139     // optimization level.  Do not add anything else.
140     llvm::SmallVector<std::reference_wrapper<llvm::cl::opt<bool>>, 4> optFlags{
141         optO0, optO1, optO2, optO3};
142 
143     llvm::BumpPtrAllocator allocator;
144     llvm::StringSaver saver(allocator);
145     llvm::SmallVector<const char *, 16> args;
146     args.push_back(""); // inject dummy program name
147     llvm::cl::TokenizeGNUCommandLine(config, saver, args);
148     llvm::cl::ParseCommandLineOptions(args.size(), args.data());
149 
150     llvm::legacy::PassManager modulePM;
151     llvm::legacy::FunctionPassManager funcPM(m);
152 
153     for (unsigned i = 0, e = llvmPasses.size(); i < e; ++i) {
154       const auto *passInfo = llvmPasses[i];
155       if (!passInfo->getNormalCtor())
156         continue;
157 
158       // If there is any of -On flags textually before this pass flag, populate
159       // the pass managers with the corresponding passes and reset the flag.
160       for (unsigned j = 0; j < 4; ++j)
161         populatePassManagersOptLevel(
162             optFlags[j].get(), j, llvmPasses.getPosition(i), modulePM, funcPM);
163 
164       auto *pass = passInfo->createPass();
165       if (!pass)
166         return llvm::make_error<llvm::StringError>(
167             "could not create pass " + passInfo->getPassName(),
168             llvm::inconvertibleErrorCode());
169 
170       modulePM.add(pass);
171     }
172     // Populate the pass managers with passes corresponding to the -On flags
173     // that have not been used yet.  Use UINT_MAX as the position index before
174     // which the -On flag should appear as an always-true marker.
175     for (unsigned j = 0; j < 4; ++j)
176       populatePassManagersOptLevel(optFlags[j].get(), j, /*pos=*/UINT_MAX,
177                                    modulePM, funcPM);
178 
179     // Run the -On function passes, then all the other passes.  Note that
180     // manually requested function passes were added to modulePM and will be
181     // executed in order with manual/-On module passes.  The function pass
182     // manager is only populated in reaction to -On flags with passes that are
183     // supposed to run "as soon as functions are created" according to the doc.
184     // This behavior is identical to that of LLVM's "opt" tool.
185     runPasses(modulePM, funcPM, *m);
186     return llvm::Error::success();
187   };
188 }
189