ClangOffloadPackager.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. //===-- clang-offload-packager/ClangOffloadPackager.cpp - file bundler ---===//
  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 tool takes several device object files and bundles them into a single
  10. // binary image using a custom binary format. This is intended to be used to
  11. // embed many device files into an application to create a fat binary.
  12. //
  13. //===---------------------------------------------------------------------===//
  14. #include "clang/Basic/Version.h"
  15. #include "llvm/BinaryFormat/Magic.h"
  16. #include "llvm/Object/OffloadBinary.h"
  17. #include "llvm/Support/CommandLine.h"
  18. #include "llvm/Support/FileOutputBuffer.h"
  19. #include "llvm/Support/FileSystem.h"
  20. #include "llvm/Support/MemoryBuffer.h"
  21. #include "llvm/Support/Path.h"
  22. #include "llvm/Support/Signals.h"
  23. #include "llvm/Support/StringSaver.h"
  24. #include "llvm/Support/WithColor.h"
  25. using namespace llvm;
  26. using namespace llvm::object;
  27. static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
  28. static cl::OptionCategory
  29. ClangOffloadPackagerCategory("clang-offload-packager options");
  30. static cl::opt<std::string> OutputFile("o", cl::desc("Write output to <file>."),
  31. cl::value_desc("file"),
  32. cl::cat(ClangOffloadPackagerCategory));
  33. static cl::opt<std::string> InputFile(cl::Positional,
  34. cl::desc("Extract from <file>."),
  35. cl::value_desc("file"),
  36. cl::cat(ClangOffloadPackagerCategory));
  37. static cl::list<std::string>
  38. DeviceImages("image",
  39. cl::desc("List of key and value arguments. Required keywords "
  40. "are 'file' and 'triple'."),
  41. cl::value_desc("<key>=<value>,..."),
  42. cl::cat(ClangOffloadPackagerCategory));
  43. static void PrintVersion(raw_ostream &OS) {
  44. OS << clang::getClangToolFullVersion("clang-offload-packager") << '\n';
  45. }
  46. // Get a map containing all the arguments for the image. Repeated arguments will
  47. // be placed in a comma separated list.
  48. static DenseMap<StringRef, StringRef> getImageArguments(StringRef Image,
  49. StringSaver &Saver) {
  50. DenseMap<StringRef, StringRef> Args;
  51. for (StringRef Arg : llvm::split(Image, ",")) {
  52. auto [Key, Value] = Arg.split("=");
  53. if (Args.count(Key))
  54. Args[Key] = Saver.save(Args[Key] + "," + Value);
  55. else
  56. Args[Key] = Value;
  57. }
  58. return Args;
  59. }
  60. static Error bundleImages() {
  61. SmallVector<char, 1024> BinaryData;
  62. raw_svector_ostream OS(BinaryData);
  63. for (StringRef Image : DeviceImages) {
  64. BumpPtrAllocator Alloc;
  65. StringSaver Saver(Alloc);
  66. DenseMap<StringRef, StringRef> Args = getImageArguments(Image, Saver);
  67. if (!Args.count("triple") || !Args.count("file"))
  68. return createStringError(
  69. inconvertibleErrorCode(),
  70. "'file' and 'triple' are required image arguments");
  71. OffloadBinary::OffloadingImage ImageBinary{};
  72. std::unique_ptr<llvm::MemoryBuffer> DeviceImage;
  73. for (const auto &[Key, Value] : Args) {
  74. if (Key == "file") {
  75. llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ObjectOrErr =
  76. llvm::MemoryBuffer::getFileOrSTDIN(Value);
  77. if (std::error_code EC = ObjectOrErr.getError())
  78. return errorCodeToError(EC);
  79. // Clang uses the '.o' suffix for LTO bitcode.
  80. if (identify_magic((*ObjectOrErr)->getBuffer()) == file_magic::bitcode)
  81. ImageBinary.TheImageKind = object::IMG_Bitcode;
  82. else
  83. ImageBinary.TheImageKind =
  84. getImageKind(sys::path::extension(Value).drop_front());
  85. ImageBinary.Image = std::move(*ObjectOrErr);
  86. } else if (Key == "kind") {
  87. ImageBinary.TheOffloadKind = getOffloadKind(Value);
  88. } else {
  89. ImageBinary.StringData[Key] = Value;
  90. }
  91. }
  92. std::unique_ptr<MemoryBuffer> Buffer = OffloadBinary::write(ImageBinary);
  93. if (Buffer->getBufferSize() % OffloadBinary::getAlignment() != 0)
  94. return createStringError(inconvertibleErrorCode(),
  95. "Offload binary has invalid size alignment");
  96. OS << Buffer->getBuffer();
  97. }
  98. Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
  99. FileOutputBuffer::create(OutputFile, BinaryData.size());
  100. if (!OutputOrErr)
  101. return OutputOrErr.takeError();
  102. std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
  103. std::copy(BinaryData.begin(), BinaryData.end(), Output->getBufferStart());
  104. if (Error E = Output->commit())
  105. return E;
  106. return Error::success();
  107. }
  108. static Error unbundleImages() {
  109. ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
  110. MemoryBuffer::getFileOrSTDIN(InputFile);
  111. if (std::error_code EC = BufferOrErr.getError())
  112. return createFileError(InputFile, EC);
  113. std::unique_ptr<MemoryBuffer> Buffer = std::move(*BufferOrErr);
  114. // This data can be misaligned if extracted from an archive.
  115. if (!isAddrAligned(Align(OffloadBinary::getAlignment()),
  116. Buffer->getBufferStart()))
  117. Buffer = MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(),
  118. Buffer->getBufferIdentifier());
  119. SmallVector<OffloadFile> Binaries;
  120. if (Error Err = extractOffloadBinaries(*Buffer, Binaries))
  121. return Err;
  122. // Try to extract each device image specified by the user from the input file.
  123. for (StringRef Image : DeviceImages) {
  124. BumpPtrAllocator Alloc;
  125. StringSaver Saver(Alloc);
  126. auto Args = getImageArguments(Image, Saver);
  127. for (uint64_t I = 0, E = Binaries.size(); I != E; ++I) {
  128. const auto *Binary = Binaries[I].getBinary();
  129. // We handle the 'file' and 'kind' identifiers differently.
  130. bool Match = llvm::all_of(Args, [&](auto &Arg) {
  131. const auto [Key, Value] = Arg;
  132. if (Key == "file")
  133. return true;
  134. if (Key == "kind")
  135. return Binary->getOffloadKind() == getOffloadKind(Value);
  136. return Binary->getString(Key) == Value;
  137. });
  138. if (!Match)
  139. continue;
  140. // If the user did not provide a filename derive one from the input and
  141. // image.
  142. StringRef Filename =
  143. !Args.count("file")
  144. ? Saver.save(sys::path::stem(InputFile) + "-" +
  145. Binary->getTriple() + "-" + Binary->getArch() + "." +
  146. std::to_string(I) + "." +
  147. getImageKindName(Binary->getImageKind()))
  148. : Args["file"];
  149. Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
  150. FileOutputBuffer::create(Filename, Binary->getImage().size());
  151. if (!OutputOrErr)
  152. return OutputOrErr.takeError();
  153. std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
  154. llvm::copy(Binary->getImage(), Output->getBufferStart());
  155. if (Error E = Output->commit())
  156. return E;
  157. }
  158. }
  159. return Error::success();
  160. }
  161. int main(int argc, const char **argv) {
  162. sys::PrintStackTraceOnErrorSignal(argv[0]);
  163. cl::HideUnrelatedOptions(ClangOffloadPackagerCategory);
  164. cl::SetVersionPrinter(PrintVersion);
  165. cl::ParseCommandLineOptions(
  166. argc, argv,
  167. "A utility for bundling several object files into a single binary.\n"
  168. "The output binary can then be embedded into the host section table\n"
  169. "to create a fatbinary containing offloading code.\n");
  170. if (Help) {
  171. cl::PrintHelpMessage();
  172. return EXIT_SUCCESS;
  173. }
  174. auto reportError = [argv](Error E) {
  175. logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
  176. return EXIT_FAILURE;
  177. };
  178. if (!InputFile.empty() && !OutputFile.empty())
  179. return reportError(
  180. createStringError(inconvertibleErrorCode(),
  181. "Packaging to an output file and extracting from an "
  182. "input file are mutually exclusive."));
  183. if (!OutputFile.empty()) {
  184. if (Error Err = bundleImages())
  185. return reportError(std::move(Err));
  186. } else if (!InputFile.empty()) {
  187. if (Error Err = unbundleImages())
  188. return reportError(std::move(Err));
  189. }
  190. return EXIT_SUCCESS;
  191. }