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