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