236 lines
8.1 KiB
C++
236 lines
8.1 KiB
C++
//===- ModuleToObject.cpp - Module to object base class ---------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the base class for transforming Operations into binary
|
|
// objects.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "mlir/Target/LLVM/ModuleToObject.h"
|
|
|
|
#include "mlir/ExecutionEngine/OptUtils.h"
|
|
#include "mlir/IR/BuiltinOps.h"
|
|
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
|
|
#include "mlir/Target/LLVMIR/Export.h"
|
|
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
|
|
|
|
#include "llvm/Bitcode/BitcodeWriter.h"
|
|
#include "llvm/IR/LegacyPassManager.h"
|
|
#include "llvm/IRReader/IRReader.h"
|
|
#include "llvm/Linker/Linker.h"
|
|
#include "llvm/MC/TargetRegistry.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include "llvm/Target/TargetMachine.h"
|
|
#include "llvm/Transforms/IPO/Internalize.h"
|
|
|
|
using namespace mlir;
|
|
using namespace mlir::LLVM;
|
|
|
|
ModuleToObject::ModuleToObject(Operation &module, StringRef triple,
|
|
StringRef chip, StringRef features, int optLevel)
|
|
: module(module), triple(triple), chip(chip), features(features),
|
|
optLevel(optLevel) {}
|
|
|
|
ModuleToObject::~ModuleToObject() = default;
|
|
|
|
Operation &ModuleToObject::getOperation() { return module; }
|
|
|
|
std::optional<llvm::TargetMachine *>
|
|
ModuleToObject::getOrCreateTargetMachine() {
|
|
if (targetMachine)
|
|
return targetMachine.get();
|
|
// Load the target.
|
|
std::string error;
|
|
const llvm::Target *target =
|
|
llvm::TargetRegistry::lookupTarget(triple, error);
|
|
if (!target) {
|
|
getOperation().emitError()
|
|
<< "Failed to lookup target for triple '" << triple << "' " << error;
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Create the target machine using the target.
|
|
targetMachine.reset(
|
|
target->createTargetMachine(triple, chip, features, {}, {}));
|
|
if (!targetMachine)
|
|
return std::nullopt;
|
|
return targetMachine.get();
|
|
}
|
|
|
|
std::unique_ptr<llvm::Module>
|
|
ModuleToObject::loadBitcodeFile(llvm::LLVMContext &context, StringRef path) {
|
|
llvm::SMDiagnostic error;
|
|
std::unique_ptr<llvm::Module> library =
|
|
llvm::getLazyIRFileModule(path, error, context);
|
|
if (!library) {
|
|
getOperation().emitError() << "Failed loading file from " << path
|
|
<< ", error: " << error.getMessage();
|
|
return nullptr;
|
|
}
|
|
if (failed(handleBitcodeFile(*library))) {
|
|
return nullptr;
|
|
}
|
|
return library;
|
|
}
|
|
|
|
LogicalResult ModuleToObject::loadBitcodeFilesFromList(
|
|
llvm::LLVMContext &context, ArrayRef<std::string> fileList,
|
|
SmallVector<std::unique_ptr<llvm::Module>> &llvmModules,
|
|
bool failureOnError) {
|
|
for (const std::string &str : fileList) {
|
|
// Test if the path exists, if it doesn't abort.
|
|
StringRef pathRef = StringRef(str.data(), str.size());
|
|
if (!llvm::sys::fs::is_regular_file(pathRef)) {
|
|
getOperation().emitError()
|
|
<< "File path: " << pathRef << " does not exist or is not a file.\n";
|
|
return failure();
|
|
}
|
|
// Load the file or abort on error.
|
|
if (auto bcFile = loadBitcodeFile(context, pathRef))
|
|
llvmModules.push_back(std::move(bcFile));
|
|
else if (failureOnError)
|
|
return failure();
|
|
}
|
|
return success();
|
|
}
|
|
|
|
std::unique_ptr<llvm::Module>
|
|
ModuleToObject::translateToLLVMIR(llvm::LLVMContext &llvmContext) {
|
|
return translateModuleToLLVMIR(&getOperation(), llvmContext);
|
|
}
|
|
|
|
LogicalResult
|
|
ModuleToObject::linkFiles(llvm::Module &module,
|
|
SmallVector<std::unique_ptr<llvm::Module>> &&libs) {
|
|
if (libs.empty())
|
|
return success();
|
|
llvm::Linker linker(module);
|
|
for (std::unique_ptr<llvm::Module> &libModule : libs) {
|
|
// This bitcode linking imports the library functions into the module,
|
|
// allowing LLVM optimization passes (which must run after linking) to
|
|
// optimize across the libraries and the module's code. We also only import
|
|
// symbols if they are referenced by the module or a previous library since
|
|
// there will be no other source of references to those symbols in this
|
|
// compilation and since we don't want to bloat the resulting code object.
|
|
bool err = linker.linkInModule(
|
|
std::move(libModule), llvm::Linker::Flags::LinkOnlyNeeded,
|
|
[](llvm::Module &m, const StringSet<> &gvs) {
|
|
llvm::internalizeModule(m, [&gvs](const llvm::GlobalValue &gv) {
|
|
return !gv.hasName() || (gvs.count(gv.getName()) == 0);
|
|
});
|
|
});
|
|
// True is linker failure
|
|
if (err) {
|
|
getOperation().emitError("Unrecoverable failure during bitcode linking.");
|
|
// We have no guaranties about the state of `ret`, so bail
|
|
return failure();
|
|
}
|
|
}
|
|
return success();
|
|
}
|
|
|
|
LogicalResult ModuleToObject::optimizeModule(llvm::Module &module,
|
|
|
|
int optLevel) {
|
|
if (optLevel < 0 || optLevel > 3)
|
|
return getOperation().emitError()
|
|
<< "Invalid optimization level: " << optLevel << ".";
|
|
|
|
std::optional<llvm::TargetMachine *> targetMachine =
|
|
getOrCreateTargetMachine();
|
|
if (!targetMachine)
|
|
return getOperation().emitError()
|
|
<< "Target Machine unavailable for triple " << triple
|
|
<< ", can't optimize with LLVM\n";
|
|
(*targetMachine)->setOptLevel(static_cast<llvm::CodeGenOptLevel>(optLevel));
|
|
|
|
auto transformer =
|
|
makeOptimizingTransformer(optLevel, /*sizeLevel=*/0, *targetMachine);
|
|
auto error = transformer(&module);
|
|
if (error) {
|
|
InFlightDiagnostic mlirError = getOperation().emitError();
|
|
llvm::handleAllErrors(
|
|
std::move(error), [&mlirError](const llvm::ErrorInfoBase &ei) {
|
|
mlirError << "Could not optimize LLVM IR: " << ei.message() << "\n";
|
|
});
|
|
return mlirError;
|
|
}
|
|
return success();
|
|
}
|
|
|
|
std::optional<std::string>
|
|
ModuleToObject::translateToISA(llvm::Module &llvmModule,
|
|
llvm::TargetMachine &targetMachine) {
|
|
std::string targetISA;
|
|
llvm::raw_string_ostream stream(targetISA);
|
|
|
|
{ // Drop pstream after this to prevent the ISA from being stuck buffering
|
|
llvm::buffer_ostream pstream(stream);
|
|
llvm::legacy::PassManager codegenPasses;
|
|
|
|
if (targetMachine.addPassesToEmitFile(codegenPasses, pstream, nullptr,
|
|
llvm::CodeGenFileType::AssemblyFile))
|
|
return std::nullopt;
|
|
|
|
codegenPasses.run(llvmModule);
|
|
}
|
|
return stream.str();
|
|
}
|
|
|
|
void ModuleToObject::setDataLayoutAndTriple(llvm::Module &module) {
|
|
// Create the target machine.
|
|
std::optional<llvm::TargetMachine *> targetMachine =
|
|
getOrCreateTargetMachine();
|
|
if (targetMachine) {
|
|
// Set the data layout and target triple of the module.
|
|
module.setDataLayout((*targetMachine)->createDataLayout());
|
|
module.setTargetTriple((*targetMachine)->getTargetTriple().getTriple());
|
|
}
|
|
}
|
|
|
|
std::optional<SmallVector<char, 0>>
|
|
ModuleToObject::moduleToObject(llvm::Module &llvmModule) {
|
|
SmallVector<char, 0> binaryData;
|
|
// Write the LLVM module bitcode to a buffer.
|
|
llvm::raw_svector_ostream outputStream(binaryData);
|
|
llvm::WriteBitcodeToFile(llvmModule, outputStream);
|
|
return binaryData;
|
|
}
|
|
|
|
std::optional<SmallVector<char, 0>> ModuleToObject::run() {
|
|
// Translate the module to LLVM IR.
|
|
llvm::LLVMContext llvmContext;
|
|
std::unique_ptr<llvm::Module> llvmModule = translateToLLVMIR(llvmContext);
|
|
if (!llvmModule) {
|
|
getOperation().emitError() << "Failed creating the llvm::Module.";
|
|
return std::nullopt;
|
|
}
|
|
setDataLayoutAndTriple(*llvmModule);
|
|
|
|
// Link bitcode files.
|
|
handleModulePreLink(*llvmModule);
|
|
{
|
|
auto libs = loadBitcodeFiles(*llvmModule);
|
|
if (!libs)
|
|
return std::nullopt;
|
|
if (!libs->empty())
|
|
if (failed(linkFiles(*llvmModule, std::move(*libs))))
|
|
return std::nullopt;
|
|
handleModulePostLink(*llvmModule);
|
|
}
|
|
|
|
// Optimize the module.
|
|
if (failed(optimizeModule(*llvmModule, optLevel)))
|
|
return std::nullopt;
|
|
|
|
// Return the serialized object.
|
|
return moduleToObject(*llvmModule);
|
|
}
|