MLIR  20.0.0git
ModuleToObject.cpp
Go to the documentation of this file.
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 
15 
19 #include "mlir/IR/BuiltinOps.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 
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 
53 
55 
56 std::optional<llvm::TargetMachine *>
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) {
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 
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())) {
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) {
132  << "Couldn't load LLVM library for linking: " << error.getMessage();
133  return failure();
134  }
135  continue;
136  }
137  if (failureOnError) {
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 =
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 =
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 
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 
275  linkedLlvmIRCallback(*llvmModule);
276 
277  // Optimize the module.
278  if (failed(optimizeModule(*llvmModule, optLevel)))
279  return std::nullopt;
280 
282  optimizedLlvmIRCallback(*llvmModule);
283 
284  // Return the serialized object.
285  return moduleToObject(*llvmModule);
286 }
Attributes are known-constant values of operations.
Definition: Attributes.h:25
This class represents a diagnostic that is inflight and set to be reported.
Definition: Diagnostics.h:314
StringRef features
Target features.
std::unique_ptr< llvm::Module > translateToLLVMIR(llvm::LLVMContext &llvmContext)
Translates the operation to LLVM IR.
virtual std::optional< SmallVector< char, 0 > > run()
Runs the serialization pipeline, returning std::nullopt on error.
static std::optional< std::string > translateToISA(llvm::Module &llvmModule, llvm::TargetMachine &targetMachine)
Utility function for translating to ISA, returns std::nullopt on failure.
LogicalResult loadBitcodeFilesFromList(llvm::LLVMContext &context, ArrayRef< Attribute > librariesToLink, SmallVector< std::unique_ptr< llvm::Module >> &llvmModules, bool failureOnError=true)
Loads multiple bitcode files.
function_ref< void(llvm::Module &)> initialLlvmIRCallback
Callback invoked with the initial LLVM IR for the device module.
function_ref< void(llvm::Module &)> optimizedLlvmIRCallback
Callback invoked with LLVM IR for the device module after LLVM optimizations but before codegen.
virtual std::optional< SmallVector< char, 0 > > moduleToObject(llvm::Module &llvmModule)
Serializes the LLVM IR bitcode to an object file, by default it serializes to LLVM bitcode.
virtual void setDataLayoutAndTriple(llvm::Module &module)
Hook for computing the Datalayout.
virtual void handleModulePreLink(llvm::Module &module)
Hook for performing additional actions on the llvmModule pre linking.
StringRef triple
Target triple.
int optLevel
Optimization level.
virtual LogicalResult handleBitcodeFile(llvm::Module &module)
Hook for performing additional actions on a loaded bitcode file.
std::optional< llvm::TargetMachine * > getOrCreateTargetMachine()
Create the target machine based on the target triple and chip.
Operation & getOperation()
Returns the operation being serialized.
virtual LogicalResult optimizeModule(llvm::Module &module, int optL)
Optimize the module.
LogicalResult linkFiles(llvm::Module &module, SmallVector< std::unique_ptr< llvm::Module >> &&libs)
Link the llvmModule to other bitcode file.
function_ref< void(llvm::Module &)> linkedLlvmIRCallback
Callback invoked with LLVM IR for the device module after linking the device libraries.
StringRef chip
Target chip.
std::unique_ptr< llvm::Module > loadBitcodeFile(llvm::LLVMContext &context, StringRef path)
Loads a bitcode file from path.
virtual std::optional< SmallVector< std::unique_ptr< llvm::Module > > > loadBitcodeFiles(llvm::Module &module)
Hook for loading bitcode files, returns std::nullopt on failure.
virtual void handleModulePostLink(llvm::Module &module)
Hook for performing additional actions on the llvmModule post linking.
ModuleToObject(Operation &module, StringRef triple, StringRef chip, StringRef features={}, int optLevel=3, function_ref< void(llvm::Module &)> initialLlvmIRCallback={}, function_ref< void(llvm::Module &)> linkedLlvmIRCallback={}, function_ref< void(llvm::Module &)> optimizedLlvmIRCallback={}, function_ref< void(StringRef)> isaCallback={})
Operation & module
Module to transform to a binary object.
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
InFlightDiagnostic emitError(const Twine &message={})
Emit an error about fatal conditions with this operation, reporting up to any diagnostic handlers tha...
Definition: Operation.cpp:268
Include the generated interface declarations.
std::unique_ptr< llvm::Module > translateModuleToLLVMIR(Operation *module, llvm::LLVMContext &llvmContext, llvm::StringRef name="LLVMDialectModule", bool disableVerification=false)
Translates a given LLVM dialect module into an LLVM IR module living in the given context.
std::function< llvm::Error(llvm::Module *)> makeOptimizingTransformer(unsigned optLevel, unsigned sizeLevel, llvm::TargetMachine *targetMachine)
Create a module transformer function for MLIR ExecutionEngine that runs LLVM IR passes corresponding ...