blob: 77480d2510ae7baac1dc9a05c5bbd10d871b4208 [file] [log] [blame]
// Copyright 2020 Google LLC
//
// 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
//
// https://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.
#include "iree/compiler/Dialect/HAL/Target/LLVM/LLVMTarget.h"
#include "iree/compiler/Dialect/Flow/IR/FlowOps.h"
#include "iree/compiler/Dialect/HAL/Target/LegacyUtil.h"
#include "iree/compiler/Translation/CodegenPasses/Passes.h"
#include "iree/schemas/llvmir_executable_def_generated.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/IR/Module.h"
#include "mlir/Conversion/LinalgToLLVM/LinalgToLLVM.h"
#include "mlir/Conversion/LoopToStandard/ConvertLoopToStandard.h"
#include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h"
#include "mlir/Dialect/Linalg/IR/LinalgOps.h"
#include "mlir/Dialect/Linalg/Passes.h"
#include "mlir/Dialect/StandardOps/IR/Ops.h"
#include "mlir/IR/Builders.h"
#include "mlir/Pass/PassManager.h"
#include "mlir/Support/LogicalResult.h"
#include "mlir/Target/LLVMIR.h"
#include "mlir/Transforms/Passes.h"
namespace mlir {
namespace iree_compiler {
namespace IREE {
namespace HAL {
LLVMTargetOptions getLLVMTargetOptionsFromFlags() {
LLVMTargetOptions targetOptions;
return targetOptions;
}
// TODO(ataei) Move this to utils ?
// Returns a list of entry point names matching the expected export ordinals.
static std::vector<std::string> populateEntryPointNames(
IREE::Flow::ExecutableOp executableOp, bool addCInterface) {
std::vector<std::string> entryPointNames;
for (auto& op : executableOp.getBlock().getOperations()) {
if (auto entryOp = dyn_cast<IREE::Flow::DispatchEntryOp>(op)) {
std::string func_name =
addCInterface ? "_mlir_ciface_" + std::string(entryOp.function_ref())
: std::string(entryOp.function_ref());
entryPointNames.push_back(func_name);
}
}
return entryPointNames;
}
// Adds a sequence of passess to a given pass manager that progressively lower
// from HLO to LLVM throught linalg dialect.
void buildLLVMTransformPassPipeline(OpPassManager& pm) {
// HLO -> Linalg on buffers.
addHLOToLinalgOnBuffersPasses(pm);
// Linalg -> Loops
pm.addPass(createConvertLinalgToLoopsPass());
pm.addPass(createCanonicalizerPass());
pm.addPass(createCSEPass());
// Loops -> STD
pm.addPass(createLowerToCFGPass());
pm.addPass(createCanonicalizerPass());
pm.addPass(createCSEPass());
// (Linalg, STD) -> LLVM
pm.addPass(createConvertLinalgToLLVMPass());
pm.addPass(createCanonicalizerPass());
pm.addPass(createCSEPass());
}
LogicalResult translateToLLVMExecutable(
IREE::HAL::ExecutableOp executableOp,
ExecutableTargetOptions executableOptions,
LLVMTargetOptions targetOptions) {
// Clone the module containing the things we want to translate. We do this so
// that multiple targets can pull from the same source without conflicting.
auto sourceOp = executableOp.getSourceOp().clone();
auto sourceOpErase =
llvm::make_scope_exit([&sourceOp]() { sourceOp.erase(); });
auto flowExecutableOp =
*sourceOp.getInnerModule().getOps<IREE::Flow::ExecutableOp>().begin();
auto moduleOp = flowExecutableOp.getInnerModule();
if (failed(makeLegacyExecutableABI(sourceOp))) {
return failure();
}
// Lower module to LLVM Dialect.
PassManager conversionPassManager(moduleOp.getContext());
buildLLVMTransformPassPipeline(conversionPassManager);
if (failed(conversionPassManager.run(moduleOp)))
return moduleOp.emitError()
<< "failed to run IREE -> LLVM conversion passes";
// At this moment we are leaving MLIR LLVM dialect land translating module
// into target independent LLVMIR.
auto llvmModule = mlir::translateModuleToLLVMIR(moduleOp);
// Serialize LLVM module.
std::string bufferString;
llvm::raw_string_ostream ostream(bufferString);
llvmModule->print(ostream, nullptr);
ostream.flush();
// Creates executable bytes.
iree::LLVMIRExecutableDefT llvmIrExecutableDef;
llvmIrExecutableDef.llvmir_module = {bufferString.begin(),
bufferString.end()};
llvmIrExecutableDef.entry_points =
populateEntryPointNames(flowExecutableOp, true);
::flatbuffers::FlatBufferBuilder fbb;
auto executableOffset =
iree::LLVMIRExecutableDef::Pack(fbb, &llvmIrExecutableDef);
iree::FinishLLVMIRExecutableDefBuffer(fbb, executableOffset);
std::vector<uint8_t> bytes;
bytes.resize(fbb.GetSize());
std::memcpy(bytes.data(), fbb.GetBufferPointer(), bytes.size());
// Add the binary data to the target executable.
OpBuilder targetBuilder(&executableOp.getBlock());
targetBuilder.setInsertionPoint(&executableOp.getBlock().back());
auto binaryOp = targetBuilder.create<IREE::HAL::ExecutableBinaryOp>(
executableOp.getLoc(),
static_cast<uint32_t>(IREE::HAL::ExecutableFormat::LLVM),
std::move(bytes));
OpBuilder binaryBuilder(&binaryOp.getBlock().back());
binaryBuilder.clone(*moduleOp.getOperation());
return success();
}
static ExecutableTargetRegistration targetRegistration(
"llvm-ir", +[](IREE::HAL::ExecutableOp executableOp,
ExecutableTargetOptions executableOptions) {
return translateToLLVMExecutable(executableOp, executableOptions,
getLLVMTargetOptionsFromFlags());
});
} // namespace HAL
} // namespace IREE
} // namespace iree_compiler
} // namespace mlir