//===- OptUtils.cpp - MLIR Execution Engine optimization pass utilities ---===// // // Copyright 2019 The MLIR Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ============================================================================= // // This file implements the utility functions to trigger LLVM optimizations from // MLIR Execution Engine. // //===----------------------------------------------------------------------===// #include "mlir/ExecutionEngine/OptUtils.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/LegacyPassNameParser.h" #include "llvm/IR/Module.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/StringSaver.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/PassManagerBuilder.h" #include #include // A category for options that should be passed to -llvm-opts. static llvm::cl::OptionCategory optFlags("opt-like flags (pass to -llvm-ops=\"\")"); // LLVM pass configuration CLI flag. static llvm::cl::list llvmPasses(llvm::cl::desc("LLVM optimizing passes to run"), llvm::cl::cat(optFlags)); // CLI variables for -On options. static llvm::cl::opt optO0("O0", llvm::cl::desc("Run opt O0 passes"), llvm::cl::cat(optFlags)); static llvm::cl::opt optO1("O1", llvm::cl::desc("Run opt O1 passes"), llvm::cl::cat(optFlags)); static llvm::cl::opt optO2("O2", llvm::cl::desc("Run opt O2 passes"), llvm::cl::cat(optFlags)); static llvm::cl::opt optO3("O3", llvm::cl::desc("Run opt O3 passes"), llvm::cl::cat(optFlags)); // Run the module and function passes managed by the module manager. static void runPasses(llvm::legacy::PassManager &modulePM, llvm::legacy::FunctionPassManager &funcPM, llvm::Module &m) { funcPM.doInitialization(); for (auto &func : m) { funcPM.run(func); } funcPM.doFinalization(); modulePM.run(m); } // Initialize basic LLVM transformation passes under lock. void mlir::initializeLLVMPasses() { static std::mutex mutex; std::lock_guard lock(mutex); auto ®istry = *llvm::PassRegistry::getPassRegistry(); llvm::initializeCore(registry); llvm::initializeTransformUtils(registry); llvm::initializeScalarOpts(registry); llvm::initializeIPO(registry); llvm::initializeInstCombine(registry); llvm::initializeAggressiveInstCombine(registry); llvm::initializeAnalysis(registry); llvm::initializeVectorization(registry); } // Populate pass managers according to the optimization and size levels. // This behaves similarly to LLVM opt. static void populatePassManagers(llvm::legacy::PassManager &modulePM, llvm::legacy::FunctionPassManager &funcPM, unsigned optLevel, unsigned sizeLevel) { llvm::PassManagerBuilder builder; builder.OptLevel = optLevel; builder.SizeLevel = sizeLevel; builder.Inliner = llvm::createFunctionInliningPass( optLevel, sizeLevel, /*DisableInlineHotCallSite=*/false); builder.LoopVectorize = optLevel > 1 && sizeLevel < 2; builder.SLPVectorize = optLevel > 1 && sizeLevel < 2; builder.DisableUnrollLoops = (optLevel == 0); builder.populateModulePassManager(modulePM); builder.populateFunctionPassManager(funcPM); } // Create and return a lambda that uses LLVM pass manager builder to set up // optimizations based on the given level. std::function mlir::makeOptimizingTransformer(unsigned optLevel, unsigned sizeLevel) { return [optLevel, sizeLevel](llvm::Module *m) -> llvm::Error { llvm::legacy::PassManager modulePM; llvm::legacy::FunctionPassManager funcPM(m); populatePassManagers(modulePM, funcPM, optLevel, sizeLevel); runPasses(modulePM, funcPM, *m); return llvm::Error::success(); }; } // Check if the opt flag is set and if it was located before `pos`. If so, // popuplate the module and the function pass managers with the passes // corresponding to the `level` of optimization and reset the flag. static void populatePassManagersOptLevel(llvm::cl::opt &opt, unsigned level, unsigned pos, llvm::legacy::PassManager &modulePM, llvm::legacy::FunctionPassManager &funcPM) { if (opt && opt.getPosition() < pos) { opt = false; populatePassManagers(modulePM, funcPM, level, /*sizeLevel=*/0); } } // Create and return a lambda that leverages LLVM PassInfo command line parser // to construct passes given the command line flags that come from the given // string rather than from the command line. std::function mlir::makeLLVMPassesTransformer(std::string config) { return [config](llvm::Module *m) -> llvm::Error { // Storage for -On flags, the index in this array corresponds to the // optimization level. Do not add anything else. llvm::SmallVector>, 4> optFlags{ optO0, optO1, optO2, optO3}; llvm::BumpPtrAllocator allocator; llvm::StringSaver saver(allocator); llvm::SmallVector args; args.push_back(""); // inject dummy program name llvm::cl::TokenizeGNUCommandLine(config, saver, args); llvm::cl::ParseCommandLineOptions(args.size(), args.data()); llvm::legacy::PassManager modulePM; llvm::legacy::FunctionPassManager funcPM(m); for (unsigned i = 0, e = llvmPasses.size(); i < e; ++i) { const auto *passInfo = llvmPasses[i]; if (!passInfo->getNormalCtor()) continue; // If there is any of -On flags textually before this pass flag, populate // the pass managers with the corresponding passes and reset the flag. for (unsigned j = 0; j < 4; ++j) populatePassManagersOptLevel( optFlags[j].get(), j, llvmPasses.getPosition(i), modulePM, funcPM); auto *pass = passInfo->createPass(); if (!pass) return llvm::make_error( "could not create pass " + passInfo->getPassName(), llvm::inconvertibleErrorCode()); modulePM.add(pass); } // Populate the pass managers with passes corresponding to the -On flags // that have not been used yet. Use UINT_MAX as the position index before // which the -On flag should appear as an always-true marker. for (unsigned j = 0; j < 4; ++j) populatePassManagersOptLevel(optFlags[j].get(), j, /*pos=*/UINT_MAX, modulePM, funcPM); // Run the -On function passes, then all the other passes. Note that // manually requested function passes were added to modulePM and will be // executed in order with manual/-On module passes. The function pass // manager is only populated in reaction to -On flags with passes that are // supposed to run "as soon as functions are created" according to the doc. // This behavior is identical to that of LLVM's "opt" tool. runPasses(modulePM, funcPM, *m); return llvm::Error::success(); }; }