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