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 ®istry = *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