1895c4ac3SFabian Mora //===- ModuleToObject.cpp - Module to object base class ---------*- C++ -*-===// 2895c4ac3SFabian Mora // 3895c4ac3SFabian Mora // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4895c4ac3SFabian Mora // See https://llvm.org/LICENSE.txt for license information. 5895c4ac3SFabian Mora // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6895c4ac3SFabian Mora // 7895c4ac3SFabian Mora //===----------------------------------------------------------------------===// 8895c4ac3SFabian Mora // 9895c4ac3SFabian Mora // This file implements the base class for transforming Operations into binary 10895c4ac3SFabian Mora // objects. 11895c4ac3SFabian Mora // 12895c4ac3SFabian Mora //===----------------------------------------------------------------------===// 13895c4ac3SFabian Mora 14895c4ac3SFabian Mora #include "mlir/Target/LLVM/ModuleToObject.h" 15895c4ac3SFabian Mora 16895c4ac3SFabian Mora #include "mlir/ExecutionEngine/OptUtils.h" 17*72e8b9aeSMehdi Amini #include "mlir/IR/BuiltinAttributeInterfaces.h" 18*72e8b9aeSMehdi Amini #include "mlir/IR/BuiltinAttributes.h" 19895c4ac3SFabian Mora #include "mlir/IR/BuiltinOps.h" 20895c4ac3SFabian Mora #include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h" 21895c4ac3SFabian Mora #include "mlir/Target/LLVMIR/Export.h" 22895c4ac3SFabian Mora #include "mlir/Target/LLVMIR/ModuleTranslation.h" 23895c4ac3SFabian Mora 24895c4ac3SFabian Mora #include "llvm/Bitcode/BitcodeWriter.h" 25895c4ac3SFabian Mora #include "llvm/IR/LegacyPassManager.h" 26895c4ac3SFabian Mora #include "llvm/IRReader/IRReader.h" 27895c4ac3SFabian Mora #include "llvm/Linker/Linker.h" 28895c4ac3SFabian Mora #include "llvm/MC/TargetRegistry.h" 29895c4ac3SFabian Mora #include "llvm/Support/FileSystem.h" 30*72e8b9aeSMehdi Amini #include "llvm/Support/MemoryBuffer.h" 31895c4ac3SFabian Mora #include "llvm/Support/Path.h" 32895c4ac3SFabian Mora #include "llvm/Support/SourceMgr.h" 33895c4ac3SFabian Mora #include "llvm/Support/raw_ostream.h" 34895c4ac3SFabian Mora #include "llvm/Target/TargetMachine.h" 35895c4ac3SFabian Mora #include "llvm/Transforms/IPO/Internalize.h" 36895c4ac3SFabian Mora 37895c4ac3SFabian Mora using namespace mlir; 38895c4ac3SFabian Mora using namespace mlir::LLVM; 39895c4ac3SFabian Mora 4008e76096SZichen Lu ModuleToObject::ModuleToObject( 4108e76096SZichen Lu Operation &module, StringRef triple, StringRef chip, StringRef features, 4208e76096SZichen Lu int optLevel, function_ref<void(llvm::Module &)> initialLlvmIRCallback, 4308e76096SZichen Lu function_ref<void(llvm::Module &)> linkedLlvmIRCallback, 4408e76096SZichen Lu function_ref<void(llvm::Module &)> optimizedLlvmIRCallback, 4508e76096SZichen Lu function_ref<void(StringRef)> isaCallback) 46895c4ac3SFabian Mora : module(module), triple(triple), chip(chip), features(features), 4708e76096SZichen Lu optLevel(optLevel), initialLlvmIRCallback(initialLlvmIRCallback), 4808e76096SZichen Lu linkedLlvmIRCallback(linkedLlvmIRCallback), 4908e76096SZichen Lu optimizedLlvmIRCallback(optimizedLlvmIRCallback), 5008e76096SZichen Lu isaCallback(isaCallback) {} 51895c4ac3SFabian Mora 52d9dadfdaSMehdi Amini ModuleToObject::~ModuleToObject() = default; 53d9dadfdaSMehdi Amini 54895c4ac3SFabian Mora Operation &ModuleToObject::getOperation() { return module; } 55895c4ac3SFabian Mora 56d9dadfdaSMehdi Amini std::optional<llvm::TargetMachine *> 57d9dadfdaSMehdi Amini ModuleToObject::getOrCreateTargetMachine() { 58d9dadfdaSMehdi Amini if (targetMachine) 59d9dadfdaSMehdi Amini return targetMachine.get(); 60895c4ac3SFabian Mora // Load the target. 61d9dadfdaSMehdi Amini std::string error; 62895c4ac3SFabian Mora const llvm::Target *target = 63895c4ac3SFabian Mora llvm::TargetRegistry::lookupTarget(triple, error); 64895c4ac3SFabian Mora if (!target) { 65d9dadfdaSMehdi Amini getOperation().emitError() 66d9dadfdaSMehdi Amini << "Failed to lookup target for triple '" << triple << "' " << error; 67d9dadfdaSMehdi Amini return std::nullopt; 68895c4ac3SFabian Mora } 69895c4ac3SFabian Mora 70895c4ac3SFabian Mora // Create the target machine using the target. 71d9dadfdaSMehdi Amini targetMachine.reset( 72d9dadfdaSMehdi Amini target->createTargetMachine(triple, chip, features, {}, {})); 73d9dadfdaSMehdi Amini if (!targetMachine) 74d9dadfdaSMehdi Amini return std::nullopt; 75d9dadfdaSMehdi Amini return targetMachine.get(); 76895c4ac3SFabian Mora } 77895c4ac3SFabian Mora 78895c4ac3SFabian Mora std::unique_ptr<llvm::Module> 79d9dadfdaSMehdi Amini ModuleToObject::loadBitcodeFile(llvm::LLVMContext &context, StringRef path) { 80895c4ac3SFabian Mora llvm::SMDiagnostic error; 81895c4ac3SFabian Mora std::unique_ptr<llvm::Module> library = 82895c4ac3SFabian Mora llvm::getLazyIRFileModule(path, error, context); 83895c4ac3SFabian Mora if (!library) { 84895c4ac3SFabian Mora getOperation().emitError() << "Failed loading file from " << path 85895c4ac3SFabian Mora << ", error: " << error.getMessage(); 86895c4ac3SFabian Mora return nullptr; 87895c4ac3SFabian Mora } 88d9dadfdaSMehdi Amini if (failed(handleBitcodeFile(*library))) { 89895c4ac3SFabian Mora return nullptr; 90895c4ac3SFabian Mora } 91895c4ac3SFabian Mora return library; 92895c4ac3SFabian Mora } 93895c4ac3SFabian Mora 94895c4ac3SFabian Mora LogicalResult ModuleToObject::loadBitcodeFilesFromList( 95*72e8b9aeSMehdi Amini llvm::LLVMContext &context, ArrayRef<Attribute> librariesToLink, 96895c4ac3SFabian Mora SmallVector<std::unique_ptr<llvm::Module>> &llvmModules, 97895c4ac3SFabian Mora bool failureOnError) { 98*72e8b9aeSMehdi Amini for (Attribute linkLib : librariesToLink) { 99*72e8b9aeSMehdi Amini // Attributes in this list can be either list of file paths using 100*72e8b9aeSMehdi Amini // StringAttr, or a resource attribute pointing to the LLVM bitcode in 101*72e8b9aeSMehdi Amini // memory. 102*72e8b9aeSMehdi Amini if (auto filePath = dyn_cast<StringAttr>(linkLib)) { 103895c4ac3SFabian Mora // Test if the path exists, if it doesn't abort. 104*72e8b9aeSMehdi Amini if (!llvm::sys::fs::is_regular_file(filePath.strref())) { 105895c4ac3SFabian Mora getOperation().emitError() 106*72e8b9aeSMehdi Amini << "File path: " << filePath << " does not exist or is not a file."; 107895c4ac3SFabian Mora return failure(); 108895c4ac3SFabian Mora } 109895c4ac3SFabian Mora // Load the file or abort on error. 110*72e8b9aeSMehdi Amini if (auto bcFile = loadBitcodeFile(context, filePath)) 111895c4ac3SFabian Mora llvmModules.push_back(std::move(bcFile)); 112895c4ac3SFabian Mora else if (failureOnError) 113895c4ac3SFabian Mora return failure(); 114*72e8b9aeSMehdi Amini continue; 115*72e8b9aeSMehdi Amini } 116*72e8b9aeSMehdi Amini if (auto blobAttr = dyn_cast<BlobAttr>(linkLib)) { 117*72e8b9aeSMehdi Amini // Load the file or abort on error. 118*72e8b9aeSMehdi Amini llvm::SMDiagnostic error; 119*72e8b9aeSMehdi Amini ArrayRef<char> data = blobAttr.getData(); 120*72e8b9aeSMehdi Amini std::unique_ptr<llvm::MemoryBuffer> buffer = 121*72e8b9aeSMehdi Amini llvm::MemoryBuffer::getMemBuffer(StringRef(data.data(), data.size()), 122*72e8b9aeSMehdi Amini "blobLinkedLib", 123*72e8b9aeSMehdi Amini /*RequiresNullTerminator=*/false); 124*72e8b9aeSMehdi Amini std::unique_ptr<llvm::Module> mod = 125*72e8b9aeSMehdi Amini getLazyIRModule(std::move(buffer), error, context); 126*72e8b9aeSMehdi Amini if (mod) { 127*72e8b9aeSMehdi Amini if (failed(handleBitcodeFile(*mod))) 128*72e8b9aeSMehdi Amini return failure(); 129*72e8b9aeSMehdi Amini llvmModules.push_back(std::move(mod)); 130*72e8b9aeSMehdi Amini } else if (failureOnError) { 131*72e8b9aeSMehdi Amini getOperation().emitError() 132*72e8b9aeSMehdi Amini << "Couldn't load LLVM library for linking: " << error.getMessage(); 133*72e8b9aeSMehdi Amini return failure(); 134*72e8b9aeSMehdi Amini } 135*72e8b9aeSMehdi Amini continue; 136*72e8b9aeSMehdi Amini } 137*72e8b9aeSMehdi Amini if (failureOnError) { 138*72e8b9aeSMehdi Amini getOperation().emitError() 139*72e8b9aeSMehdi Amini << "Unknown attribute describing LLVM library to load: " << linkLib; 140*72e8b9aeSMehdi Amini return failure(); 141*72e8b9aeSMehdi Amini } 142895c4ac3SFabian Mora } 143895c4ac3SFabian Mora return success(); 144895c4ac3SFabian Mora } 145895c4ac3SFabian Mora 146895c4ac3SFabian Mora std::unique_ptr<llvm::Module> 147895c4ac3SFabian Mora ModuleToObject::translateToLLVMIR(llvm::LLVMContext &llvmContext) { 148895c4ac3SFabian Mora return translateModuleToLLVMIR(&getOperation(), llvmContext); 149895c4ac3SFabian Mora } 150895c4ac3SFabian Mora 151895c4ac3SFabian Mora LogicalResult 152895c4ac3SFabian Mora ModuleToObject::linkFiles(llvm::Module &module, 153895c4ac3SFabian Mora SmallVector<std::unique_ptr<llvm::Module>> &&libs) { 154895c4ac3SFabian Mora if (libs.empty()) 155895c4ac3SFabian Mora return success(); 156895c4ac3SFabian Mora llvm::Linker linker(module); 157895c4ac3SFabian Mora for (std::unique_ptr<llvm::Module> &libModule : libs) { 158895c4ac3SFabian Mora // This bitcode linking imports the library functions into the module, 159895c4ac3SFabian Mora // allowing LLVM optimization passes (which must run after linking) to 160895c4ac3SFabian Mora // optimize across the libraries and the module's code. We also only import 161895c4ac3SFabian Mora // symbols if they are referenced by the module or a previous library since 162895c4ac3SFabian Mora // there will be no other source of references to those symbols in this 163895c4ac3SFabian Mora // compilation and since we don't want to bloat the resulting code object. 164895c4ac3SFabian Mora bool err = linker.linkInModule( 165895c4ac3SFabian Mora std::move(libModule), llvm::Linker::Flags::LinkOnlyNeeded, 166895c4ac3SFabian Mora [](llvm::Module &m, const StringSet<> &gvs) { 167895c4ac3SFabian Mora llvm::internalizeModule(m, [&gvs](const llvm::GlobalValue &gv) { 168895c4ac3SFabian Mora return !gv.hasName() || (gvs.count(gv.getName()) == 0); 169895c4ac3SFabian Mora }); 170895c4ac3SFabian Mora }); 171895c4ac3SFabian Mora // True is linker failure 172895c4ac3SFabian Mora if (err) { 173895c4ac3SFabian Mora getOperation().emitError("Unrecoverable failure during bitcode linking."); 174895c4ac3SFabian Mora // We have no guaranties about the state of `ret`, so bail 175895c4ac3SFabian Mora return failure(); 176895c4ac3SFabian Mora } 177895c4ac3SFabian Mora } 178895c4ac3SFabian Mora return success(); 179895c4ac3SFabian Mora } 180895c4ac3SFabian Mora 181895c4ac3SFabian Mora LogicalResult ModuleToObject::optimizeModule(llvm::Module &module, 182d9dadfdaSMehdi Amini 183895c4ac3SFabian Mora int optLevel) { 184895c4ac3SFabian Mora if (optLevel < 0 || optLevel > 3) 185895c4ac3SFabian Mora return getOperation().emitError() 186895c4ac3SFabian Mora << "Invalid optimization level: " << optLevel << "."; 187895c4ac3SFabian Mora 188d9dadfdaSMehdi Amini std::optional<llvm::TargetMachine *> targetMachine = 189d9dadfdaSMehdi Amini getOrCreateTargetMachine(); 190d9dadfdaSMehdi Amini if (!targetMachine) 191d9dadfdaSMehdi Amini return getOperation().emitError() 192d9dadfdaSMehdi Amini << "Target Machine unavailable for triple " << triple 193d9dadfdaSMehdi Amini << ", can't optimize with LLVM\n"; 194d9dadfdaSMehdi Amini (*targetMachine)->setOptLevel(static_cast<llvm::CodeGenOptLevel>(optLevel)); 195895c4ac3SFabian Mora 196895c4ac3SFabian Mora auto transformer = 197d9dadfdaSMehdi Amini makeOptimizingTransformer(optLevel, /*sizeLevel=*/0, *targetMachine); 198895c4ac3SFabian Mora auto error = transformer(&module); 199895c4ac3SFabian Mora if (error) { 200895c4ac3SFabian Mora InFlightDiagnostic mlirError = getOperation().emitError(); 201895c4ac3SFabian Mora llvm::handleAllErrors( 202895c4ac3SFabian Mora std::move(error), [&mlirError](const llvm::ErrorInfoBase &ei) { 203895c4ac3SFabian Mora mlirError << "Could not optimize LLVM IR: " << ei.message() << "\n"; 204895c4ac3SFabian Mora }); 205895c4ac3SFabian Mora return mlirError; 206895c4ac3SFabian Mora } 207895c4ac3SFabian Mora return success(); 208895c4ac3SFabian Mora } 209895c4ac3SFabian Mora 210895c4ac3SFabian Mora std::optional<std::string> 211895c4ac3SFabian Mora ModuleToObject::translateToISA(llvm::Module &llvmModule, 212895c4ac3SFabian Mora llvm::TargetMachine &targetMachine) { 213895c4ac3SFabian Mora std::string targetISA; 214895c4ac3SFabian Mora llvm::raw_string_ostream stream(targetISA); 215895c4ac3SFabian Mora 216895c4ac3SFabian Mora { // Drop pstream after this to prevent the ISA from being stuck buffering 217895c4ac3SFabian Mora llvm::buffer_ostream pstream(stream); 218895c4ac3SFabian Mora llvm::legacy::PassManager codegenPasses; 219895c4ac3SFabian Mora 220895c4ac3SFabian Mora if (targetMachine.addPassesToEmitFile(codegenPasses, pstream, nullptr, 2210a1aa6cdSArthur Eubanks llvm::CodeGenFileType::AssemblyFile)) 222895c4ac3SFabian Mora return std::nullopt; 223895c4ac3SFabian Mora 224895c4ac3SFabian Mora codegenPasses.run(llvmModule); 225895c4ac3SFabian Mora } 226095b41c6SJOE1994 return targetISA; 227895c4ac3SFabian Mora } 228895c4ac3SFabian Mora 229d9dadfdaSMehdi Amini void ModuleToObject::setDataLayoutAndTriple(llvm::Module &module) { 230d9dadfdaSMehdi Amini // Create the target machine. 231d9dadfdaSMehdi Amini std::optional<llvm::TargetMachine *> targetMachine = 232d9dadfdaSMehdi Amini getOrCreateTargetMachine(); 233d9dadfdaSMehdi Amini if (targetMachine) { 234d9dadfdaSMehdi Amini // Set the data layout and target triple of the module. 235d9dadfdaSMehdi Amini module.setDataLayout((*targetMachine)->createDataLayout()); 236d9dadfdaSMehdi Amini module.setTargetTriple((*targetMachine)->getTargetTriple().getTriple()); 237d9dadfdaSMehdi Amini } 238d9dadfdaSMehdi Amini } 239d9dadfdaSMehdi Amini 240895c4ac3SFabian Mora std::optional<SmallVector<char, 0>> 241d9dadfdaSMehdi Amini ModuleToObject::moduleToObject(llvm::Module &llvmModule) { 242895c4ac3SFabian Mora SmallVector<char, 0> binaryData; 243895c4ac3SFabian Mora // Write the LLVM module bitcode to a buffer. 244895c4ac3SFabian Mora llvm::raw_svector_ostream outputStream(binaryData); 245895c4ac3SFabian Mora llvm::WriteBitcodeToFile(llvmModule, outputStream); 246895c4ac3SFabian Mora return binaryData; 247895c4ac3SFabian Mora } 248895c4ac3SFabian Mora 249895c4ac3SFabian Mora std::optional<SmallVector<char, 0>> ModuleToObject::run() { 250895c4ac3SFabian Mora // Translate the module to LLVM IR. 251895c4ac3SFabian Mora llvm::LLVMContext llvmContext; 252895c4ac3SFabian Mora std::unique_ptr<llvm::Module> llvmModule = translateToLLVMIR(llvmContext); 253895c4ac3SFabian Mora if (!llvmModule) { 254895c4ac3SFabian Mora getOperation().emitError() << "Failed creating the llvm::Module."; 255895c4ac3SFabian Mora return std::nullopt; 256895c4ac3SFabian Mora } 257d9dadfdaSMehdi Amini setDataLayoutAndTriple(*llvmModule); 258895c4ac3SFabian Mora 25908e76096SZichen Lu if (initialLlvmIRCallback) 26008e76096SZichen Lu initialLlvmIRCallback(*llvmModule); 26108e76096SZichen Lu 262895c4ac3SFabian Mora // Link bitcode files. 263d9dadfdaSMehdi Amini handleModulePreLink(*llvmModule); 264895c4ac3SFabian Mora { 265d9dadfdaSMehdi Amini auto libs = loadBitcodeFiles(*llvmModule); 266895c4ac3SFabian Mora if (!libs) 267895c4ac3SFabian Mora return std::nullopt; 268cad26783SJacques Pienaar if (!libs->empty()) 269895c4ac3SFabian Mora if (failed(linkFiles(*llvmModule, std::move(*libs)))) 270895c4ac3SFabian Mora return std::nullopt; 271d9dadfdaSMehdi Amini handleModulePostLink(*llvmModule); 272895c4ac3SFabian Mora } 273895c4ac3SFabian Mora 27408e76096SZichen Lu if (linkedLlvmIRCallback) 27508e76096SZichen Lu linkedLlvmIRCallback(*llvmModule); 27608e76096SZichen Lu 277895c4ac3SFabian Mora // Optimize the module. 278d9dadfdaSMehdi Amini if (failed(optimizeModule(*llvmModule, optLevel))) 279895c4ac3SFabian Mora return std::nullopt; 280895c4ac3SFabian Mora 28108e76096SZichen Lu if (optimizedLlvmIRCallback) 28208e76096SZichen Lu optimizedLlvmIRCallback(*llvmModule); 28308e76096SZichen Lu 284895c4ac3SFabian Mora // Return the serialized object. 285d9dadfdaSMehdi Amini return moduleToObject(*llvmModule); 286895c4ac3SFabian Mora } 287