blob: 606060068be4dbf67e943811cebe0dc72a90a0b1 [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/AOT/LLVMAOTTarget.h"
#include <cstdlib>
#include "iree/compiler/Dialect/HAL/Target/LLVM/AOT/LLVMAOTTargetLinker.h"
#include "iree/compiler/Dialect/HAL/Target/LLVM/LLVMBaseTarget.h"
#include "iree/compiler/Dialect/HAL/Target/LLVM/LLVMIRPasses.h"
#include "iree/compiler/Dialect/HAL/Target/TargetRegistry.h"
#include "iree/schemas/dylib_executable_def_generated.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/TargetSelect.h"
#include "mlir/Target/LLVMIR.h"
namespace mlir {
namespace iree_compiler {
namespace IREE {
namespace HAL {
class LLVMAOTTargetBackend final : public LLVMBaseTargetBackend {
public:
explicit LLVMAOTTargetBackend(LLVMTargetOptions options)
: LLVMBaseTargetBackend(options) {}
// NOTE: we could vary these based on the options, such as by arch/etc.
std::string name() const override { return "llvm_aot"; }
std::string filter_pattern() const override { return "dylib*"; }
LogicalResult serializeExecutable(IREE::HAL::ExecutableTargetOp targetOp,
OpBuilder &executableBuilder) override {
// Perform the translation in a separate context to avoid any
// multi-threading issues.
llvm::LLVMContext context;
// Remove all private functions, e.g tile size calcuations.
SmallVector<FuncOp, 4> nonPublicFn;
for (auto func : targetOp.getInnerModule().getOps<FuncOp>()) {
if (SymbolTable::getSymbolVisibility(func) !=
SymbolTable::Visibility::Public) {
nonPublicFn.push_back(func);
}
}
for (auto func : nonPublicFn) {
func.erase();
}
// At this moment we are leaving MLIR LLVM dialect land translating module
// into target independent LLVMIR.
auto llvmModule =
mlir::translateModuleToLLVMIR(targetOp.getInnerModule(), context);
if (!llvmModule) {
return failure();
}
iree::DyLibExecutableDefT dyLibExecutableDef;
// Create invocation function an populate entry_points.
auto entryPointOps = targetOp.getBlock().getOps<ExecutableEntryPointOp>();
for (auto entryPointOp : entryPointOps) {
dyLibExecutableDef.entry_points.push_back(
std::string(entryPointOp.sym_name()));
}
// LLVMIR opt passes.
auto targetMachine = createTargetMachine(options_);
if (!targetMachine) {
targetOp.emitError("Can't create target machine for target triple: " +
options_.targetTriple);
return failure();
}
llvmModule->setDataLayout(targetMachine->createDataLayout());
llvmModule->setTargetTriple(targetMachine->getTargetTriple().str());
if (failed(
runLLVMIRPasses(options_, targetMachine.get(), llvmModule.get()))) {
return targetOp.emitError(
"Can't build LLVMIR opt passes for ExecutableOp module");
}
std::string objData;
if (failed(runEmitObjFilePasses(targetMachine.get(), llvmModule.get(),
&objData))) {
return targetOp.emitError("Can't compile LLVMIR module to an obj");
}
std::string sharedLibData;
const char *linkerToolPath = std::getenv("IREE_LLVMAOT_LINKER_PATH");
if (linkerToolPath != nullptr) {
auto sharedLibDataStatus = linkLLVMAOTObjects(linkerToolPath, objData);
if (!sharedLibDataStatus.ok()) {
return targetOp.emitError(
"Can't link executable and generate target dylib, using linker "
"toolchain: '" +
std::string(linkerToolPath) + "'");
}
sharedLibData = sharedLibDataStatus.value();
} else {
auto sharedLibDataStatus = linkLLVMAOTObjectsWithLLDElf(objData);
if (!sharedLibDataStatus.ok()) {
return targetOp.emitError(
"Can't link executable and generate target dylib using "
"lld::elf::link");
}
sharedLibData = sharedLibDataStatus.value();
}
dyLibExecutableDef.library_embedded = {sharedLibData.begin(),
sharedLibData.end()};
::flatbuffers::FlatBufferBuilder fbb;
auto executableOffset =
iree::DyLibExecutableDef::Pack(fbb, &dyLibExecutableDef);
iree::FinishDyLibExecutableDefBuffer(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.
executableBuilder.create<IREE::HAL::ExecutableBinaryOp>(
targetOp.getLoc(),
static_cast<uint32_t>(IREE::HAL::ExecutableFormat::DyLib),
std::move(bytes));
return success();
}
};
void registerLLVMAOTTargetBackends(
std::function<LLVMTargetOptions()> queryOptions) {
getLLVMTargetOptionsFromFlags();
static TargetBackendRegistration registration("dylib-llvm-aot", [=]() {
#define INIT_LLVM_TARGET(TargetName) \
LLVMInitialize##TargetName##Target(); \
LLVMInitialize##TargetName##TargetMC(); \
LLVMInitialize##TargetName##TargetInfo(); \
LLVMInitialize##TargetName##AsmPrinter(); \
LLVMInitialize##TargetName##AsmParser();
INIT_LLVM_TARGET(X86)
INIT_LLVM_TARGET(ARM)
INIT_LLVM_TARGET(AArch64)
return std::make_unique<LLVMAOTTargetBackend>(queryOptions());
});
}
} // namespace HAL
} // namespace IREE
} // namespace iree_compiler
} // namespace mlir