xref: /llvm-project/mlir/lib/Target/LLVM/ModuleToObject.cpp (revision c8e0364a4336569f91fe74dbc231beefc8793518)
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/TargetParser/TargetParser.h"
33 #include "llvm/Transforms/IPO/Internalize.h"
34 
35 using namespace mlir;
36 using namespace mlir::LLVM;
37 
38 ModuleToObject::ModuleToObject(Operation &module, StringRef triple,
39                                StringRef chip, StringRef features, int optLevel)
40     : module(module), triple(triple), chip(chip), features(features),
41       optLevel(optLevel) {}
42 
43 Operation &ModuleToObject::getOperation() { return module; }
44 
45 std::unique_ptr<llvm::TargetMachine> ModuleToObject::createTargetMachine() {
46   std::string error;
47   // Load the target.
48   const llvm::Target *target =
49       llvm::TargetRegistry::lookupTarget(triple, error);
50   if (!target) {
51     getOperation().emitError() << "Failed to lookup target: " << error;
52     return {};
53   }
54 
55   // Create the target machine using the target.
56   llvm::TargetMachine *machine =
57       target->createTargetMachine(triple, chip, features, {}, {});
58   if (!machine) {
59     getOperation().emitError() << "Failed to create the target machine.";
60     return {};
61   }
62   return std::unique_ptr<llvm::TargetMachine>{machine};
63 }
64 
65 std::unique_ptr<llvm::Module>
66 ModuleToObject::loadBitcodeFile(llvm::LLVMContext &context,
67                                 llvm::TargetMachine &targetMachine,
68                                 StringRef path) {
69   llvm::SMDiagnostic error;
70   std::unique_ptr<llvm::Module> library =
71       llvm::getLazyIRFileModule(path, error, context);
72   if (!library) {
73     getOperation().emitError() << "Failed loading file from " << path
74                                << ", error: " << error.getMessage();
75     return nullptr;
76   }
77   if (failed(handleBitcodeFile(*library, targetMachine))) {
78     return nullptr;
79   }
80   return library;
81 }
82 
83 LogicalResult ModuleToObject::loadBitcodeFilesFromList(
84     llvm::LLVMContext &context, llvm::TargetMachine &targetMachine,
85     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, targetMachine, 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                                              llvm::TargetMachine &targetMachine,
142                                              int optLevel) {
143   if (optLevel < 0 || optLevel > 3)
144     return getOperation().emitError()
145            << "Invalid optimization level: " << optLevel << ".";
146 
147   targetMachine.setOptLevel(static_cast<llvm::CodeGenOpt::Level>(optLevel));
148 
149   auto transformer =
150       makeOptimizingTransformer(optLevel, /*sizeLevel=*/0, &targetMachine);
151   auto error = transformer(&module);
152   if (error) {
153     InFlightDiagnostic mlirError = getOperation().emitError();
154     llvm::handleAllErrors(
155         std::move(error), [&mlirError](const llvm::ErrorInfoBase &ei) {
156           mlirError << "Could not optimize LLVM IR: " << ei.message() << "\n";
157         });
158     return mlirError;
159   }
160   return success();
161 }
162 
163 std::optional<std::string>
164 ModuleToObject::translateToISA(llvm::Module &llvmModule,
165                                llvm::TargetMachine &targetMachine) {
166   std::string targetISA;
167   llvm::raw_string_ostream stream(targetISA);
168 
169   { // Drop pstream after this to prevent the ISA from being stuck buffering
170     llvm::buffer_ostream pstream(stream);
171     llvm::legacy::PassManager codegenPasses;
172 
173     if (targetMachine.addPassesToEmitFile(codegenPasses, pstream, nullptr,
174                                           llvm::CGFT_AssemblyFile))
175       return std::nullopt;
176 
177     codegenPasses.run(llvmModule);
178   }
179   return stream.str();
180 }
181 
182 std::optional<SmallVector<char, 0>>
183 ModuleToObject::moduleToObject(llvm::Module &llvmModule,
184                                llvm::TargetMachine &targetMachine) {
185   SmallVector<char, 0> binaryData;
186   // Write the LLVM module bitcode to a buffer.
187   llvm::raw_svector_ostream outputStream(binaryData);
188   llvm::WriteBitcodeToFile(llvmModule, outputStream);
189   return binaryData;
190 }
191 
192 std::optional<SmallVector<char, 0>> ModuleToObject::run() {
193   // Translate the module to LLVM IR.
194   llvm::LLVMContext llvmContext;
195   std::unique_ptr<llvm::Module> llvmModule = translateToLLVMIR(llvmContext);
196   if (!llvmModule) {
197     getOperation().emitError() << "Failed creating the llvm::Module.";
198     return std::nullopt;
199   }
200 
201   // Create the target machine.
202   std::unique_ptr<llvm::TargetMachine> targetMachine = createTargetMachine();
203   if (!targetMachine)
204     return std::nullopt;
205 
206   // Set the data layout and target triple of the module.
207   llvmModule->setDataLayout(targetMachine->createDataLayout());
208   llvmModule->setTargetTriple(targetMachine->getTargetTriple().getTriple());
209 
210   // Link bitcode files.
211   handleModulePreLink(*llvmModule, *targetMachine);
212   {
213     auto libs = loadBitcodeFiles(*llvmModule, *targetMachine);
214     if (!libs)
215       return std::nullopt;
216     if (libs->size())
217       if (failed(linkFiles(*llvmModule, std::move(*libs))))
218         return std::nullopt;
219     handleModulePostLink(*llvmModule, *targetMachine);
220   }
221 
222   // Optimize the module.
223   if (failed(optimizeModule(*llvmModule, *targetMachine, optLevel)))
224     return std::nullopt;
225 
226   // Return the serialized object.
227   return moduleToObject(*llvmModule, *targetMachine);
228 }
229