MLIR 22.0.0git
ModuleToObject.cpp
Go to the documentation of this file.
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
15
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
48
50
52
53FailureOr<llvm::TargetMachine *> ModuleToObject::getOrCreateTargetMachine() {
54 if (targetMachine)
55 return targetMachine.get();
56 // Load the target.
57 std::string error;
58 llvm::Triple parsedTriple(triple);
59 const llvm::Target *target =
60 llvm::TargetRegistry::lookupTarget(parsedTriple, error);
61 if (!target)
62 return getOperation().emitError()
63 << "Failed to lookup target for triple '" << triple << "' " << error;
64
65 // Create the target machine using the target.
66 targetMachine.reset(
67 target->createTargetMachine(parsedTriple, chip, features, {}, {}));
68 if (!targetMachine)
69 return getOperation().emitError()
70 << "Failed to create target machine for triple '" << triple << "'";
71
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(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
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>(linkLib)) {
100 // Test if the path exists, if it doesn't abort.
101 if (!llvm::sys::fs::is_regular_file(filePath.strref())) {
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, filePath))
108 llvmModules.push_back(std::move(bcFile));
109 else if (failureOnError)
110 return failure();
111 continue;
112 }
113 if (auto blobAttr = dyn_cast<BlobAttr>(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(StringRef(data.data(), data.size()),
119 "blobLinkedLib",
120 /*RequiresNullTerminator=*/false);
121 std::unique_ptr<llvm::Module> mod =
122 getLazyIRModule(std::move(buffer), error, context);
123 if (mod) {
124 if (failed(handleBitcodeFile(*mod)))
125 return failure();
126 llvmModules.push_back(std::move(mod));
127 } else if (failureOnError) {
129 << "Couldn't load LLVM library for linking: " << error.getMessage();
130 return failure();
131 }
132 continue;
133 }
134 if (failureOnError) {
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(&getOperation(), llvmContext);
146}
147
148LogicalResult
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 std::move(libModule), llvm::Linker::Flags::LinkOnlyNeeded,
163 [](llvm::Module &m, const StringSet<> &gvs) {
164 llvm::internalizeModule(m, [&gvs](const llvm::GlobalValue &gv) {
165 return !gv.hasName() || (gvs.count(gv.getName()) == 0);
166 });
167 });
168 // True is linker failure
169 if (err) {
170 getOperation().emitError("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) {
182 return getOperation().emitError()
183 << "Invalid optimization level: " << optLevel << ".";
184
185 FailureOr<llvm::TargetMachine *> targetMachine = getOrCreateTargetMachine();
186 if (failed(targetMachine))
187 return getOperation().emitError()
188 << "Target Machine unavailable for triple " << triple
189 << ", can't optimize with LLVM\n";
190 (*targetMachine)->setOptLevel(static_cast<llvm::CodeGenOptLevel>(optLevel));
191
192 auto transformer =
193 makeOptimizingTransformer(optLevel, /*sizeLevel=*/0, *targetMachine);
194 auto error = transformer(&module);
195 if (error) {
197 llvm::handleAllErrors(
198 std::move(error), [&mlirError](const llvm::ErrorInfoBase &ei) {
199 mlirError << "Could not optimize LLVM IR: " << ei.message() << "\n";
200 });
201 return mlirError;
202 }
203 return success();
204}
205
206FailureOr<SmallString<0>> ModuleToObject::translateModuleToISA(
207 llvm::Module &llvmModule, llvm::TargetMachine &targetMachine,
209 SmallString<0> targetISA;
210 llvm::raw_svector_ostream stream(targetISA);
211
212 { // Drop pstream after this to prevent the ISA from being stuck buffering
213 llvm::buffer_ostream pstream(stream);
214 llvm::legacy::PassManager codegenPasses;
215
216 if (targetMachine.addPassesToEmitFile(codegenPasses, pstream, nullptr,
217 llvm::CodeGenFileType::AssemblyFile))
218 return emitError() << "Target machine cannot emit assembly";
219
220 codegenPasses.run(llvmModule);
221 }
222 return targetISA;
223}
224
226 // Create the target machine.
227 FailureOr<llvm::TargetMachine *> targetMachine = getOrCreateTargetMachine();
228 if (failed(targetMachine))
229 return;
230
231 // Set the data layout and target triple of the module.
232 module.setDataLayout((*targetMachine)->createDataLayout());
233 module.setTargetTriple((*targetMachine)->getTargetTriple());
234}
235
236FailureOr<SmallVector<char, 0>>
237ModuleToObject::moduleToObject(llvm::Module &llvmModule) {
238 SmallVector<char, 0> binaryData;
239 // Write the LLVM module bitcode to a buffer.
240 llvm::raw_svector_ostream outputStream(binaryData);
241 llvm::WriteBitcodeToFile(llvmModule, outputStream);
242 return binaryData;
243}
244
245std::optional<SmallVector<char, 0>> ModuleToObject::run() {
246 // Translate the module to LLVM IR.
247 llvm::LLVMContext llvmContext;
248 std::unique_ptr<llvm::Module> llvmModule = translateToLLVMIR(llvmContext);
249 if (!llvmModule) {
250 getOperation().emitError() << "Failed creating the llvm::Module.";
251 return std::nullopt;
252 }
253 setDataLayoutAndTriple(*llvmModule);
254
256 initialLlvmIRCallback(*llvmModule);
257
258 // Link bitcode files.
259 handleModulePreLink(*llvmModule);
260 {
261 auto libs = loadBitcodeFiles(*llvmModule);
262 if (!libs)
263 return std::nullopt;
264 if (!libs->empty())
265 if (failed(linkFiles(*llvmModule, std::move(*libs))))
266 return std::nullopt;
267 handleModulePostLink(*llvmModule);
268 }
269
271 linkedLlvmIRCallback(*llvmModule);
272
273 // Optimize the module.
274 if (failed(optimizeModule(*llvmModule, optLevel)))
275 return std::nullopt;
276
278 optimizedLlvmIRCallback(*llvmModule);
279
280 // Return the serialized object.
281 return moduleToObject(*llvmModule);
282}
return success()
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class represents a diagnostic that is inflight and set to be reported.
LogicalResult loadBitcodeFilesFromList(llvm::LLVMContext &context, ArrayRef< Attribute > librariesToLink, SmallVector< std::unique_ptr< llvm::Module > > &llvmModules, bool failureOnError=true)
Loads multiple bitcode files.
FailureOr< llvm::TargetMachine * > getOrCreateTargetMachine()
Create the target machine based on the target triple and chip.
StringRef features
Target features.
std::unique_ptr< llvm::Module > translateToLLVMIR(llvm::LLVMContext &llvmContext)
Translates the operation to LLVM IR.
virtual std::optional< SmallVector< char, 0 > > run()
Runs the serialization pipeline, returning std::nullopt on error.
function_ref< void(llvm::Module &)> initialLlvmIRCallback
Callback invoked with the initial LLVM IR for the device module.
function_ref< void(llvm::Module &)> optimizedLlvmIRCallback
Callback invoked with LLVM IR for the device module after LLVM optimizations but before codegen.
virtual void setDataLayoutAndTriple(llvm::Module &module)
Hook for computing the Datalayout.
virtual FailureOr< SmallVector< char, 0 > > moduleToObject(llvm::Module &llvmModule)
Serializes the LLVM IR bitcode to an object file, by default it serializes to LLVM bitcode.
virtual void handleModulePreLink(llvm::Module &module)
Hook for performing additional actions on the llvmModule pre linking.
StringRef triple
Target triple.
int optLevel
Optimization level.
virtual LogicalResult handleBitcodeFile(llvm::Module &module)
Hook for performing additional actions on a loaded bitcode file.
Operation & getOperation()
Returns the operation being serialized.
LogicalResult linkFiles(llvm::Module &module, SmallVector< std::unique_ptr< llvm::Module > > &&libs)
Link the llvmModule to other bitcode file.
function_ref< void(StringRef)> isaCallback
Callback invoked with the target ISA for the device, for example PTX assembly.
virtual std::optional< SmallVector< std::unique_ptr< llvm::Module > > > loadBitcodeFiles(llvm::Module &module)
Hook for loading bitcode files, returns std::nullopt on failure.
static FailureOr< SmallString< 0 > > translateModuleToISA(llvm::Module &llvmModule, llvm::TargetMachine &targetMachine, function_ref< InFlightDiagnostic()> emitError)
Translate LLVM module to textual ISA.
virtual LogicalResult optimizeModule(llvm::Module &module, int optL)
Optimize the module.
function_ref< void(llvm::Module &)> linkedLlvmIRCallback
Callback invoked with LLVM IR for the device module after linking the device libraries.
StringRef chip
Target chip.
std::unique_ptr< llvm::Module > loadBitcodeFile(llvm::LLVMContext &context, StringRef path)
Loads a bitcode file from path.
virtual void handleModulePostLink(llvm::Module &module)
Hook for performing additional actions on the llvmModule post linking.
ModuleToObject(Operation &module, StringRef triple, StringRef chip, StringRef features={}, int optLevel=3, function_ref< void(llvm::Module &)> initialLlvmIRCallback={}, function_ref< void(llvm::Module &)> linkedLlvmIRCallback={}, function_ref< void(llvm::Module &)> optimizedLlvmIRCallback={}, function_ref< void(StringRef)> isaCallback={})
Operation & module
Module to transform to a binary object.
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Include the generated interface declarations.
std::function< llvm::Error(llvm::Module *)> makeOptimizingTransformer(unsigned optLevel, unsigned sizeLevel, llvm::TargetMachine *targetMachine)
Create a module transformer function for MLIR ExecutionEngine that runs LLVM IR passes corresponding ...
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
std::unique_ptr< llvm::Module > translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext, llvm::StringRef name="LLVMDialectModule", bool disableVerification=false)
Translates a given LLVM dialect module into an LLVM IR module living in the given context.
llvm::StringSet< AllocatorTy > StringSet
Definition LLVM.h:133
llvm::function_ref< Fn > function_ref
Definition LLVM.h:152