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