| //===- 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); |
| } |