forked from OSchip/llvm-project
189 lines
7.7 KiB
C++
189 lines
7.7 KiB
C++
//===- 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 <climits>
|
|
#include <mutex>
|
|
|
|
// 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<const llvm::PassInfo *, bool, llvm::PassNameParser>
|
|
llvmPasses(llvm::cl::desc("LLVM optimizing passes to run"),
|
|
llvm::cl::cat(optFlags));
|
|
|
|
// CLI variables for -On options.
|
|
static llvm::cl::opt<bool> optO0("O0", llvm::cl::desc("Run opt O0 passes"),
|
|
llvm::cl::cat(optFlags));
|
|
static llvm::cl::opt<bool> optO1("O1", llvm::cl::desc("Run opt O1 passes"),
|
|
llvm::cl::cat(optFlags));
|
|
static llvm::cl::opt<bool> optO2("O2", llvm::cl::desc("Run opt O2 passes"),
|
|
llvm::cl::cat(optFlags));
|
|
static llvm::cl::opt<bool> 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<std::mutex> 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<llvm::Error(llvm::Module *)>
|
|
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<bool> &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<llvm::Error(llvm::Module *)>
|
|
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<std::reference_wrapper<llvm::cl::opt<bool>>, 4> optFlags{
|
|
optO0, optO1, optO2, optO3};
|
|
|
|
llvm::BumpPtrAllocator allocator;
|
|
llvm::StringSaver saver(allocator);
|
|
llvm::SmallVector<const char *, 16> 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<llvm::StringError>(
|
|
"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();
|
|
};
|
|
}
|