xref: /llvm-project/mlir/lib/Target/LLVM/ModuleToObject.cpp (revision 72e8b9aeaa3f584f223bc59924812df69a09a48b)
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