Alex Zinenko 4bb31f7377 ExecutionEngine: provide utils for running CLI-configured LLVM passes
A recent change introduced a possibility to run LLVM IR transformation during
JIT-compilation in the ExecutionEngine.  Provide helper functions that
construct IR transformers given either clang-style optimization levels or a
list passes to run.  The latter wraps the LLVM command line option parser to
parse strings rather than actual command line arguments.  As a result, we can
run either of

    mlir-cpu-runner -O3 input.mlir
    mlir-cpu-runner -some-mlir-pass -llvm-opts="-llvm-pass -other-llvm-pass"

to combine different transformations.  The transformer builder functions are
provided as a separate library that depends on LLVM pass libraries unlike the
main execution engine library.  The library can be used for integrating MLIR
execution engine into external frameworks.

PiperOrigin-RevId: 234173493
2019-03-29 16:29:41 -07:00

119 lines
4.2 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 <mutex>
// 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) {
for (auto &func : m) {
funcPM.run(func);
}
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 &registry = *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);
}
// 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::PassManagerBuilder builder;
builder.OptLevel = optLevel;
builder.SizeLevel = sizeLevel;
builder.Inliner = llvm::createFunctionInliningPass(
optLevel, sizeLevel, /*DisableInlineHotCallSite=*/false);
llvm::legacy::PassManager modulePM;
llvm::legacy::FunctionPassManager funcPM(m);
builder.populateModulePassManager(modulePM);
builder.populateFunctionPassManager(funcPM);
runPasses(modulePM, funcPM, *m);
return llvm::Error::success();
};
}
// 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 {
static llvm::cl::list<const llvm::PassInfo *, bool, llvm::PassNameParser>
llvmPasses(llvm::cl::desc("LLVM optimizing passes to run"));
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;
for (const auto *passInfo : llvmPasses) {
if (!passInfo->getNormalCtor())
continue;
auto *pass = passInfo->createPass();
if (!pass)
return llvm::make_error<llvm::StringError>(
"could not create pass " + passInfo->getPassName(),
llvm::inconvertibleErrorCode());
modulePM.add(pass);
}
modulePM.run(*m);
return llvm::Error::success();
};
}