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/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 const llvm::Target *target =
60 llvm::TargetRegistry::lookupTarget(TripleStr: triple, Error&: 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(p: target->createTargetMachine(TT: llvm::Triple(triple), CPU: chip,
69 Features: features, Options: {}, RM: {}));
70 if (!targetMachine)
71 return std::nullopt;
72 return targetMachine.get();
73}
74
75std::unique_ptr<llvm::Module>
76ModuleToObject::loadBitcodeFile(llvm::LLVMContext &context, StringRef path) {
77 llvm::SMDiagnostic error;
78 std::unique_ptr<llvm::Module> library =
79 llvm::getLazyIRFileModule(Filename: path, Err&: error, Context&: context);
80 if (!library) {
81 getOperation().emitError() << "Failed loading file from " << path
82 << ", error: " << error.getMessage();
83 return nullptr;
84 }
85 if (failed(Result: handleBitcodeFile(module&: *library))) {
86 return nullptr;
87 }
88 return library;
89}
90
91LogicalResult ModuleToObject::loadBitcodeFilesFromList(
92 llvm::LLVMContext &context, ArrayRef<Attribute> librariesToLink,
93 SmallVector<std::unique_ptr<llvm::Module>> &llvmModules,
94 bool failureOnError) {
95 for (Attribute linkLib : librariesToLink) {
96 // Attributes in this list can be either list of file paths using
97 // StringAttr, or a resource attribute pointing to the LLVM bitcode in
98 // memory.
99 if (auto filePath = dyn_cast<StringAttr>(Val&: linkLib)) {
100 // Test if the path exists, if it doesn't abort.
101 if (!llvm::sys::fs::is_regular_file(Path: filePath.strref())) {
102 getOperation().emitError()
103 << "File path: " << filePath << " does not exist or is not a file.";
104 return failure();
105 }
106 // Load the file or abort on error.
107 if (auto bcFile = loadBitcodeFile(context, path: filePath))
108 llvmModules.push_back(Elt: std::move(bcFile));
109 else if (failureOnError)
110 return failure();
111 continue;
112 }
113 if (auto blobAttr = dyn_cast<BlobAttr>(Val&: linkLib)) {
114 // Load the file or abort on error.
115 llvm::SMDiagnostic error;
116 ArrayRef<char> data = blobAttr.getData();
117 std::unique_ptr<llvm::MemoryBuffer> buffer =
118 llvm::MemoryBuffer::getMemBuffer(InputData: StringRef(data.data(), data.size()),
119 BufferName: "blobLinkedLib",
120 /*RequiresNullTerminator=*/false);
121 std::unique_ptr<llvm::Module> mod =
122 getLazyIRModule(Buffer: std::move(buffer), Err&: error, Context&: context);
123 if (mod) {
124 if (failed(Result: handleBitcodeFile(module&: *mod)))
125 return failure();
126 llvmModules.push_back(Elt: std::move(mod));
127 } else if (failureOnError) {
128 getOperation().emitError()
129 << "Couldn't load LLVM library for linking: " << error.getMessage();
130 return failure();
131 }
132 continue;
133 }
134 if (failureOnError) {
135 getOperation().emitError()
136 << "Unknown attribute describing LLVM library to load: " << linkLib;
137 return failure();
138 }
139 }
140 return success();
141}
142
143std::unique_ptr<llvm::Module>
144ModuleToObject::translateToLLVMIR(llvm::LLVMContext &llvmContext) {
145 return translateModuleToLLVMIR(module: &getOperation(), llvmContext);
146}
147
148LogicalResult
149ModuleToObject::linkFiles(llvm::Module &module,
150 SmallVector<std::unique_ptr<llvm::Module>> &&libs) {
151 if (libs.empty())
152 return success();
153 llvm::Linker linker(module);
154 for (std::unique_ptr<llvm::Module> &libModule : libs) {
155 // This bitcode linking imports the library functions into the module,
156 // allowing LLVM optimization passes (which must run after linking) to
157 // optimize across the libraries and the module's code. We also only import
158 // symbols if they are referenced by the module or a previous library since
159 // there will be no other source of references to those symbols in this
160 // compilation and since we don't want to bloat the resulting code object.
161 bool err = linker.linkInModule(
162 Src: std::move(libModule), Flags: llvm::Linker::Flags::LinkOnlyNeeded,
163 InternalizeCallback: [](llvm::Module &m, const StringSet<> &gvs) {
164 llvm::internalizeModule(TheModule&: m, MustPreserveGV: [&gvs](const llvm::GlobalValue &gv) {
165 return !gv.hasName() || (gvs.count(Key: gv.getName()) == 0);
166 });
167 });
168 // True is linker failure
169 if (err) {
170 getOperation().emitError(message: "Unrecoverable failure during bitcode linking.");
171 // We have no guaranties about the state of `ret`, so bail
172 return failure();
173 }
174 }
175 return success();
176}
177
178LogicalResult ModuleToObject::optimizeModule(llvm::Module &module,
179
180 int optLevel) {
181 if (optLevel < 0 || optLevel > 3)
182 return getOperation().emitError()
183 << "Invalid optimization level: " << optLevel << ".";
184
185 std::optional<llvm::TargetMachine *> targetMachine =
186 getOrCreateTargetMachine();
187 if (!targetMachine)
188 return getOperation().emitError()
189 << "Target Machine unavailable for triple " << triple
190 << ", can't optimize with LLVM\n";
191 (*targetMachine)->setOptLevel(static_cast<llvm::CodeGenOptLevel>(optLevel));
192
193 auto transformer =
194 makeOptimizingTransformer(optLevel, /*sizeLevel=*/0, targetMachine: *targetMachine);
195 auto error = transformer(&module);
196 if (error) {
197 InFlightDiagnostic mlirError = getOperation().emitError();
198 llvm::handleAllErrors(
199 E: std::move(error), Handlers: [&mlirError](const llvm::ErrorInfoBase &ei) {
200 mlirError << "Could not optimize LLVM IR: " << ei.message() << "\n";
201 });
202 return mlirError;
203 }
204 return success();
205}
206
207std::optional<std::string>
208ModuleToObject::translateToISA(llvm::Module &llvmModule,
209 llvm::TargetMachine &targetMachine) {
210 std::string targetISA;
211 llvm::raw_string_ostream stream(targetISA);
212
213 { // Drop pstream after this to prevent the ISA from being stuck buffering
214 llvm::buffer_ostream pstream(stream);
215 llvm::legacy::PassManager codegenPasses;
216
217 if (targetMachine.addPassesToEmitFile(codegenPasses, pstream, nullptr,
218 llvm::CodeGenFileType::AssemblyFile))
219 return std::nullopt;
220
221 codegenPasses.run(M&: llvmModule);
222 }
223 return targetISA;
224}
225
226void ModuleToObject::setDataLayoutAndTriple(llvm::Module &module) {
227 // Create the target machine.
228 std::optional<llvm::TargetMachine *> targetMachine =
229 getOrCreateTargetMachine();
230 if (targetMachine) {
231 // Set the data layout and target triple of the module.
232 module.setDataLayout((*targetMachine)->createDataLayout());
233 module.setTargetTriple((*targetMachine)->getTargetTriple());
234 }
235}
236
237std::optional<SmallVector<char, 0>>
238ModuleToObject::moduleToObject(llvm::Module &llvmModule) {
239 SmallVector<char, 0> binaryData;
240 // Write the LLVM module bitcode to a buffer.
241 llvm::raw_svector_ostream outputStream(binaryData);
242 llvm::WriteBitcodeToFile(M: llvmModule, Out&: outputStream);
243 return binaryData;
244}
245
246std::optional<SmallVector<char, 0>> ModuleToObject::run() {
247 // Translate the module to LLVM IR.
248 llvm::LLVMContext llvmContext;
249 std::unique_ptr<llvm::Module> llvmModule = translateToLLVMIR(llvmContext);
250 if (!llvmModule) {
251 getOperation().emitError() << "Failed creating the llvm::Module.";
252 return std::nullopt;
253 }
254 setDataLayoutAndTriple(*llvmModule);
255
256 if (initialLlvmIRCallback)
257 initialLlvmIRCallback(*llvmModule);
258
259 // Link bitcode files.
260 handleModulePreLink(module&: *llvmModule);
261 {
262 auto libs = loadBitcodeFiles(module&: *llvmModule);
263 if (!libs)
264 return std::nullopt;
265 if (!libs->empty())
266 if (failed(Result: linkFiles(module&: *llvmModule, libs: std::move(*libs))))
267 return std::nullopt;
268 handleModulePostLink(module&: *llvmModule);
269 }
270
271 if (linkedLlvmIRCallback)
272 linkedLlvmIRCallback(*llvmModule);
273
274 // Optimize the module.
275 if (failed(Result: optimizeModule(module&: *llvmModule, optLevel)))
276 return std::nullopt;
277
278 if (optimizedLlvmIRCallback)
279 optimizedLlvmIRCallback(*llvmModule);
280
281 // Return the serialized object.
282 return moduleToObject(llvmModule&: *llvmModule);
283}
284

source code of mlir/lib/Target/LLVM/ModuleToObject.cpp