MLIR  20.0.0git
Target.cpp
Go to the documentation of this file.
1 //===- Target.cpp - MLIR LLVM NVVM target compilation -----------*- 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 files defines NVVM target related functions including registration
10 // calls for the `#nvvm.target` compilation attribute.
11 //
12 //===----------------------------------------------------------------------===//
13 
15 
23 
24 #include "llvm/Config/llvm-config.h"
25 #include "llvm/Support/FileSystem.h"
26 #include "llvm/Support/FileUtilities.h"
27 #include "llvm/Support/FormatVariadic.h"
28 #include "llvm/Support/MemoryBuffer.h"
29 #include "llvm/Support/Path.h"
30 #include "llvm/Support/Process.h"
31 #include "llvm/Support/Program.h"
32 #include "llvm/Support/TargetSelect.h"
33 #include "llvm/Support/raw_ostream.h"
34 
35 #include <cstdlib>
36 
37 using namespace mlir;
38 using namespace mlir::NVVM;
39 
40 #ifndef __DEFAULT_CUDATOOLKIT_PATH__
41 #define __DEFAULT_CUDATOOLKIT_PATH__ ""
42 #endif
43 
44 namespace {
45 // Implementation of the `TargetAttrInterface` model.
46 class NVVMTargetAttrImpl
47  : public gpu::TargetAttrInterface::FallbackModel<NVVMTargetAttrImpl> {
48 public:
49  std::optional<SmallVector<char, 0>>
50  serializeToObject(Attribute attribute, Operation *module,
51  const gpu::TargetOptions &options) const;
52 
53  Attribute createObject(Attribute attribute, Operation *module,
54  const SmallVector<char, 0> &object,
55  const gpu::TargetOptions &options) const;
56 };
57 } // namespace
58 
59 // Register the NVVM dialect, the NVVM translation & the target interface.
61  DialectRegistry &registry) {
62  registry.addExtension(+[](MLIRContext *ctx, NVVM::NVVMDialect *dialect) {
63  NVVMTargetAttr::attachInterface<NVVMTargetAttrImpl>(*ctx);
64  });
65 }
66 
68  MLIRContext &context) {
69  DialectRegistry registry;
71  context.appendDialectRegistry(registry);
72 }
73 
74 // Search for the CUDA toolkit path.
76  if (const char *var = std::getenv("CUDA_ROOT"))
77  return var;
78  if (const char *var = std::getenv("CUDA_HOME"))
79  return var;
80  if (const char *var = std::getenv("CUDA_PATH"))
81  return var;
83 }
84 
86  Operation &module, NVVMTargetAttr target,
87  const gpu::TargetOptions &targetOptions)
88  : ModuleToObject(module, target.getTriple(), target.getChip(),
89  target.getFeatures(), target.getO()),
90  target(target), toolkitPath(targetOptions.getToolkitPath()),
91  fileList(targetOptions.getLinkFiles()) {
92 
93  // If `targetOptions` have an empty toolkitPath use `getCUDAToolkitPath`
94  if (toolkitPath.empty())
96 
97  // Append the files in the target attribute.
98  if (ArrayAttr files = target.getLink())
99  for (Attribute attr : files.getValue())
100  if (auto file = dyn_cast<StringAttr>(attr))
101  fileList.push_back(file.str());
102 
103  // Append libdevice to the files to be loaded.
104  (void)appendStandardLibs();
105 }
106 
108  static llvm::once_flag initializeBackendOnce;
109  llvm::call_once(initializeBackendOnce, []() {
110  // If the `NVPTX` LLVM target was built, initialize it.
111 #if LLVM_HAS_NVPTX_TARGET
112  LLVMInitializeNVPTXTarget();
113  LLVMInitializeNVPTXTargetInfo();
114  LLVMInitializeNVPTXTargetMC();
115  LLVMInitializeNVPTXAsmPrinter();
116 #endif
117  });
118 }
119 
120 NVVMTargetAttr SerializeGPUModuleBase::getTarget() const { return target; }
121 
123 
125  return fileList;
126 }
127 
128 // Try to append `libdevice` from a CUDA toolkit installation.
130  StringRef pathRef = getToolkitPath();
131  if (!pathRef.empty()) {
133  path.insert(path.begin(), pathRef.begin(), pathRef.end());
134  pathRef = StringRef(path.data(), path.size());
135  if (!llvm::sys::fs::is_directory(pathRef)) {
136  getOperation().emitError() << "CUDA path: " << pathRef
137  << " does not exist or is not a directory.\n";
138  return failure();
139  }
140  llvm::sys::path::append(path, "nvvm", "libdevice", "libdevice.10.bc");
141  pathRef = StringRef(path.data(), path.size());
142  if (!llvm::sys::fs::is_regular_file(pathRef)) {
143  getOperation().emitError() << "LibDevice path: " << pathRef
144  << " does not exist or is not a file.\n";
145  return failure();
146  }
147  fileList.push_back(pathRef.str());
148  }
149  return success();
150 }
151 
152 std::optional<SmallVector<std::unique_ptr<llvm::Module>>>
155  if (failed(loadBitcodeFilesFromList(module.getContext(), fileList, bcFiles,
156  true)))
157  return std::nullopt;
158  return std::move(bcFiles);
159 }
160 
161 namespace {
162 class NVPTXSerializer : public SerializeGPUModuleBase {
163 public:
164  NVPTXSerializer(Operation &module, NVVMTargetAttr target,
165  const gpu::TargetOptions &targetOptions);
166 
167  /// Returns the GPU module op being serialized.
168  gpu::GPUModuleOp getOperation();
169 
170  /// Compiles PTX to cubin using `ptxas`.
171  std::optional<SmallVector<char, 0>>
172  compileToBinary(const std::string &ptxCode);
173 
174  /// Compiles PTX to cubin using the `nvptxcompiler` library.
175  std::optional<SmallVector<char, 0>>
176  compileToBinaryNVPTX(const std::string &ptxCode);
177 
178  /// Serializes the LLVM module to an object format, depending on the
179  /// compilation target selected in target options.
180  std::optional<SmallVector<char, 0>>
181  moduleToObject(llvm::Module &llvmModule) override;
182 
183 private:
184  using TmpFile = std::pair<llvm::SmallString<128>, llvm::FileRemover>;
185 
186  /// Creates a temp file.
187  std::optional<TmpFile> createTemp(StringRef name, StringRef suffix);
188 
189  /// Finds the `tool` path, where `tool` is the name of the binary to search,
190  /// i.e. `ptxas` or `fatbinary`. The search order is:
191  /// 1. The toolkit path in `targetOptions`.
192  /// 2. In the system PATH.
193  /// 3. The path from `getCUDAToolkitPath()`.
194  std::optional<std::string> findTool(StringRef tool);
195 
196  /// Target options.
197  gpu::TargetOptions targetOptions;
198 };
199 } // namespace
200 
201 NVPTXSerializer::NVPTXSerializer(Operation &module, NVVMTargetAttr target,
202  const gpu::TargetOptions &targetOptions)
203  : SerializeGPUModuleBase(module, target, targetOptions),
204  targetOptions(targetOptions) {}
205 
206 std::optional<NVPTXSerializer::TmpFile>
207 NVPTXSerializer::createTemp(StringRef name, StringRef suffix) {
208  llvm::SmallString<128> filename;
209  std::error_code ec =
210  llvm::sys::fs::createTemporaryFile(name, suffix, filename);
211  if (ec) {
212  getOperation().emitError() << "Couldn't create the temp file: `" << filename
213  << "`, error message: " << ec.message();
214  return std::nullopt;
215  }
216  return TmpFile(filename, llvm::FileRemover(filename.c_str()));
217 }
218 
219 gpu::GPUModuleOp NVPTXSerializer::getOperation() {
220  return dyn_cast<gpu::GPUModuleOp>(&SerializeGPUModuleBase::getOperation());
221 }
222 
223 std::optional<std::string> NVPTXSerializer::findTool(StringRef tool) {
224  // Find the `tool` path.
225  // 1. Check the toolkit path given in the command line.
226  StringRef pathRef = targetOptions.getToolkitPath();
228  if (!pathRef.empty()) {
229  path.insert(path.begin(), pathRef.begin(), pathRef.end());
230  llvm::sys::path::append(path, "bin", tool);
231  if (llvm::sys::fs::can_execute(path))
232  return StringRef(path.data(), path.size()).str();
233  }
234 
235  // 2. Check PATH.
236  if (std::optional<std::string> toolPath =
237  llvm::sys::Process::FindInEnvPath("PATH", tool))
238  return *toolPath;
239 
240  // 3. Check `getCUDAToolkitPath()`.
241  pathRef = getCUDAToolkitPath();
242  path.clear();
243  if (!pathRef.empty()) {
244  path.insert(path.begin(), pathRef.begin(), pathRef.end());
245  llvm::sys::path::append(path, "bin", tool);
246  if (llvm::sys::fs::can_execute(path))
247  return StringRef(path.data(), path.size()).str();
248  }
249  getOperation().emitError()
250  << "Couldn't find the `" << tool
251  << "` binary. Please specify the toolkit "
252  "path, add the compiler to $PATH, or set one of the environment "
253  "variables in `NVVM::getCUDAToolkitPath()`.";
254  return std::nullopt;
255 }
256 
257 // TODO: clean this method & have a generic tool driver or never emit binaries
258 // with this mechanism and let another stage take care of it.
259 std::optional<SmallVector<char, 0>>
260 NVPTXSerializer::compileToBinary(const std::string &ptxCode) {
261  // Determine if the serializer should create a fatbinary with the PTX embeded
262  // or a simple CUBIN binary.
263  const bool createFatbin =
264  targetOptions.getCompilationTarget() == gpu::CompilationTarget::Fatbin;
265 
266  // Find the `ptxas` & `fatbinary` tools.
267  std::optional<std::string> ptxasCompiler = findTool("ptxas");
268  if (!ptxasCompiler)
269  return std::nullopt;
270  std::optional<std::string> fatbinaryTool;
271  if (createFatbin) {
272  fatbinaryTool = findTool("fatbinary");
273  if (!fatbinaryTool)
274  return std::nullopt;
275  }
276  Location loc = getOperation().getLoc();
277 
278  // Base name for all temp files: mlir-<module name>-<target triple>-<chip>.
279  std::string basename =
280  llvm::formatv("mlir-{0}-{1}-{2}", getOperation().getNameAttr().getValue(),
281  getTarget().getTriple(), getTarget().getChip());
282 
283  // Create temp files:
284  std::optional<TmpFile> ptxFile = createTemp(basename, "ptx");
285  if (!ptxFile)
286  return std::nullopt;
287  std::optional<TmpFile> logFile = createTemp(basename, "log");
288  if (!logFile)
289  return std::nullopt;
290  std::optional<TmpFile> binaryFile = createTemp(basename, "bin");
291  if (!binaryFile)
292  return std::nullopt;
293  TmpFile cubinFile;
294  if (createFatbin) {
295  Twine cubinFilename = ptxFile->first + ".cubin";
296  cubinFile = TmpFile(cubinFilename.str(), llvm::FileRemover(cubinFilename));
297  } else {
298  cubinFile.first = binaryFile->first;
299  }
300 
301  std::error_code ec;
302  // Dump the PTX to a temp file.
303  {
304  llvm::raw_fd_ostream ptxStream(ptxFile->first, ec);
305  if (ec) {
306  emitError(loc) << "Couldn't open the file: `" << ptxFile->first
307  << "`, error message: " << ec.message();
308  return std::nullopt;
309  }
310  ptxStream << ptxCode;
311  if (ptxStream.has_error()) {
312  emitError(loc) << "An error occurred while writing the PTX to: `"
313  << ptxFile->first << "`.";
314  return std::nullopt;
315  }
316  ptxStream.flush();
317  }
318 
319  // Command redirects.
320  std::optional<StringRef> redirects[] = {
321  std::nullopt,
322  logFile->first,
323  logFile->first,
324  };
325 
326  // Get any extra args passed in `targetOptions`.
327  std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>> cmdOpts =
328  targetOptions.tokenizeCmdOptions();
329 
330  // Create ptxas args.
331  std::string optLevel = std::to_string(this->optLevel);
332  SmallVector<StringRef, 12> ptxasArgs(
333  {StringRef("ptxas"), StringRef("-arch"), getTarget().getChip(),
334  StringRef(ptxFile->first), StringRef("-o"), StringRef(cubinFile.first),
335  "--opt-level", optLevel});
336 
337  bool useFatbin32 = false;
338  for (const auto *cArg : cmdOpts.second) {
339  // All `cmdOpts` are for `ptxas` except `-32` which passes `-32` to
340  // `fatbinary`, indicating a 32-bit target. By default a 64-bit target is
341  // assumed.
342  if (StringRef arg(cArg); arg != "-32")
343  ptxasArgs.push_back(arg);
344  else
345  useFatbin32 = true;
346  }
347 
348  // Create the `fatbinary` args.
349  StringRef chip = getTarget().getChip();
350  // Remove the arch prefix to obtain the compute capability.
351  chip.consume_front("sm_"), chip.consume_front("compute_");
352  // Embed the cubin object.
353  std::string cubinArg =
354  llvm::formatv("--image3=kind=elf,sm={0},file={1}", chip, cubinFile.first)
355  .str();
356  // Embed the PTX file so the driver can JIT if needed.
357  std::string ptxArg =
358  llvm::formatv("--image3=kind=ptx,sm={0},file={1}", chip, ptxFile->first)
359  .str();
360  SmallVector<StringRef, 6> fatbinArgs({StringRef("fatbinary"),
361  useFatbin32 ? "-32" : "-64", cubinArg,
362  ptxArg, "--create", binaryFile->first});
363 
364  // Dump tool invocation commands.
365 #define DEBUG_TYPE "serialize-to-binary"
366  LLVM_DEBUG({
367  llvm::dbgs() << "Tool invocation for module: "
368  << getOperation().getNameAttr() << "\n";
369  llvm::interleave(ptxasArgs, llvm::dbgs(), " ");
370  llvm::dbgs() << "\n";
371  if (createFatbin) {
372  llvm::interleave(fatbinArgs, llvm::dbgs(), " ");
373  llvm::dbgs() << "\n";
374  }
375  });
376 #undef DEBUG_TYPE
377 
378  // Helper function for printing tool error logs.
379  std::string message;
380  auto emitLogError =
381  [&](StringRef toolName) -> std::optional<SmallVector<char, 0>> {
382  if (message.empty()) {
383  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> toolStderr =
384  llvm::MemoryBuffer::getFile(logFile->first);
385  if (toolStderr)
386  emitError(loc) << toolName << " invocation failed. Log:\n"
387  << toolStderr->get()->getBuffer();
388  else
389  emitError(loc) << toolName << " invocation failed.";
390  return std::nullopt;
391  }
392  emitError(loc) << toolName
393  << " invocation failed, error message: " << message;
394  return std::nullopt;
395  };
396 
397  // Invoke PTXAS.
398  if (llvm::sys::ExecuteAndWait(ptxasCompiler.value(), ptxasArgs,
399  /*Env=*/std::nullopt,
400  /*Redirects=*/redirects,
401  /*SecondsToWait=*/0,
402  /*MemoryLimit=*/0,
403  /*ErrMsg=*/&message))
404  return emitLogError("`ptxas`");
405 #define DEBUG_TYPE "dump-sass"
406  LLVM_DEBUG({
407  std::optional<std::string> nvdisasm = findTool("nvdisasm");
408  SmallVector<StringRef> nvdisasmArgs(
409  {StringRef("nvdisasm"), StringRef(cubinFile.first)});
410  if (llvm::sys::ExecuteAndWait(nvdisasm.value(), nvdisasmArgs,
411  /*Env=*/std::nullopt,
412  /*Redirects=*/redirects,
413  /*SecondsToWait=*/0,
414  /*MemoryLimit=*/0,
415  /*ErrMsg=*/&message))
416  return emitLogError("`nvdisasm`");
417  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> logBuffer =
418  llvm::MemoryBuffer::getFile(logFile->first);
419  if (logBuffer && !(*logBuffer)->getBuffer().empty()) {
420  llvm::dbgs() << "Output:\n" << (*logBuffer)->getBuffer() << "\n";
421  llvm::dbgs().flush();
422  }
423  });
424 #undef DEBUG_TYPE
425 
426  // Invoke `fatbin`.
427  message.clear();
428  if (createFatbin && llvm::sys::ExecuteAndWait(*fatbinaryTool, fatbinArgs,
429  /*Env=*/std::nullopt,
430  /*Redirects=*/redirects,
431  /*SecondsToWait=*/0,
432  /*MemoryLimit=*/0,
433  /*ErrMsg=*/&message))
434  return emitLogError("`fatbinary`");
435 
436 // Dump the output of the tools, helpful if the verbose flag was passed.
437 #define DEBUG_TYPE "serialize-to-binary"
438  LLVM_DEBUG({
439  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> logBuffer =
440  llvm::MemoryBuffer::getFile(logFile->first);
441  if (logBuffer && !(*logBuffer)->getBuffer().empty()) {
442  llvm::dbgs() << "Output:\n" << (*logBuffer)->getBuffer() << "\n";
443  llvm::dbgs().flush();
444  }
445  });
446 #undef DEBUG_TYPE
447 
448  // Read the fatbin.
449  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> binaryBuffer =
450  llvm::MemoryBuffer::getFile(binaryFile->first);
451  if (!binaryBuffer) {
452  emitError(loc) << "Couldn't open the file: `" << binaryFile->first
453  << "`, error message: " << binaryBuffer.getError().message();
454  return std::nullopt;
455  }
456  StringRef fatbin = (*binaryBuffer)->getBuffer();
457  return SmallVector<char, 0>(fatbin.begin(), fatbin.end());
458 }
459 
460 #if MLIR_ENABLE_NVPTXCOMPILER
461 #include "nvPTXCompiler.h"
462 
463 #define RETURN_ON_NVPTXCOMPILER_ERROR(expr) \
464  do { \
465  if (auto status = (expr)) { \
466  emitError(loc) << llvm::Twine(#expr).concat(" failed with error code ") \
467  << status; \
468  return std::nullopt; \
469  } \
470  } while (false)
471 
472 std::optional<SmallVector<char, 0>>
473 NVPTXSerializer::compileToBinaryNVPTX(const std::string &ptxCode) {
474  Location loc = getOperation().getLoc();
475  nvPTXCompilerHandle compiler = nullptr;
476  nvPTXCompileResult status;
477  size_t logSize;
478 
479  // Create the options.
480  std::string optLevel = std::to_string(this->optLevel);
481  std::pair<llvm::BumpPtrAllocator, SmallVector<const char *>> cmdOpts =
482  targetOptions.tokenizeCmdOptions();
483  cmdOpts.second.append(
484  {"-arch", getTarget().getChip().data(), "--opt-level", optLevel.c_str()});
485 
486  // Create the compiler handle.
487  RETURN_ON_NVPTXCOMPILER_ERROR(
488  nvPTXCompilerCreate(&compiler, ptxCode.size(), ptxCode.c_str()));
489 
490  // Try to compile the binary.
491  status = nvPTXCompilerCompile(compiler, cmdOpts.second.size(),
492  cmdOpts.second.data());
493 
494  // Check if compilation failed.
495  if (status != NVPTXCOMPILE_SUCCESS) {
496  RETURN_ON_NVPTXCOMPILER_ERROR(
497  nvPTXCompilerGetErrorLogSize(compiler, &logSize));
498  if (logSize != 0) {
499  SmallVector<char> log(logSize + 1, 0);
500  RETURN_ON_NVPTXCOMPILER_ERROR(
501  nvPTXCompilerGetErrorLog(compiler, log.data()));
502  emitError(loc) << "NVPTX compiler invocation failed, error log: "
503  << log.data();
504  } else
505  emitError(loc) << "NVPTX compiler invocation failed with error code: "
506  << status;
507  return std::nullopt;
508  }
509 
510  // Retrieve the binary.
511  size_t elfSize;
512  RETURN_ON_NVPTXCOMPILER_ERROR(
513  nvPTXCompilerGetCompiledProgramSize(compiler, &elfSize));
514  SmallVector<char, 0> binary(elfSize, 0);
515  RETURN_ON_NVPTXCOMPILER_ERROR(
516  nvPTXCompilerGetCompiledProgram(compiler, (void *)binary.data()));
517 
518 // Dump the log of the compiler, helpful if the verbose flag was passed.
519 #define DEBUG_TYPE "serialize-to-binary"
520  LLVM_DEBUG({
521  RETURN_ON_NVPTXCOMPILER_ERROR(
522  nvPTXCompilerGetInfoLogSize(compiler, &logSize));
523  if (logSize != 0) {
524  SmallVector<char> log(logSize + 1, 0);
525  RETURN_ON_NVPTXCOMPILER_ERROR(
526  nvPTXCompilerGetInfoLog(compiler, log.data()));
527  llvm::dbgs() << "NVPTX compiler invocation for module: "
528  << getOperation().getNameAttr() << "\n";
529  llvm::dbgs() << "Arguments: ";
530  llvm::interleave(cmdOpts.second, llvm::dbgs(), " ");
531  llvm::dbgs() << "\nOutput\n" << log.data() << "\n";
532  llvm::dbgs().flush();
533  }
534  });
535 #undef DEBUG_TYPE
536  RETURN_ON_NVPTXCOMPILER_ERROR(nvPTXCompilerDestroy(&compiler));
537  return binary;
538 }
539 #endif // MLIR_ENABLE_NVPTXCOMPILER
540 
541 std::optional<SmallVector<char, 0>>
542 NVPTXSerializer::moduleToObject(llvm::Module &llvmModule) {
543  // Return LLVM IR if the compilation target is `offload`.
544 #define DEBUG_TYPE "serialize-to-llvm"
545  LLVM_DEBUG({
546  llvm::dbgs() << "LLVM IR for module: " << getOperation().getNameAttr()
547  << "\n";
548  llvm::dbgs() << llvmModule << "\n";
549  llvm::dbgs().flush();
550  });
551 #undef DEBUG_TYPE
552  if (targetOptions.getCompilationTarget() == gpu::CompilationTarget::Offload)
553  return SerializeGPUModuleBase::moduleToObject(llvmModule);
554 
555 #if !LLVM_HAS_NVPTX_TARGET
556  getOperation()->emitError(
557  "The `NVPTX` target was not built. Please enable it when building LLVM.");
558  return std::nullopt;
559 #endif // LLVM_HAS_NVPTX_TARGET
560 
561  // Emit PTX code.
562  std::optional<llvm::TargetMachine *> targetMachine =
563  getOrCreateTargetMachine();
564  if (!targetMachine) {
565  getOperation().emitError() << "Target Machine unavailable for triple "
566  << triple << ", can't optimize with LLVM\n";
567  return std::nullopt;
568  }
569  std::optional<std::string> serializedISA =
570  translateToISA(llvmModule, **targetMachine);
571  if (!serializedISA) {
572  getOperation().emitError() << "Failed translating the module to ISA.";
573  return std::nullopt;
574  }
575 #define DEBUG_TYPE "serialize-to-isa"
576  LLVM_DEBUG({
577  llvm::dbgs() << "PTX for module: " << getOperation().getNameAttr() << "\n";
578  llvm::dbgs() << *serializedISA << "\n";
579  llvm::dbgs().flush();
580  });
581 #undef DEBUG_TYPE
582 
583  // Return PTX if the compilation target is `assembly`.
584  if (targetOptions.getCompilationTarget() ==
585  gpu::CompilationTarget::Assembly) {
586  // Make sure to include the null terminator.
587  StringRef bin(serializedISA->c_str(), serializedISA->size() + 1);
588  return SmallVector<char, 0>(bin.begin(), bin.end());
589  }
590 
591  // Compile to binary.
592 #if MLIR_ENABLE_NVPTXCOMPILER
593  return compileToBinaryNVPTX(*serializedISA);
594 #else
595  return compileToBinary(*serializedISA);
596 #endif // MLIR_ENABLE_NVPTXCOMPILER
597 }
598 
599 std::optional<SmallVector<char, 0>>
600 NVVMTargetAttrImpl::serializeToObject(Attribute attribute, Operation *module,
601  const gpu::TargetOptions &options) const {
602  assert(module && "The module must be non null.");
603  if (!module)
604  return std::nullopt;
605  if (!mlir::isa<gpu::GPUModuleOp>(module)) {
606  module->emitError("Module must be a GPU module.");
607  return std::nullopt;
608  }
609  NVPTXSerializer serializer(*module, cast<NVVMTargetAttr>(attribute), options);
610  serializer.init();
611  return serializer.run();
612 }
613 
614 Attribute
615 NVVMTargetAttrImpl::createObject(Attribute attribute, Operation *module,
616  const SmallVector<char, 0> &object,
617  const gpu::TargetOptions &options) const {
618  auto target = cast<NVVMTargetAttr>(attribute);
619  gpu::CompilationTarget format = options.getCompilationTarget();
620  DictionaryAttr objectProps;
621  Builder builder(attribute.getContext());
622  if (format == gpu::CompilationTarget::Assembly)
623  objectProps = builder.getDictionaryAttr(
624  {builder.getNamedAttr("O", builder.getI32IntegerAttr(target.getO()))});
625  return builder.getAttr<gpu::ObjectAttr>(
626  attribute, format,
627  builder.getStringAttr(StringRef(object.data(), object.size())),
628  objectProps, /*kernels=*/nullptr);
629 }
#define __DEFAULT_CUDATOOLKIT_PATH__
Definition: Target.cpp:41
static llvm::ManagedStatic< PassManagerOptions > options
Attributes are known-constant values of operations.
Definition: Attributes.h:25
MLIRContext * getContext() const
Return the context this attribute belongs to.
Definition: Attributes.cpp:37
This class is a general helper class for creating context-global objects like types,...
Definition: Builders.h:50
The DialectRegistry maps a dialect namespace to a constructor for the matching dialect.
bool addExtension(TypeID extensionID, std::unique_ptr< DialectExtensionBase > extension)
Add the given extension to the registry.
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.
Operation & getOperation()
Returns the operation being serialized.
LogicalResult loadBitcodeFilesFromList(llvm::LLVMContext &context, ArrayRef< std::string > fileList, SmallVector< std::unique_ptr< llvm::Module >> &llvmModules, bool failureOnError=true)
Loads multiple bitcode files.
Operation & module
Module to transform to a binary object.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:66
MLIRContext is the top-level object for a collection of MLIR operations.
Definition: MLIRContext.h:60
void appendDialectRegistry(const DialectRegistry &registry)
Append the contents of the given dialect registry to the registry associated with this context.
Base class for all NVVM serializations from GPU modules into binary strings.
Definition: Utils.h:32
ArrayRef< std::string > getFileList() const
Returns the bitcode files to be loaded.
Definition: Target.cpp:124
SerializeGPUModuleBase(Operation &module, NVVMTargetAttr target, const gpu::TargetOptions &targetOptions={})
Initializes the toolkitPath with the path in targetOptions or if empty with the path in getCUDAToolki...
Definition: Target.cpp:85
SmallVector< std::string > fileList
List of LLVM bitcode files to link to.
Definition: Utils.h:68
NVVMTargetAttr target
NVVM target attribute.
Definition: Utils.h:62
std::string toolkitPath
CUDA toolkit path.
Definition: Utils.h:65
virtual std::optional< SmallVector< std::unique_ptr< llvm::Module > > > loadBitcodeFiles(llvm::Module &module) override
Loads the bitcode files in fileList.
Definition: Target.cpp:153
LogicalResult appendStandardLibs()
Appends nvvm/libdevice.bc into fileList.
Definition: Target.cpp:129
static void init()
Initializes the LLVM NVPTX target by safely calling LLVMInitializeNVPTX* methods if available.
Definition: Target.cpp:107
StringRef getToolkitPath() const
Returns the CUDA toolkit path.
Definition: Target.cpp:122
NVVMTargetAttr getTarget() const
Returns the target attribute.
Definition: Target.cpp:120
Operation is the basic unit of execution within MLIR.
Definition: Operation.h:88
MLIRContext * getContext()
Return the context this operation is associated with.
Definition: Operation.h:216
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
This class serves as an opaque interface for passing options to the TargetAttrInterface methods.
void registerNVVMTargetInterfaceExternalModels(DialectRegistry &registry)
Registers the TargetAttrInterface for the #nvvm.target attribute in the given registry.
Definition: Target.cpp:60
StringRef getCUDAToolkitPath()
Searches & returns the path CUDA toolkit path, the search order is:
Definition: Target.cpp:75
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.