285 lines · cpp
1//===- ModuleToObject.cpp - Module to object base class ---------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the base class for transforming Operations into binary10// objects.11//12//===----------------------------------------------------------------------===//13 14#include "mlir/Target/LLVM/ModuleToObject.h"15 16#include "mlir/ExecutionEngine/OptUtils.h"17#include "mlir/IR/BuiltinAttributeInterfaces.h"18#include "mlir/IR/BuiltinAttributes.h"19#include "mlir/Target/LLVMIR/Export.h"20#include "mlir/Target/LLVMIR/ModuleTranslation.h"21 22#include "llvm/Bitcode/BitcodeWriter.h"23#include "llvm/IR/LegacyPassManager.h"24#include "llvm/IRReader/IRReader.h"25#include "llvm/Linker/Linker.h"26#include "llvm/MC/TargetRegistry.h"27#include "llvm/Support/FileSystem.h"28#include "llvm/Support/MemoryBuffer.h"29#include "llvm/Support/SourceMgr.h"30#include "llvm/Support/raw_ostream.h"31#include "llvm/Target/TargetMachine.h"32#include "llvm/Transforms/IPO/Internalize.h"33 34using namespace mlir;35using namespace mlir::LLVM;36 37ModuleToObject::ModuleToObject(38 Operation &module, StringRef triple, StringRef chip, StringRef features,39 int optLevel, function_ref<void(llvm::Module &)> initialLlvmIRCallback,40 function_ref<void(llvm::Module &)> linkedLlvmIRCallback,41 function_ref<void(llvm::Module &)> optimizedLlvmIRCallback,42 function_ref<void(StringRef)> isaCallback)43 : module(module), triple(triple), chip(chip), features(features),44 optLevel(optLevel), initialLlvmIRCallback(initialLlvmIRCallback),45 linkedLlvmIRCallback(linkedLlvmIRCallback),46 optimizedLlvmIRCallback(optimizedLlvmIRCallback),47 isaCallback(isaCallback) {}48 49ModuleToObject::~ModuleToObject() = default;50 51Operation &ModuleToObject::getOperation() { return module; }52 53std::optional<llvm::TargetMachine *>54ModuleToObject::getOrCreateTargetMachine() {55 if (targetMachine)56 return targetMachine.get();57 // Load the target.58 std::string error;59 llvm::Triple parsedTriple(triple);60 const llvm::Target *target =61 llvm::TargetRegistry::lookupTarget(parsedTriple, error);62 if (!target) {63 getOperation().emitError()64 << "Failed to lookup target for triple '" << triple << "' " << error;65 return std::nullopt;66 }67 68 // Create the target machine using the target.69 targetMachine.reset(70 target->createTargetMachine(parsedTriple, chip, features, {}, {}));71 if (!targetMachine)72 return std::nullopt;73 return targetMachine.get();74}75 76std::unique_ptr<llvm::Module>77ModuleToObject::loadBitcodeFile(llvm::LLVMContext &context, StringRef path) {78 llvm::SMDiagnostic error;79 std::unique_ptr<llvm::Module> library =80 llvm::getLazyIRFileModule(path, error, context);81 if (!library) {82 getOperation().emitError() << "Failed loading file from " << path83 << ", error: " << error.getMessage();84 return nullptr;85 }86 if (failed(handleBitcodeFile(*library))) {87 return nullptr;88 }89 return library;90}91 92LogicalResult ModuleToObject::loadBitcodeFilesFromList(93 llvm::LLVMContext &context, ArrayRef<Attribute> librariesToLink,94 SmallVector<std::unique_ptr<llvm::Module>> &llvmModules,95 bool failureOnError) {96 for (Attribute linkLib : librariesToLink) {97 // Attributes in this list can be either list of file paths using98 // StringAttr, or a resource attribute pointing to the LLVM bitcode in99 // memory.100 if (auto filePath = dyn_cast<StringAttr>(linkLib)) {101 // Test if the path exists, if it doesn't abort.102 if (!llvm::sys::fs::is_regular_file(filePath.strref())) {103 getOperation().emitError()104 << "File path: " << filePath << " does not exist or is not a file.";105 return failure();106 }107 // Load the file or abort on error.108 if (auto bcFile = loadBitcodeFile(context, filePath))109 llvmModules.push_back(std::move(bcFile));110 else if (failureOnError)111 return failure();112 continue;113 }114 if (auto blobAttr = dyn_cast<BlobAttr>(linkLib)) {115 // Load the file or abort on error.116 llvm::SMDiagnostic error;117 ArrayRef<char> data = blobAttr.getData();118 std::unique_ptr<llvm::MemoryBuffer> buffer =119 llvm::MemoryBuffer::getMemBuffer(StringRef(data.data(), data.size()),120 "blobLinkedLib",121 /*RequiresNullTerminator=*/false);122 std::unique_ptr<llvm::Module> mod =123 getLazyIRModule(std::move(buffer), error, context);124 if (mod) {125 if (failed(handleBitcodeFile(*mod)))126 return failure();127 llvmModules.push_back(std::move(mod));128 } else if (failureOnError) {129 getOperation().emitError()130 << "Couldn't load LLVM library for linking: " << error.getMessage();131 return failure();132 }133 continue;134 }135 if (failureOnError) {136 getOperation().emitError()137 << "Unknown attribute describing LLVM library to load: " << linkLib;138 return failure();139 }140 }141 return success();142}143 144std::unique_ptr<llvm::Module>145ModuleToObject::translateToLLVMIR(llvm::LLVMContext &llvmContext) {146 return translateModuleToLLVMIR(&getOperation(), llvmContext);147}148 149LogicalResult150ModuleToObject::linkFiles(llvm::Module &module,151 SmallVector<std::unique_ptr<llvm::Module>> &&libs) {152 if (libs.empty())153 return success();154 llvm::Linker linker(module);155 for (std::unique_ptr<llvm::Module> &libModule : libs) {156 // This bitcode linking imports the library functions into the module,157 // allowing LLVM optimization passes (which must run after linking) to158 // optimize across the libraries and the module's code. We also only import159 // symbols if they are referenced by the module or a previous library since160 // there will be no other source of references to those symbols in this161 // compilation and since we don't want to bloat the resulting code object.162 bool err = linker.linkInModule(163 std::move(libModule), llvm::Linker::Flags::LinkOnlyNeeded,164 [](llvm::Module &m, const StringSet<> &gvs) {165 llvm::internalizeModule(m, [&gvs](const llvm::GlobalValue &gv) {166 return !gv.hasName() || (gvs.count(gv.getName()) == 0);167 });168 });169 // True is linker failure170 if (err) {171 getOperation().emitError("Unrecoverable failure during bitcode linking.");172 // We have no guaranties about the state of `ret`, so bail173 return failure();174 }175 }176 return success();177}178 179LogicalResult ModuleToObject::optimizeModule(llvm::Module &module,180 181 int optLevel) {182 if (optLevel < 0 || optLevel > 3)183 return getOperation().emitError()184 << "Invalid optimization level: " << optLevel << ".";185 186 std::optional<llvm::TargetMachine *> targetMachine =187 getOrCreateTargetMachine();188 if (!targetMachine)189 return getOperation().emitError()190 << "Target Machine unavailable for triple " << triple191 << ", can't optimize with LLVM\n";192 (*targetMachine)->setOptLevel(static_cast<llvm::CodeGenOptLevel>(optLevel));193 194 auto transformer =195 makeOptimizingTransformer(optLevel, /*sizeLevel=*/0, *targetMachine);196 auto error = transformer(&module);197 if (error) {198 InFlightDiagnostic mlirError = getOperation().emitError();199 llvm::handleAllErrors(200 std::move(error), [&mlirError](const llvm::ErrorInfoBase &ei) {201 mlirError << "Could not optimize LLVM IR: " << ei.message() << "\n";202 });203 return mlirError;204 }205 return success();206}207 208std::optional<std::string>209ModuleToObject::translateToISA(llvm::Module &llvmModule,210 llvm::TargetMachine &targetMachine) {211 std::string targetISA;212 llvm::raw_string_ostream stream(targetISA);213 214 { // Drop pstream after this to prevent the ISA from being stuck buffering215 llvm::buffer_ostream pstream(stream);216 llvm::legacy::PassManager codegenPasses;217 218 if (targetMachine.addPassesToEmitFile(codegenPasses, pstream, nullptr,219 llvm::CodeGenFileType::AssemblyFile))220 return std::nullopt;221 222 codegenPasses.run(llvmModule);223 }224 return targetISA;225}226 227void ModuleToObject::setDataLayoutAndTriple(llvm::Module &module) {228 // Create the target machine.229 std::optional<llvm::TargetMachine *> targetMachine =230 getOrCreateTargetMachine();231 if (targetMachine) {232 // Set the data layout and target triple of the module.233 module.setDataLayout((*targetMachine)->createDataLayout());234 module.setTargetTriple((*targetMachine)->getTargetTriple());235 }236}237 238std::optional<SmallVector<char, 0>>239ModuleToObject::moduleToObject(llvm::Module &llvmModule) {240 SmallVector<char, 0> binaryData;241 // Write the LLVM module bitcode to a buffer.242 llvm::raw_svector_ostream outputStream(binaryData);243 llvm::WriteBitcodeToFile(llvmModule, outputStream);244 return binaryData;245}246 247std::optional<SmallVector<char, 0>> ModuleToObject::run() {248 // Translate the module to LLVM IR.249 llvm::LLVMContext llvmContext;250 std::unique_ptr<llvm::Module> llvmModule = translateToLLVMIR(llvmContext);251 if (!llvmModule) {252 getOperation().emitError() << "Failed creating the llvm::Module.";253 return std::nullopt;254 }255 setDataLayoutAndTriple(*llvmModule);256 257 if (initialLlvmIRCallback)258 initialLlvmIRCallback(*llvmModule);259 260 // Link bitcode files.261 handleModulePreLink(*llvmModule);262 {263 auto libs = loadBitcodeFiles(*llvmModule);264 if (!libs)265 return std::nullopt;266 if (!libs->empty())267 if (failed(linkFiles(*llvmModule, std::move(*libs))))268 return std::nullopt;269 handleModulePostLink(*llvmModule);270 }271 272 if (linkedLlvmIRCallback)273 linkedLlvmIRCallback(*llvmModule);274 275 // Optimize the module.276 if (failed(optimizeModule(*llvmModule, optLevel)))277 return std::nullopt;278 279 if (optimizedLlvmIRCallback)280 optimizedLlvmIRCallback(*llvmModule);281 282 // Return the serialized object.283 return moduleToObject(*llvmModule);284}285