ClangOffloadBundler.cpp revision 1.1.1.1 1 //===-- clang-offload-bundler/ClangOffloadBundler.cpp ---------------------===//
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 /// \file
10 /// This file implements a clang-offload-bundler that bundles different
11 /// files that relate with the same source code but different targets into a
12 /// single one. Also the implements the opposite functionality, i.e. unbundle
13 /// files previous created by this tool.
14 ///
15 //===----------------------------------------------------------------------===//
16
17 #include "clang/Basic/Version.h"
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/ADT/SmallString.h"
20 #include "llvm/ADT/SmallVector.h"
21 #include "llvm/ADT/StringMap.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/ADT/StringSwitch.h"
24 #include "llvm/ADT/Triple.h"
25 #include "llvm/Object/Binary.h"
26 #include "llvm/Object/ObjectFile.h"
27 #include "llvm/Support/Casting.h"
28 #include "llvm/Support/CommandLine.h"
29 #include "llvm/Support/Errc.h"
30 #include "llvm/Support/Error.h"
31 #include "llvm/Support/ErrorOr.h"
32 #include "llvm/Support/FileSystem.h"
33 #include "llvm/Support/MemoryBuffer.h"
34 #include "llvm/Support/Path.h"
35 #include "llvm/Support/Program.h"
36 #include "llvm/Support/Signals.h"
37 #include "llvm/Support/StringSaver.h"
38 #include "llvm/Support/WithColor.h"
39 #include "llvm/Support/raw_ostream.h"
40 #include <algorithm>
41 #include <cassert>
42 #include <cstddef>
43 #include <cstdint>
44 #include <memory>
45 #include <string>
46 #include <system_error>
47 #include <utility>
48
49 using namespace llvm;
50 using namespace llvm::object;
51
52 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
53
54 // Mark all our options with this category, everything else (except for -version
55 // and -help) will be hidden.
56 static cl::OptionCategory
57 ClangOffloadBundlerCategory("clang-offload-bundler options");
58
59 static cl::list<std::string>
60 InputFileNames("inputs", cl::CommaSeparated, cl::OneOrMore,
61 cl::desc("[<input file>,...]"),
62 cl::cat(ClangOffloadBundlerCategory));
63 static cl::list<std::string>
64 OutputFileNames("outputs", cl::CommaSeparated, cl::OneOrMore,
65 cl::desc("[<output file>,...]"),
66 cl::cat(ClangOffloadBundlerCategory));
67 static cl::list<std::string>
68 TargetNames("targets", cl::CommaSeparated, cl::OneOrMore,
69 cl::desc("[<offload kind>-<target triple>,...]"),
70 cl::cat(ClangOffloadBundlerCategory));
71 static cl::opt<std::string>
72 FilesType("type", cl::Required,
73 cl::desc("Type of the files to be bundled/unbundled.\n"
74 "Current supported types are:\n"
75 " i - cpp-output\n"
76 " ii - c++-cpp-output\n"
77 " cui - cuda/hip-output\n"
78 " d - dependency\n"
79 " ll - llvm\n"
80 " bc - llvm-bc\n"
81 " s - assembler\n"
82 " o - object\n"
83 " gch - precompiled-header\n"
84 " ast - clang AST file"),
85 cl::cat(ClangOffloadBundlerCategory));
86 static cl::opt<bool>
87 Unbundle("unbundle",
88 cl::desc("Unbundle bundled file into several output files.\n"),
89 cl::init(false), cl::cat(ClangOffloadBundlerCategory));
90
91 static cl::opt<bool> PrintExternalCommands(
92 "###",
93 cl::desc("Print any external commands that are to be executed "
94 "instead of actually executing them - for testing purposes.\n"),
95 cl::init(false), cl::cat(ClangOffloadBundlerCategory));
96
97 /// Magic string that marks the existence of offloading data.
98 #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
99
100 /// The index of the host input in the list of inputs.
101 static unsigned HostInputIndex = ~0u;
102
103 /// Path to the current binary.
104 static std::string BundlerExecutable;
105
106 /// Obtain the offload kind and real machine triple out of the target
107 /// information specified by the user.
108 static void getOffloadKindAndTriple(StringRef Target, StringRef &OffloadKind,
109 StringRef &Triple) {
110 auto KindTriplePair = Target.split('-');
111 OffloadKind = KindTriplePair.first;
112 Triple = KindTriplePair.second;
113 }
114 static bool hasHostKind(StringRef Target) {
115 StringRef OffloadKind;
116 StringRef Triple;
117 getOffloadKindAndTriple(Target, OffloadKind, Triple);
118 return OffloadKind == "host";
119 }
120
121 /// Generic file handler interface.
122 class FileHandler {
123 public:
124 FileHandler() {}
125
126 virtual ~FileHandler() {}
127
128 /// Update the file handler with information from the header of the bundled
129 /// file.
130 virtual Error ReadHeader(MemoryBuffer &Input) = 0;
131
132 /// Read the marker of the next bundled to be read in the file. The bundle
133 /// name is returned if there is one in the file, or `None` if there are no
134 /// more bundles to be read.
135 virtual Expected<Optional<StringRef>>
136 ReadBundleStart(MemoryBuffer &Input) = 0;
137
138 /// Read the marker that closes the current bundle.
139 virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
140
141 /// Read the current bundle and write the result into the stream \a OS.
142 virtual Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0;
143
144 /// Write the header of the bundled file to \a OS based on the information
145 /// gathered from \a Inputs.
146 virtual Error WriteHeader(raw_fd_ostream &OS,
147 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0;
148
149 /// Write the marker that initiates a bundle for the triple \a TargetTriple to
150 /// \a OS.
151 virtual Error WriteBundleStart(raw_fd_ostream &OS,
152 StringRef TargetTriple) = 0;
153
154 /// Write the marker that closes a bundle for the triple \a TargetTriple to \a
155 /// OS.
156 virtual Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) = 0;
157
158 /// Write the bundle from \a Input into \a OS.
159 virtual Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0;
160 };
161
162 /// Handler for binary files. The bundled file will have the following format
163 /// (all integers are stored in little-endian format):
164 ///
165 /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
166 ///
167 /// NumberOfOffloadBundles (8-byte integer)
168 ///
169 /// OffsetOfBundle1 (8-byte integer)
170 /// SizeOfBundle1 (8-byte integer)
171 /// NumberOfBytesInTripleOfBundle1 (8-byte integer)
172 /// TripleOfBundle1 (byte length defined before)
173 ///
174 /// ...
175 ///
176 /// OffsetOfBundleN (8-byte integer)
177 /// SizeOfBundleN (8-byte integer)
178 /// NumberOfBytesInTripleOfBundleN (8-byte integer)
179 /// TripleOfBundleN (byte length defined before)
180 ///
181 /// Bundle1
182 /// ...
183 /// BundleN
184
185 /// Read 8-byte integers from a buffer in little-endian format.
186 static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) {
187 uint64_t Res = 0;
188 const char *Data = Buffer.data();
189
190 for (unsigned i = 0; i < 8; ++i) {
191 Res <<= 8;
192 uint64_t Char = (uint64_t)Data[pos + 7 - i];
193 Res |= 0xffu & Char;
194 }
195 return Res;
196 }
197
198 /// Write 8-byte integers to a buffer in little-endian format.
199 static void Write8byteIntegerToBuffer(raw_fd_ostream &OS, uint64_t Val) {
200 for (unsigned i = 0; i < 8; ++i) {
201 char Char = (char)(Val & 0xffu);
202 OS.write(&Char, 1);
203 Val >>= 8;
204 }
205 }
206
207 class BinaryFileHandler final : public FileHandler {
208 /// Information about the bundles extracted from the header.
209 struct BundleInfo final {
210 /// Size of the bundle.
211 uint64_t Size = 0u;
212 /// Offset at which the bundle starts in the bundled file.
213 uint64_t Offset = 0u;
214
215 BundleInfo() {}
216 BundleInfo(uint64_t Size, uint64_t Offset) : Size(Size), Offset(Offset) {}
217 };
218
219 /// Map between a triple and the corresponding bundle information.
220 StringMap<BundleInfo> BundlesInfo;
221
222 /// Iterator for the bundle information that is being read.
223 StringMap<BundleInfo>::iterator CurBundleInfo;
224 StringMap<BundleInfo>::iterator NextBundleInfo;
225
226 public:
227 BinaryFileHandler() : FileHandler() {}
228
229 ~BinaryFileHandler() final {}
230
231 Error ReadHeader(MemoryBuffer &Input) final {
232 StringRef FC = Input.getBuffer();
233
234 // Initialize the current bundle with the end of the container.
235 CurBundleInfo = BundlesInfo.end();
236
237 // Check if buffer is smaller than magic string.
238 size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
239 if (ReadChars > FC.size())
240 return Error::success();
241
242 // Check if no magic was found.
243 StringRef Magic(FC.data(), sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
244 if (!Magic.equals(OFFLOAD_BUNDLER_MAGIC_STR))
245 return Error::success();
246
247 // Read number of bundles.
248 if (ReadChars + 8 > FC.size())
249 return Error::success();
250
251 uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
252 ReadChars += 8;
253
254 // Read bundle offsets, sizes and triples.
255 for (uint64_t i = 0; i < NumberOfBundles; ++i) {
256
257 // Read offset.
258 if (ReadChars + 8 > FC.size())
259 return Error::success();
260
261 uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars);
262 ReadChars += 8;
263
264 // Read size.
265 if (ReadChars + 8 > FC.size())
266 return Error::success();
267
268 uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars);
269 ReadChars += 8;
270
271 // Read triple size.
272 if (ReadChars + 8 > FC.size())
273 return Error::success();
274
275 uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars);
276 ReadChars += 8;
277
278 // Read triple.
279 if (ReadChars + TripleSize > FC.size())
280 return Error::success();
281
282 StringRef Triple(&FC.data()[ReadChars], TripleSize);
283 ReadChars += TripleSize;
284
285 // Check if the offset and size make sense.
286 if (!Offset || Offset + Size > FC.size())
287 return Error::success();
288
289 assert(BundlesInfo.find(Triple) == BundlesInfo.end() &&
290 "Triple is duplicated??");
291 BundlesInfo[Triple] = BundleInfo(Size, Offset);
292 }
293 // Set the iterator to where we will start to read.
294 CurBundleInfo = BundlesInfo.end();
295 NextBundleInfo = BundlesInfo.begin();
296 return Error::success();
297 }
298
299 Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final {
300 if (NextBundleInfo == BundlesInfo.end())
301 return None;
302 CurBundleInfo = NextBundleInfo++;
303 return CurBundleInfo->first();
304 }
305
306 Error ReadBundleEnd(MemoryBuffer &Input) final {
307 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
308 return Error::success();
309 }
310
311 Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
312 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
313 StringRef FC = Input.getBuffer();
314 OS.write(FC.data() + CurBundleInfo->second.Offset,
315 CurBundleInfo->second.Size);
316 return Error::success();
317 }
318
319 Error WriteHeader(raw_fd_ostream &OS,
320 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
321 // Compute size of the header.
322 uint64_t HeaderSize = 0;
323
324 HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
325 HeaderSize += 8; // Number of Bundles
326
327 for (auto &T : TargetNames) {
328 HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple.
329 HeaderSize += T.size(); // The triple.
330 }
331
332 // Write to the buffer the header.
333 OS << OFFLOAD_BUNDLER_MAGIC_STR;
334
335 Write8byteIntegerToBuffer(OS, TargetNames.size());
336
337 unsigned Idx = 0;
338 for (auto &T : TargetNames) {
339 MemoryBuffer &MB = *Inputs[Idx++];
340 // Bundle offset.
341 Write8byteIntegerToBuffer(OS, HeaderSize);
342 // Size of the bundle (adds to the next bundle's offset)
343 Write8byteIntegerToBuffer(OS, MB.getBufferSize());
344 HeaderSize += MB.getBufferSize();
345 // Size of the triple
346 Write8byteIntegerToBuffer(OS, T.size());
347 // Triple
348 OS << T;
349 }
350 return Error::success();
351 }
352
353 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
354 return Error::success();
355 }
356
357 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
358 return Error::success();
359 }
360
361 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
362 OS.write(Input.getBufferStart(), Input.getBufferSize());
363 return Error::success();
364 }
365 };
366
367 /// Handler for object files. The bundles are organized by sections with a
368 /// designated name.
369 ///
370 /// To unbundle, we just copy the contents of the designated section.
371 class ObjectFileHandler final : public FileHandler {
372
373 /// The object file we are currently dealing with.
374 std::unique_ptr<ObjectFile> Obj;
375
376 /// Return the input file contents.
377 StringRef getInputFileContents() const { return Obj->getData(); }
378
379 /// Return bundle name (<kind>-<triple>) if the provided section is an offload
380 /// section.
381 static Expected<Optional<StringRef>> IsOffloadSection(SectionRef CurSection) {
382 Expected<StringRef> NameOrErr = CurSection.getName();
383 if (!NameOrErr)
384 return NameOrErr.takeError();
385
386 // If it does not start with the reserved suffix, just skip this section.
387 if (!NameOrErr->startswith(OFFLOAD_BUNDLER_MAGIC_STR))
388 return None;
389
390 // Return the triple that is right after the reserved prefix.
391 return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
392 }
393
394 /// Total number of inputs.
395 unsigned NumberOfInputs = 0;
396
397 /// Total number of processed inputs, i.e, inputs that were already
398 /// read from the buffers.
399 unsigned NumberOfProcessedInputs = 0;
400
401 /// Iterator of the current and next section.
402 section_iterator CurrentSection;
403 section_iterator NextSection;
404
405 public:
406 ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn)
407 : FileHandler(), Obj(std::move(ObjIn)),
408 CurrentSection(Obj->section_begin()),
409 NextSection(Obj->section_begin()) {}
410
411 ~ObjectFileHandler() final {}
412
413 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
414
415 Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final {
416 while (NextSection != Obj->section_end()) {
417 CurrentSection = NextSection;
418 ++NextSection;
419
420 // Check if the current section name starts with the reserved prefix. If
421 // so, return the triple.
422 Expected<Optional<StringRef>> TripleOrErr =
423 IsOffloadSection(*CurrentSection);
424 if (!TripleOrErr)
425 return TripleOrErr.takeError();
426 if (*TripleOrErr)
427 return **TripleOrErr;
428 }
429 return None;
430 }
431
432 Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); }
433
434 Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
435 Expected<StringRef> Content = CurrentSection->getContents();
436 if (!Content)
437 return Content.takeError();
438
439 OS.write(Content->data(), Content->size());
440 return Error::success();
441 }
442
443 Error WriteHeader(raw_fd_ostream &OS,
444 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
445 assert(HostInputIndex != ~0u && "Host input index not defined.");
446
447 // Record number of inputs.
448 NumberOfInputs = Inputs.size();
449 return Error::success();
450 }
451
452 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
453 ++NumberOfProcessedInputs;
454 return Error::success();
455 }
456
457 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
458 assert(NumberOfProcessedInputs <= NumberOfInputs &&
459 "Processing more inputs that actually exist!");
460 assert(HostInputIndex != ~0u && "Host input index not defined.");
461
462 // If this is not the last output, we don't have to do anything.
463 if (NumberOfProcessedInputs != NumberOfInputs)
464 return Error::success();
465
466 // Find llvm-objcopy in order to create the bundle binary.
467 ErrorOr<std::string> Objcopy = sys::findProgramByName(
468 "llvm-objcopy", sys::path::parent_path(BundlerExecutable));
469 if (!Objcopy)
470 Objcopy = sys::findProgramByName("llvm-objcopy");
471 if (!Objcopy)
472 return createStringError(Objcopy.getError(),
473 "unable to find 'llvm-objcopy' in path");
474
475 // We write to the output file directly. So, we close it and use the name
476 // to pass down to llvm-objcopy.
477 OS.close();
478
479 // Compose command line for the objcopy tool.
480 BumpPtrAllocator Alloc;
481 StringSaver SS{Alloc};
482 SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"};
483 for (unsigned I = 0; I < NumberOfInputs; ++I)
484 ObjcopyArgs.push_back(SS.save(Twine("--add-section=") +
485 OFFLOAD_BUNDLER_MAGIC_STR + TargetNames[I] +
486 "=" + InputFileNames[I]));
487 ObjcopyArgs.push_back(InputFileNames[HostInputIndex]);
488 ObjcopyArgs.push_back(OutputFileNames.front());
489
490 // If the user asked for the commands to be printed out, we do that instead
491 // of executing it.
492 if (PrintExternalCommands) {
493 errs() << "\"" << *Objcopy << "\"";
494 for (StringRef Arg : drop_begin(ObjcopyArgs, 1))
495 errs() << " \"" << Arg << "\"";
496 errs() << "\n";
497 } else {
498 if (sys::ExecuteAndWait(*Objcopy, ObjcopyArgs))
499 return createStringError(inconvertibleErrorCode(),
500 "'llvm-objcopy' tool failed");
501 }
502
503 return Error::success();
504 }
505
506 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
507 return Error::success();
508 }
509 };
510
511 /// Handler for text files. The bundled file will have the following format.
512 ///
513 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
514 /// Bundle 1
515 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
516 /// ...
517 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
518 /// Bundle N
519 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
520 class TextFileHandler final : public FileHandler {
521 /// String that begins a line comment.
522 StringRef Comment;
523
524 /// String that initiates a bundle.
525 std::string BundleStartString;
526
527 /// String that closes a bundle.
528 std::string BundleEndString;
529
530 /// Number of chars read from input.
531 size_t ReadChars = 0u;
532
533 protected:
534 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
535
536 Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final {
537 StringRef FC = Input.getBuffer();
538
539 // Find start of the bundle.
540 ReadChars = FC.find(BundleStartString, ReadChars);
541 if (ReadChars == FC.npos)
542 return None;
543
544 // Get position of the triple.
545 size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
546
547 // Get position that closes the triple.
548 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars);
549 if (TripleEnd == FC.npos)
550 return None;
551
552 // Next time we read after the new line.
553 ++ReadChars;
554
555 return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
556 }
557
558 Error ReadBundleEnd(MemoryBuffer &Input) final {
559 StringRef FC = Input.getBuffer();
560
561 // Read up to the next new line.
562 assert(FC[ReadChars] == '\n' && "The bundle should end with a new line.");
563
564 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1);
565 if (TripleEnd != FC.npos)
566 // Next time we read after the new line.
567 ++ReadChars;
568
569 return Error::success();
570 }
571
572 Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
573 StringRef FC = Input.getBuffer();
574 size_t BundleStart = ReadChars;
575
576 // Find end of the bundle.
577 size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
578
579 StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
580 OS << Bundle;
581
582 return Error::success();
583 }
584
585 Error WriteHeader(raw_fd_ostream &OS,
586 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
587 return Error::success();
588 }
589
590 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
591 OS << BundleStartString << TargetTriple << "\n";
592 return Error::success();
593 }
594
595 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
596 OS << BundleEndString << TargetTriple << "\n";
597 return Error::success();
598 }
599
600 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
601 OS << Input.getBuffer();
602 return Error::success();
603 }
604
605 public:
606 TextFileHandler(StringRef Comment)
607 : FileHandler(), Comment(Comment), ReadChars(0) {
608 BundleStartString =
609 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ ";
610 BundleEndString =
611 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ ";
612 }
613 };
614
615 /// Return an appropriate object file handler. We use the specific object
616 /// handler if we know how to deal with that format, otherwise we use a default
617 /// binary file handler.
618 static std::unique_ptr<FileHandler>
619 CreateObjectFileHandler(MemoryBuffer &FirstInput) {
620 // Check if the input file format is one that we know how to deal with.
621 Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput);
622
623 // We only support regular object files. If failed to open the input as a
624 // known binary or this is not an object file use the default binary handler.
625 if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr))
626 return std::make_unique<BinaryFileHandler>();
627
628 // Otherwise create an object file handler. The handler will be owned by the
629 // client of this function.
630 return std::make_unique<ObjectFileHandler>(
631 std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())));
632 }
633
634 /// Return an appropriate handler given the input files and options.
635 static Expected<std::unique_ptr<FileHandler>>
636 CreateFileHandler(MemoryBuffer &FirstInput) {
637 if (FilesType == "i")
638 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
639 if (FilesType == "ii")
640 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
641 if (FilesType == "cui")
642 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
643 // TODO: `.d` should be eventually removed once `-M` and its variants are
644 // handled properly in offload compilation.
645 if (FilesType == "d")
646 return std::make_unique<TextFileHandler>(/*Comment=*/"#");
647 if (FilesType == "ll")
648 return std::make_unique<TextFileHandler>(/*Comment=*/";");
649 if (FilesType == "bc")
650 return std::make_unique<BinaryFileHandler>();
651 if (FilesType == "s")
652 return std::make_unique<TextFileHandler>(/*Comment=*/"#");
653 if (FilesType == "o")
654 return CreateObjectFileHandler(FirstInput);
655 if (FilesType == "gch")
656 return std::make_unique<BinaryFileHandler>();
657 if (FilesType == "ast")
658 return std::make_unique<BinaryFileHandler>();
659
660 return createStringError(errc::invalid_argument,
661 "'" + FilesType + "': invalid file type specified");
662 }
663
664 /// Bundle the files. Return true if an error was found.
665 static Error BundleFiles() {
666 std::error_code EC;
667
668 // Create output file.
669 raw_fd_ostream OutputFile(OutputFileNames.front(), EC, sys::fs::OF_None);
670 if (EC)
671 return createFileError(OutputFileNames.front(), EC);
672
673 // Open input files.
674 SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers;
675 InputBuffers.reserve(InputFileNames.size());
676 for (auto &I : InputFileNames) {
677 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
678 MemoryBuffer::getFileOrSTDIN(I);
679 if (std::error_code EC = CodeOrErr.getError())
680 return createFileError(I, EC);
681 InputBuffers.emplace_back(std::move(*CodeOrErr));
682 }
683
684 // Get the file handler. We use the host buffer as reference.
685 assert(HostInputIndex != ~0u && "Host input index undefined??");
686 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
687 CreateFileHandler(*InputBuffers[HostInputIndex]);
688 if (!FileHandlerOrErr)
689 return FileHandlerOrErr.takeError();
690
691 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
692 assert(FH);
693
694 // Write header.
695 if (Error Err = FH->WriteHeader(OutputFile, InputBuffers))
696 return Err;
697
698 // Write all bundles along with the start/end markers. If an error was found
699 // writing the end of the bundle component, abort the bundle writing.
700 auto Input = InputBuffers.begin();
701 for (auto &Triple : TargetNames) {
702 if (Error Err = FH->WriteBundleStart(OutputFile, Triple))
703 return Err;
704 if (Error Err = FH->WriteBundle(OutputFile, **Input))
705 return Err;
706 if (Error Err = FH->WriteBundleEnd(OutputFile, Triple))
707 return Err;
708 ++Input;
709 }
710 return Error::success();
711 }
712
713 // Unbundle the files. Return true if an error was found.
714 static Error UnbundleFiles() {
715 // Open Input file.
716 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
717 MemoryBuffer::getFileOrSTDIN(InputFileNames.front());
718 if (std::error_code EC = CodeOrErr.getError())
719 return createFileError(InputFileNames.front(), EC);
720
721 MemoryBuffer &Input = **CodeOrErr;
722
723 // Select the right files handler.
724 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
725 CreateFileHandler(Input);
726 if (!FileHandlerOrErr)
727 return FileHandlerOrErr.takeError();
728
729 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
730 assert(FH);
731
732 // Read the header of the bundled file.
733 if (Error Err = FH->ReadHeader(Input))
734 return Err;
735
736 // Create a work list that consist of the map triple/output file.
737 StringMap<StringRef> Worklist;
738 auto Output = OutputFileNames.begin();
739 for (auto &Triple : TargetNames) {
740 Worklist[Triple] = *Output;
741 ++Output;
742 }
743
744 // Read all the bundles that are in the work list. If we find no bundles we
745 // assume the file is meant for the host target.
746 bool FoundHostBundle = false;
747 while (!Worklist.empty()) {
748 Expected<Optional<StringRef>> CurTripleOrErr = FH->ReadBundleStart(Input);
749 if (!CurTripleOrErr)
750 return CurTripleOrErr.takeError();
751
752 // We don't have more bundles.
753 if (!*CurTripleOrErr)
754 break;
755
756 StringRef CurTriple = **CurTripleOrErr;
757 assert(!CurTriple.empty());
758
759 auto Output = Worklist.find(CurTriple);
760 // The file may have more bundles for other targets, that we don't care
761 // about. Therefore, move on to the next triple
762 if (Output == Worklist.end())
763 continue;
764
765 // Check if the output file can be opened and copy the bundle to it.
766 std::error_code EC;
767 raw_fd_ostream OutputFile(Output->second, EC, sys::fs::OF_None);
768 if (EC)
769 return createFileError(Output->second, EC);
770 if (Error Err = FH->ReadBundle(OutputFile, Input))
771 return Err;
772 if (Error Err = FH->ReadBundleEnd(Input))
773 return Err;
774 Worklist.erase(Output);
775
776 // Record if we found the host bundle.
777 if (hasHostKind(CurTriple))
778 FoundHostBundle = true;
779 }
780
781 // If no bundles were found, assume the input file is the host bundle and
782 // create empty files for the remaining targets.
783 if (Worklist.size() == TargetNames.size()) {
784 for (auto &E : Worklist) {
785 std::error_code EC;
786 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
787 if (EC)
788 return createFileError(E.second, EC);
789
790 // If this entry has a host kind, copy the input file to the output file.
791 if (hasHostKind(E.first()))
792 OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
793 }
794 return Error::success();
795 }
796
797 // If we found elements, we emit an error if none of those were for the host
798 // in case host bundle name was provided in command line.
799 if (!FoundHostBundle && HostInputIndex != ~0u)
800 return createStringError(inconvertibleErrorCode(),
801 "Can't find bundle for the host target");
802
803 // If we still have any elements in the worklist, create empty files for them.
804 for (auto &E : Worklist) {
805 std::error_code EC;
806 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
807 if (EC)
808 return createFileError(E.second, EC);
809 }
810
811 return Error::success();
812 }
813
814 static void PrintVersion(raw_ostream &OS) {
815 OS << clang::getClangToolFullVersion("clang-offload-bundler") << '\n';
816 }
817
818 int main(int argc, const char **argv) {
819 sys::PrintStackTraceOnErrorSignal(argv[0]);
820
821 cl::HideUnrelatedOptions(ClangOffloadBundlerCategory);
822 cl::SetVersionPrinter(PrintVersion);
823 cl::ParseCommandLineOptions(
824 argc, argv,
825 "A tool to bundle several input files of the specified type <type> \n"
826 "referring to the same source file but different targets into a single \n"
827 "one. The resulting file can also be unbundled into different files by \n"
828 "this tool if -unbundle is provided.\n");
829
830 if (Help) {
831 cl::PrintHelpMessage();
832 return 0;
833 }
834
835 auto reportError = [argv](Error E) {
836 logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
837 };
838
839 bool Error = false;
840 if (Unbundle) {
841 if (InputFileNames.size() != 1) {
842 Error = true;
843 reportError(createStringError(
844 errc::invalid_argument,
845 "only one input file supported in unbundling mode"));
846 }
847 if (OutputFileNames.size() != TargetNames.size()) {
848 Error = true;
849 reportError(createStringError(errc::invalid_argument,
850 "number of output files and targets should "
851 "match in unbundling mode"));
852 }
853 } else {
854 if (OutputFileNames.size() != 1) {
855 Error = true;
856 reportError(createStringError(
857 errc::invalid_argument,
858 "only one output file supported in bundling mode"));
859 }
860 if (InputFileNames.size() != TargetNames.size()) {
861 Error = true;
862 reportError(createStringError(
863 errc::invalid_argument,
864 "number of input files and targets should match in bundling mode"));
865 }
866 }
867
868 // Verify that the offload kinds and triples are known. We also check that we
869 // have exactly one host target.
870 unsigned Index = 0u;
871 unsigned HostTargetNum = 0u;
872 for (StringRef Target : TargetNames) {
873 StringRef Kind;
874 StringRef Triple;
875 getOffloadKindAndTriple(Target, Kind, Triple);
876
877 bool KindIsValid = !Kind.empty();
878 KindIsValid = KindIsValid && StringSwitch<bool>(Kind)
879 .Case("host", true)
880 .Case("openmp", true)
881 .Case("hip", true)
882 .Default(false);
883
884 bool TripleIsValid = !Triple.empty();
885 llvm::Triple T(Triple);
886 TripleIsValid &= T.getArch() != Triple::UnknownArch;
887
888 if (!KindIsValid || !TripleIsValid) {
889 Error = true;
890
891 SmallVector<char, 128u> Buf;
892 raw_svector_ostream Msg(Buf);
893 Msg << "invalid target '" << Target << "'";
894 if (!KindIsValid)
895 Msg << ", unknown offloading kind '" << Kind << "'";
896 if (!TripleIsValid)
897 Msg << ", unknown target triple '" << Triple << "'";
898 reportError(createStringError(errc::invalid_argument, Msg.str()));
899 }
900
901 if (KindIsValid && Kind == "host") {
902 ++HostTargetNum;
903 // Save the index of the input that refers to the host.
904 HostInputIndex = Index;
905 }
906
907 ++Index;
908 }
909
910 // Host triple is not really needed for unbundling operation, so do not
911 // treat missing host triple as error if we do unbundling.
912 if ((Unbundle && HostTargetNum > 1) || (!Unbundle && HostTargetNum != 1)) {
913 Error = true;
914 reportError(createStringError(errc::invalid_argument,
915 "expecting exactly one host target but got " +
916 Twine(HostTargetNum)));
917 }
918
919 if (Error)
920 return 1;
921
922 // Save the current executable directory as it will be useful to find other
923 // tools.
924 BundlerExecutable = sys::fs::getMainExecutable(argv[0], &BundlerExecutable);
925
926 if (llvm::Error Err = Unbundle ? UnbundleFiles() : BundleFiles()) {
927 reportError(std::move(Err));
928 return 1;
929 }
930 return 0;
931 }
932