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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file implements the base class for transforming Operations into binary 10 // objects. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "mlir/Target/LLVM/ModuleToObject.h" 15 16 #include "mlir/ExecutionEngine/OptUtils.h" 17 #include "mlir/IR/BuiltinOps.h" 18 #include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.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/Path.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 34 using namespace mlir; 35 using namespace mlir::LLVM; 36 37 ModuleToObject::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 49 ModuleToObject::~ModuleToObject() = default; 50 51 Operation &ModuleToObject::getOperation() { return module; } 52 53 std::optional<llvm::TargetMachine *> 54 ModuleToObject::getOrCreateTargetMachine() { 55 if (targetMachine) 56 return targetMachine.get(); 57 // Load the target. 58 std::string error; 59 const llvm::Target *target = 60 llvm::TargetRegistry::lookupTarget(triple, error); 61 if (!target) { 62 getOperation().emitError() 63 << "Failed to lookup target for triple '" << triple << "' " << error; 64 return std::nullopt; 65 } 66 67 // Create the target machine using the target. 68 targetMachine.reset( 69 target->createTargetMachine(triple, chip, features, {}, {})); 70 if (!targetMachine) 71 return std::nullopt; 72 return targetMachine.get(); 73 } 74 75 std::unique_ptr<llvm::Module> 76 ModuleToObject::loadBitcodeFile(llvm::LLVMContext &context, StringRef path) { 77 llvm::SMDiagnostic error; 78 std::unique_ptr<llvm::Module> library = 79 llvm::getLazyIRFileModule(path, error, context); 80 if (!library) { 81 getOperation().emitError() << "Failed loading file from " << path 82 << ", error: " << error.getMessage(); 83 return nullptr; 84 } 85 if (failed(handleBitcodeFile(*library))) { 86 return nullptr; 87 } 88 return library; 89 } 90 91 LogicalResult ModuleToObject::loadBitcodeFilesFromList( 92 llvm::LLVMContext &context, ArrayRef<std::string> fileList, 93 SmallVector<std::unique_ptr<llvm::Module>> &llvmModules, 94 bool failureOnError) { 95 for (const std::string &str : fileList) { 96 // Test if the path exists, if it doesn't abort. 97 StringRef pathRef = StringRef(str.data(), str.size()); 98 if (!llvm::sys::fs::is_regular_file(pathRef)) { 99 getOperation().emitError() 100 << "File path: " << pathRef << " does not exist or is not a file.\n"; 101 return failure(); 102 } 103 // Load the file or abort on error. 104 if (auto bcFile = loadBitcodeFile(context, pathRef)) 105 llvmModules.push_back(std::move(bcFile)); 106 else if (failureOnError) 107 return failure(); 108 } 109 return success(); 110 } 111 112 std::unique_ptr<llvm::Module> 113 ModuleToObject::translateToLLVMIR(llvm::LLVMContext &llvmContext) { 114 return translateModuleToLLVMIR(&getOperation(), llvmContext); 115 } 116 117 LogicalResult 118 ModuleToObject::linkFiles(llvm::Module &module, 119 SmallVector<std::unique_ptr<llvm::Module>> &&libs) { 120 if (libs.empty()) 121 return success(); 122 llvm::Linker linker(module); 123 for (std::unique_ptr<llvm::Module> &libModule : libs) { 124 // This bitcode linking imports the library functions into the module, 125 // allowing LLVM optimization passes (which must run after linking) to 126 // optimize across the libraries and the module's code. We also only import 127 // symbols if they are referenced by the module or a previous library since 128 // there will be no other source of references to those symbols in this 129 // compilation and since we don't want to bloat the resulting code object. 130 bool err = linker.linkInModule( 131 std::move(libModule), llvm::Linker::Flags::LinkOnlyNeeded, 132 [](llvm::Module &m, const StringSet<> &gvs) { 133 llvm::internalizeModule(m, [&gvs](const llvm::GlobalValue &gv) { 134 return !gv.hasName() || (gvs.count(gv.getName()) == 0); 135 }); 136 }); 137 // True is linker failure 138 if (err) { 139 getOperation().emitError("Unrecoverable failure during bitcode linking."); 140 // We have no guaranties about the state of `ret`, so bail 141 return failure(); 142 } 143 } 144 return success(); 145 } 146 147 LogicalResult ModuleToObject::optimizeModule(llvm::Module &module, 148 149 int optLevel) { 150 if (optLevel < 0 || optLevel > 3) 151 return getOperation().emitError() 152 << "Invalid optimization level: " << optLevel << "."; 153 154 std::optional<llvm::TargetMachine *> targetMachine = 155 getOrCreateTargetMachine(); 156 if (!targetMachine) 157 return getOperation().emitError() 158 << "Target Machine unavailable for triple " << triple 159 << ", can't optimize with LLVM\n"; 160 (*targetMachine)->setOptLevel(static_cast<llvm::CodeGenOptLevel>(optLevel)); 161 162 auto transformer = 163 makeOptimizingTransformer(optLevel, /*sizeLevel=*/0, *targetMachine); 164 auto error = transformer(&module); 165 if (error) { 166 InFlightDiagnostic mlirError = getOperation().emitError(); 167 llvm::handleAllErrors( 168 std::move(error), [&mlirError](const llvm::ErrorInfoBase &ei) { 169 mlirError << "Could not optimize LLVM IR: " << ei.message() << "\n"; 170 }); 171 return mlirError; 172 } 173 return success(); 174 } 175 176 std::optional<std::string> 177 ModuleToObject::translateToISA(llvm::Module &llvmModule, 178 llvm::TargetMachine &targetMachine) { 179 std::string targetISA; 180 llvm::raw_string_ostream stream(targetISA); 181 182 { // Drop pstream after this to prevent the ISA from being stuck buffering 183 llvm::buffer_ostream pstream(stream); 184 llvm::legacy::PassManager codegenPasses; 185 186 if (targetMachine.addPassesToEmitFile(codegenPasses, pstream, nullptr, 187 llvm::CodeGenFileType::AssemblyFile)) 188 return std::nullopt; 189 190 codegenPasses.run(llvmModule); 191 } 192 return targetISA; 193 } 194 195 void ModuleToObject::setDataLayoutAndTriple(llvm::Module &module) { 196 // Create the target machine. 197 std::optional<llvm::TargetMachine *> targetMachine = 198 getOrCreateTargetMachine(); 199 if (targetMachine) { 200 // Set the data layout and target triple of the module. 201 module.setDataLayout((*targetMachine)->createDataLayout()); 202 module.setTargetTriple((*targetMachine)->getTargetTriple().getTriple()); 203 } 204 } 205 206 std::optional<SmallVector<char, 0>> 207 ModuleToObject::moduleToObject(llvm::Module &llvmModule) { 208 SmallVector<char, 0> binaryData; 209 // Write the LLVM module bitcode to a buffer. 210 llvm::raw_svector_ostream outputStream(binaryData); 211 llvm::WriteBitcodeToFile(llvmModule, outputStream); 212 return binaryData; 213 } 214 215 std::optional<SmallVector<char, 0>> ModuleToObject::run() { 216 // Translate the module to LLVM IR. 217 llvm::LLVMContext llvmContext; 218 std::unique_ptr<llvm::Module> llvmModule = translateToLLVMIR(llvmContext); 219 if (!llvmModule) { 220 getOperation().emitError() << "Failed creating the llvm::Module."; 221 return std::nullopt; 222 } 223 setDataLayoutAndTriple(*llvmModule); 224 225 if (initialLlvmIRCallback) 226 initialLlvmIRCallback(*llvmModule); 227 228 // Link bitcode files. 229 handleModulePreLink(*llvmModule); 230 { 231 auto libs = loadBitcodeFiles(*llvmModule); 232 if (!libs) 233 return std::nullopt; 234 if (!libs->empty()) 235 if (failed(linkFiles(*llvmModule, std::move(*libs)))) 236 return std::nullopt; 237 handleModulePostLink(*llvmModule); 238 } 239 240 if (linkedLlvmIRCallback) 241 linkedLlvmIRCallback(*llvmModule); 242 243 // Optimize the module. 244 if (failed(optimizeModule(*llvmModule, optLevel))) 245 return std::nullopt; 246 247 if (optimizedLlvmIRCallback) 248 optimizedLlvmIRCallback(*llvmModule); 249 250 // Return the serialized object. 251 return moduleToObject(*llvmModule); 252 } 253