1//===-- clang-linker-wrapper/ClangLinkerWrapper.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// This tool works as a wrapper over a linking job. This tool is used to create
10// linked device images for offloading. It scans the linker's input for embedded
11// device offloading data stored in sections `.llvm.offloading` and extracts it
12// as a temporary file. The extracted device files will then be passed to a
13// device linking job to create a final device image.
14//
15//===----------------------------------------------------------------------===//
16
17#include "clang/Basic/TargetID.h"
18#include "clang/Basic/Version.h"
19#include "llvm/ADT/MapVector.h"
20#include "llvm/BinaryFormat/Magic.h"
21#include "llvm/Bitcode/BitcodeWriter.h"
22#include "llvm/CodeGen/CommandFlags.h"
23#include "llvm/Frontend/Offloading/OffloadWrapper.h"
24#include "llvm/Frontend/Offloading/Utility.h"
25#include "llvm/IR/DiagnosticPrinter.h"
26#include "llvm/IR/Module.h"
27#include "llvm/IRReader/IRReader.h"
28#include "llvm/LTO/LTO.h"
29#include "llvm/MC/TargetRegistry.h"
30#include "llvm/Object/Binary.h"
31#include "llvm/Object/IRObjectFile.h"
32#include "llvm/Object/ObjectFile.h"
33#include "llvm/Object/OffloadBinary.h"
34#include "llvm/Option/ArgList.h"
35#include "llvm/Option/OptTable.h"
36#include "llvm/Option/Option.h"
37#include "llvm/Plugins/PassPlugin.h"
38#include "llvm/Remarks/HotnessThresholdParser.h"
39#include "llvm/Support/CommandLine.h"
40#include "llvm/Support/FileOutputBuffer.h"
41#include "llvm/Support/FileSystem.h"
42#include "llvm/Support/InitLLVM.h"
43#include "llvm/Support/MemoryBuffer.h"
44#include "llvm/Support/Parallel.h"
45#include "llvm/Support/Path.h"
46#include "llvm/Support/Program.h"
47#include "llvm/Support/Signals.h"
48#include "llvm/Support/SourceMgr.h"
49#include "llvm/Support/StringSaver.h"
50#include "llvm/Support/TargetSelect.h"
51#include "llvm/Support/TimeProfiler.h"
52#include "llvm/Support/WithColor.h"
53#include "llvm/Support/raw_ostream.h"
54#include "llvm/Target/TargetMachine.h"
55#include "llvm/TargetParser/Host.h"
56#include <optional>
57
58using namespace llvm;
59using namespace llvm::opt;
60using namespace llvm::object;
61
62// Various tools (e.g., llc and opt) duplicate this series of declarations for
63// options related to passes and remarks.
64
65static cl::opt<bool> RemarksWithHotness(
66 "pass-remarks-with-hotness",
67 cl::desc("With PGO, include profile count in optimization remarks"),
68 cl::Hidden);
69
70static cl::opt<std::optional<uint64_t>, false, remarks::HotnessThresholdParser>
71 RemarksHotnessThreshold(
72 "pass-remarks-hotness-threshold",
73 cl::desc("Minimum profile count required for "
74 "an optimization remark to be output. "
75 "Use 'auto' to apply the threshold from profile summary."),
76 cl::value_desc("N or 'auto'"), cl::init(Val: 0), cl::Hidden);
77
78static cl::opt<std::string>
79 RemarksFilename("pass-remarks-output",
80 cl::desc("Output filename for pass remarks"),
81 cl::value_desc("filename"));
82
83static cl::opt<std::string>
84 RemarksPasses("pass-remarks-filter",
85 cl::desc("Only record optimization remarks from passes whose "
86 "names match the given regular expression"),
87 cl::value_desc("regex"));
88
89static cl::opt<std::string> RemarksFormat(
90 "pass-remarks-format",
91 cl::desc("The format used for serializing remarks (default: YAML)"),
92 cl::value_desc("format"), cl::init(Val: "yaml"));
93
94static cl::list<std::string>
95 PassPlugins("load-pass-plugin",
96 cl::desc("Load passes from plugin library"));
97
98static cl::opt<std::string> PassPipeline(
99 "passes",
100 cl::desc(
101 "A textual description of the pass pipeline. To have analysis passes "
102 "available before a certain pass, add 'require<foo-analysis>'. "
103 "'-passes' overrides the pass pipeline (but not all effects) from "
104 "specifying '--opt-level=O?' (O2 is the default) to "
105 "clang-linker-wrapper. Be sure to include the corresponding "
106 "'default<O?>' in '-passes'."));
107static cl::alias PassPipeline2("p", cl::aliasopt(PassPipeline),
108 cl::desc("Alias for -passes"));
109
110/// Path of the current binary.
111static const char *LinkerExecutable;
112
113/// Save intermediary results.
114static bool SaveTemps = false;
115
116/// Print arguments without executing.
117static bool DryRun = false;
118
119/// Print verbose output.
120static bool Verbose = false;
121
122/// Filename of the executable being created.
123static StringRef ExecutableName;
124
125/// Binary path for the CUDA installation.
126static std::string CudaBinaryPath;
127
128/// Mutex lock to protect writes to shared TempFiles in parallel.
129static std::mutex TempFilesMutex;
130
131/// Temporary files created by the linker wrapper.
132static std::list<SmallString<128>> TempFiles;
133
134/// Codegen flags for LTO backend.
135static codegen::RegisterCodeGenFlags CodeGenFlags;
136
137/// Whether or not to look through symlinks when resolving binaries.
138static bool CanonicalPrefixes = true;
139
140using OffloadingImage = OffloadBinary::OffloadingImage;
141
142namespace llvm {
143// Provide DenseMapInfo so that OffloadKind can be used in a DenseMap.
144template <> struct DenseMapInfo<OffloadKind> {
145 static unsigned getHashValue(const OffloadKind &Val) { return Val; }
146
147 static bool isEqual(const OffloadKind &LHS, const OffloadKind &RHS) {
148 return LHS == RHS;
149 }
150};
151} // namespace llvm
152
153namespace {
154using std::error_code;
155
156/// Must not overlap with llvm::opt::DriverFlag.
157enum WrapperFlags {
158 WrapperOnlyOption = (1 << 4), // Options only used by the linker wrapper.
159 DeviceOnlyOption = (1 << 5), // Options only used for device linking.
160};
161
162enum ID {
163 OPT_INVALID = 0, // This is not an option ID.
164#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
165#include "LinkerWrapperOpts.inc"
166 LastOption
167#undef OPTION
168};
169
170#define OPTTABLE_STR_TABLE_CODE
171#include "LinkerWrapperOpts.inc"
172#undef OPTTABLE_STR_TABLE_CODE
173
174#define OPTTABLE_PREFIXES_TABLE_CODE
175#include "LinkerWrapperOpts.inc"
176#undef OPTTABLE_PREFIXES_TABLE_CODE
177
178static constexpr OptTable::Info InfoTable[] = {
179#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
180#include "LinkerWrapperOpts.inc"
181#undef OPTION
182};
183
184class WrapperOptTable : public opt::GenericOptTable {
185public:
186 WrapperOptTable()
187 : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
188};
189
190const OptTable &getOptTable() {
191 static const WrapperOptTable Table;
192 return Table;
193}
194
195void printCommands(ArrayRef<StringRef> CmdArgs) {
196 if (CmdArgs.empty())
197 return;
198
199 llvm::errs() << " \"" << CmdArgs.front() << "\" ";
200 for (auto IC = std::next(x: CmdArgs.begin()), IE = CmdArgs.end(); IC != IE; ++IC)
201 llvm::errs() << *IC << (std::next(x: IC) != IE ? " " : "\n");
202}
203
204[[noreturn]] void reportError(Error E) {
205 outs().flush();
206 logAllUnhandledErrors(E: std::move(E),
207 OS&: WithColor::error(OS&: errs(), Prefix: LinkerExecutable));
208 exit(EXIT_FAILURE);
209}
210
211std::string getExecutableDir(const char *Name) {
212 if (!CanonicalPrefixes)
213 return sys::path::parent_path(path: LinkerExecutable).str();
214 void *Ptr = reinterpret_cast<void *>(&getExecutableDir);
215 return sys::path::parent_path(path: sys::fs::getMainExecutable(argv0: Name, MainExecAddr: Ptr)).str();
216}
217
218/// Get a temporary filename suitable for output.
219Expected<StringRef> createOutputFile(const Twine &Prefix, StringRef Extension) {
220 std::scoped_lock<decltype(TempFilesMutex)> Lock(TempFilesMutex);
221 SmallString<128> OutputFile;
222 std::string PrefixStr = clang::sanitizeTargetIDInFileName(TargetID: Prefix.str());
223
224 if (SaveTemps) {
225 (PrefixStr + "." + Extension).toNullTerminatedStringRef(Out&: OutputFile);
226 } else {
227 if (std::error_code EC = sys::fs::createTemporaryFile(
228 Prefix: sys::path::filename(path: PrefixStr), Suffix: Extension, ResultPath&: OutputFile))
229 return createFileError(F: OutputFile, EC);
230 }
231
232 TempFiles.emplace_back(args: std::move(OutputFile));
233 return TempFiles.back();
234}
235
236/// Execute the command \p ExecutablePath with the arguments \p Args.
237Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) {
238 if (Verbose || DryRun)
239 printCommands(CmdArgs: Args);
240
241 if (DryRun)
242 return Error::success();
243
244 // If the command line fits within system limits, execute directly.
245 if (sys::commandLineFitsWithinSystemLimits(Program: ExecutablePath, Args)) {
246 if (sys::ExecuteAndWait(Program: ExecutablePath, Args))
247 return createStringError(
248 Fmt: "'%s' failed", Vals: sys::path::filename(path: ExecutablePath).str().c_str());
249 return Error::success();
250 }
251
252 // Write the arguments to a response file and pass that instead.
253 auto TempFileOrErr = createOutputFile(Prefix: "response", Extension: "rsp");
254 if (!TempFileOrErr)
255 return TempFileOrErr.takeError();
256
257 SmallString<256> Contents;
258 raw_svector_ostream OS(Contents);
259 for (StringRef Arg : llvm::drop_begin(RangeOrContainer&: Args)) {
260 sys::printArg(OS, Arg, /*Quote=*/true);
261 OS << " ";
262 }
263
264 if (std::error_code EC = sys::writeFileWithEncoding(FileName: *TempFileOrErr, Contents))
265 return createStringError(Fmt: "failed to write response file: %s",
266 Vals: EC.message().c_str());
267
268 std::string ResponseFile = ("@" + *TempFileOrErr).str();
269 SmallVector<StringRef, 2> NewArgs = {Args.front(), ResponseFile};
270 if (sys::ExecuteAndWait(Program: ExecutablePath, Args: NewArgs))
271 return createStringError(Fmt: "'%s' failed",
272 Vals: sys::path::filename(path: ExecutablePath).str().c_str());
273 return Error::success();
274}
275
276Expected<std::string> findProgram(StringRef Name, ArrayRef<StringRef> Paths) {
277
278 ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths);
279 if (!Path)
280 Path = sys::findProgramByName(Name);
281 if (!Path && DryRun)
282 return Name.str();
283 if (!Path)
284 return createStringError(EC: Path.getError(),
285 S: "Unable to find '" + Name + "' in path");
286 return *Path;
287}
288
289bool linkerSupportsLTO(const ArgList &Args) {
290 llvm::Triple Triple(Args.getLastArgValue(Id: OPT_triple_EQ));
291 return Triple.isNVPTX() || Triple.isAMDGPU() ||
292 (!Triple.isGPU() &&
293 Args.getLastArgValue(Id: OPT_linker_path_EQ).ends_with(Suffix: "lld"));
294}
295
296/// Returns the hashed value for a constant string.
297std::string getHash(StringRef Str) {
298 llvm::MD5 Hasher;
299 llvm::MD5::MD5Result Hash;
300 Hasher.update(Str);
301 Hasher.final(Result&: Hash);
302 return llvm::utohexstr(X: Hash.low(), /*LowerCase=*/true);
303}
304
305/// Renames offloading entry sections in a relocatable link so they do not
306/// conflict with a later link job.
307Error relocateOffloadSection(const ArgList &Args, StringRef Output) {
308 llvm::Triple Triple(
309 Args.getLastArgValue(Id: OPT_host_triple_EQ, Default: sys::getDefaultTargetTriple()));
310 if (Triple.isOSWindows())
311 return createStringError(
312 Fmt: "Relocatable linking is not supported on COFF targets");
313
314 Expected<std::string> ObjcopyPath =
315 findProgram(Name: "llvm-objcopy", Paths: {getExecutableDir(Name: "llvm-objcopy")});
316 if (!ObjcopyPath)
317 return ObjcopyPath.takeError();
318
319 // Use the linker output file to get a unique hash. This creates a unique
320 // identifier to rename the sections to that is deterministic to the contents.
321 auto BufferOrErr = DryRun ? MemoryBuffer::getMemBuffer(InputData: "")
322 : MemoryBuffer::getFileOrSTDIN(Filename: Output);
323 if (!BufferOrErr)
324 return createStringError(Fmt: "Failed to open %s", Vals: Output.str().c_str());
325 std::string Suffix = "_" + getHash(Str: (*BufferOrErr)->getBuffer());
326
327 SmallVector<StringRef> ObjcopyArgs = {
328 *ObjcopyPath,
329 Output,
330 };
331
332 // Remove the old .llvm.offloading section to prevent further linking.
333 ObjcopyArgs.emplace_back(Args: "--remove-section");
334 ObjcopyArgs.emplace_back(Args: ".llvm.offloading");
335 StringRef Prefix = "llvm";
336 auto Section = (Prefix + "_offload_entries").str();
337 // Rename the offloading entries to make them private to this link unit.
338 ObjcopyArgs.emplace_back(Args: "--rename-section");
339 ObjcopyArgs.emplace_back(
340 Args: Args.MakeArgString(Str: Section + "=" + Section + Suffix));
341
342 // Rename the __start_ / __stop_ symbols appropriately to iterate over the
343 // newly renamed section containing the offloading entries.
344 ObjcopyArgs.emplace_back(Args: "--redefine-sym");
345 ObjcopyArgs.emplace_back(Args: Args.MakeArgString(Str: "__start_" + Section + "=" +
346 "__start_" + Section + Suffix));
347 ObjcopyArgs.emplace_back(Args: "--redefine-sym");
348 ObjcopyArgs.emplace_back(Args: Args.MakeArgString(Str: "__stop_" + Section + "=" +
349 "__stop_" + Section + Suffix));
350
351 if (Error Err = executeCommands(ExecutablePath: *ObjcopyPath, Args: ObjcopyArgs))
352 return Err;
353
354 return Error::success();
355}
356
357/// Runs the wrapped linker job with the newly created input.
358Error runLinker(ArrayRef<StringRef> Files, const ArgList &Args) {
359 llvm::TimeTraceScope TimeScope("Execute host linker");
360
361 // Render the linker arguments and add the newly created image. We add it
362 // after the output file to ensure it is linked with the correct libraries.
363 StringRef LinkerPath = Args.getLastArgValue(Id: OPT_linker_path_EQ);
364 if (LinkerPath.empty())
365 return createStringError(Fmt: "linker path missing, must pass 'linker-path'");
366 ArgStringList NewLinkerArgs;
367 for (const opt::Arg *Arg : Args) {
368 // Do not forward arguments only intended for the linker wrapper.
369 if (Arg->getOption().hasFlag(Val: WrapperOnlyOption))
370 continue;
371
372 Arg->render(Args, Output&: NewLinkerArgs);
373 if (Arg->getOption().matches(ID: OPT_o) || Arg->getOption().matches(ID: OPT_out))
374 llvm::transform(Range&: Files, d_first: std::back_inserter(x&: NewLinkerArgs),
375 F: [&](StringRef A) { return Args.MakeArgString(Str: A); });
376 }
377
378 SmallVector<StringRef> LinkerArgs({LinkerPath});
379 for (StringRef Arg : NewLinkerArgs)
380 LinkerArgs.push_back(Elt: Arg);
381 if (Error Err = executeCommands(ExecutablePath: LinkerPath, Args: LinkerArgs))
382 return Err;
383
384 if (Args.hasArg(Ids: OPT_relocatable))
385 return relocateOffloadSection(Args, Output: ExecutableName);
386
387 return Error::success();
388}
389
390void printVersion(raw_ostream &OS) {
391 OS << clang::getClangToolFullVersion(ToolName: "clang-linker-wrapper") << '\n';
392}
393
394namespace nvptx {
395Expected<StringRef>
396fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,
397 const ArgList &Args) {
398 llvm::TimeTraceScope TimeScope("NVPTX fatbinary");
399 // NVPTX uses the fatbinary program to bundle the linked images.
400 Expected<std::string> FatBinaryPath =
401 findProgram(Name: "fatbinary", Paths: {CudaBinaryPath + "/bin"});
402 if (!FatBinaryPath)
403 return FatBinaryPath.takeError();
404
405 llvm::Triple Triple(
406 Args.getLastArgValue(Id: OPT_host_triple_EQ, Default: sys::getDefaultTargetTriple()));
407
408 // Create a new file to write the linked device image to.
409 auto TempFileOrErr = createOutputFile(Prefix: ExecutableName, Extension: "fatbin");
410 if (!TempFileOrErr)
411 return TempFileOrErr.takeError();
412
413 SmallVector<StringRef, 16> CmdArgs;
414 CmdArgs.push_back(Elt: *FatBinaryPath);
415 CmdArgs.push_back(Elt: Triple.isArch64Bit() ? "-64" : "-32");
416 CmdArgs.push_back(Elt: "--create");
417 CmdArgs.push_back(Elt: *TempFileOrErr);
418 for (const auto &[File, Arch] : InputFiles)
419 CmdArgs.push_back(Elt: Args.MakeArgString(
420 Str: "--image3=kind=elf,sm=" + Arch.drop_front(N: 3) + ",file=" + File));
421
422 if (Error Err = executeCommands(ExecutablePath: *FatBinaryPath, Args: CmdArgs))
423 return std::move(Err);
424
425 return *TempFileOrErr;
426}
427} // namespace nvptx
428
429namespace amdgcn {
430
431// Constructs a triple string for clang offload bundler.
432// NOTE: copied from HIPUtility.cpp.
433static std::string normalizeForBundler(const llvm::Triple &T,
434 bool HasTargetID) {
435 return HasTargetID ? (T.getArchName() + "-" + T.getVendorName() + "-" +
436 T.getOSName() + "-" + T.getEnvironmentName())
437 .str()
438 : T.normalize(Form: llvm::Triple::CanonicalForm::FOUR_IDENT);
439}
440
441Expected<StringRef>
442fatbinary(ArrayRef<std::tuple<StringRef, StringRef, StringRef>> InputFiles,
443 const ArgList &Args) {
444 llvm::TimeTraceScope TimeScope("AMDGPU Fatbinary");
445
446 // AMDGPU uses the clang-offload-bundler to bundle the linked images.
447 Expected<std::string> OffloadBundlerPath = findProgram(
448 Name: "clang-offload-bundler", Paths: {getExecutableDir(Name: "clang-offload-bundler")});
449 if (!OffloadBundlerPath)
450 return OffloadBundlerPath.takeError();
451
452 // Create a new file to write the linked device image to.
453 auto TempFileOrErr = createOutputFile(Prefix: ExecutableName, Extension: "hipfb");
454 if (!TempFileOrErr)
455 return TempFileOrErr.takeError();
456
457 BumpPtrAllocator Alloc;
458 StringSaver Saver(Alloc);
459
460 SmallVector<StringRef, 16> CmdArgs;
461 CmdArgs.push_back(Elt: *OffloadBundlerPath);
462 CmdArgs.push_back(Elt: "-type=o");
463 CmdArgs.push_back(Elt: "-bundle-align=4096");
464
465 if (Args.hasArg(Ids: OPT_compress))
466 CmdArgs.push_back(Elt: "-compress");
467 if (auto *Arg = Args.getLastArg(Ids: OPT_compression_level_eq))
468 CmdArgs.push_back(
469 Elt: Args.MakeArgString(Str: Twine("-compression-level=") + Arg->getValue()));
470
471 llvm::Triple HostTriple(
472 Args.getLastArgValue(Id: OPT_host_triple_EQ, Default: sys::getDefaultTargetTriple()));
473 SmallVector<StringRef> Targets = {
474 Saver.save(S: "-targets=host-" + HostTriple.normalize())};
475 for (const auto &[File, TripleRef, Arch] : InputFiles) {
476 std::string NormalizedTriple =
477 normalizeForBundler(T: Triple(TripleRef), HasTargetID: !Arch.empty());
478 Targets.push_back(Elt: Saver.save(S: "hip-" + NormalizedTriple + "-" + Arch));
479 }
480 CmdArgs.push_back(Elt: Saver.save(S: llvm::join(R&: Targets, Separator: ",")));
481
482#ifdef _WIN32
483 CmdArgs.push_back("-input=NUL");
484#else
485 CmdArgs.push_back(Elt: "-input=/dev/null");
486#endif
487 for (const auto &[File, Triple, Arch] : InputFiles)
488 CmdArgs.push_back(Elt: Saver.save(S: "-input=" + File));
489
490 CmdArgs.push_back(Elt: Saver.save(S: "-output=" + *TempFileOrErr));
491
492 if (Error Err = executeCommands(ExecutablePath: *OffloadBundlerPath, Args: CmdArgs))
493 return std::move(Err);
494
495 return *TempFileOrErr;
496}
497} // namespace amdgcn
498
499namespace generic {
500Expected<StringRef> clang(ArrayRef<StringRef> InputFiles, const ArgList &Args,
501 uint16_t ActiveOffloadKindMask) {
502 llvm::TimeTraceScope TimeScope("Clang");
503 // Use `clang` to invoke the appropriate device tools.
504 Expected<std::string> ClangPath =
505 findProgram(Name: "clang", Paths: {getExecutableDir(Name: "clang")});
506 if (!ClangPath)
507 return ClangPath.takeError();
508
509 const llvm::Triple Triple(Args.getLastArgValue(Id: OPT_triple_EQ));
510 StringRef Arch = Args.getLastArgValue(Id: OPT_arch_EQ);
511 // Create a new file to write the linked device image to. Assume that the
512 // input filename already has the device and architecture.
513 std::string OutputFileBase =
514 "." + Triple.getArchName().str() + "." + Arch.str();
515 auto TempFileOrErr = createOutputFile(Prefix: ExecutableName + OutputFileBase, Extension: "img");
516 if (!TempFileOrErr)
517 return TempFileOrErr.takeError();
518
519 SmallVector<StringRef, 16> CmdArgs{
520 *ClangPath,
521 "--no-default-config",
522 "-o",
523 *TempFileOrErr,
524 // Without -dumpdir, Clang will place auxiliary output files in the
525 // temporary directory of TempFileOrErr, where they will not easily be
526 // found by the user and might eventually be automatically removed. Tell
527 // Clang to instead place them alongside the final executable.
528 "-dumpdir",
529 Args.MakeArgString(Str: ExecutableName + OutputFileBase + ".img."),
530 Args.MakeArgString(Str: "--target=" + Triple.getTriple()),
531 };
532
533 if (!Arch.empty())
534 Triple.isAMDGPU() ? CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-mcpu=" + Arch))
535 : CmdArgs.push_back(Elt: Args.MakeArgString(Str: "-march=" + Arch));
536
537 // Forward all of the `--offload-opt` and `-mllvm` options to the device.
538 for (auto &Arg : Args.filtered(Ids: OPT_offload_opt_eq_minus, Ids: OPT_mllvm))
539 CmdArgs.append(
540 IL: {"-Xlinker",
541 Args.MakeArgString(Str: "--plugin-opt=" + StringRef(Arg->getValue()))});
542
543 if (!Triple.isNVPTX() && !Triple.isSPIRV())
544 CmdArgs.push_back(Elt: "-Wl,--no-undefined");
545
546 for (StringRef InputFile : InputFiles)
547 CmdArgs.push_back(Elt: InputFile);
548
549 // If this is CPU offloading we copy the input libraries.
550 if (!Triple.isGPU()) {
551 CmdArgs.push_back(Elt: "-Wl,-Bsymbolic");
552 CmdArgs.push_back(Elt: "-shared");
553 ArgStringList LinkerArgs;
554 for (const opt::Arg *Arg :
555 Args.filtered(Ids: OPT_INPUT, Ids: OPT_library, Ids: OPT_library_path, Ids: OPT_rpath,
556 Ids: OPT_whole_archive, Ids: OPT_no_whole_archive)) {
557 // Sometimes needed libraries are passed by name, such as when using
558 // sanitizers. We need to check the file magic for any libraries.
559 if (Arg->getOption().matches(ID: OPT_INPUT)) {
560 if (!sys::fs::exists(Path: Arg->getValue()) ||
561 sys::fs::is_directory(Path: Arg->getValue()))
562 continue;
563
564 file_magic Magic;
565 if (auto EC = identify_magic(path: Arg->getValue(), result&: Magic))
566 return createStringError(Fmt: "Failed to open %s", Vals: Arg->getValue());
567 if (Magic != file_magic::archive &&
568 Magic != file_magic::elf_shared_object)
569 continue;
570 }
571 if (Arg->getOption().matches(ID: OPT_whole_archive))
572 LinkerArgs.push_back(Elt: Args.MakeArgString(Str: "-Wl,--whole-archive"));
573 else if (Arg->getOption().matches(ID: OPT_no_whole_archive))
574 LinkerArgs.push_back(Elt: Args.MakeArgString(Str: "-Wl,--no-whole-archive"));
575 else
576 Arg->render(Args, Output&: LinkerArgs);
577 }
578 llvm::append_range(C&: CmdArgs, R&: LinkerArgs);
579 }
580
581 // Pass on -mllvm options to the linker invocation.
582 for (const opt::Arg *Arg : Args.filtered(Ids: OPT_mllvm))
583 CmdArgs.append(IL: {"-Xlinker", Args.MakeArgString(
584 Str: "-mllvm=" + StringRef(Arg->getValue()))});
585
586 if (SaveTemps && linkerSupportsLTO(Args))
587 CmdArgs.push_back(Elt: "-Wl,--save-temps");
588
589 if (Args.hasArg(Ids: OPT_embed_bitcode))
590 CmdArgs.push_back(Elt: "-Wl,--lto-emit-llvm");
591
592 // For linking device code with the SYCL offload kind, special handling is
593 // required. Passing --sycl-link to clang results in a call to
594 // clang-sycl-linker. Additional linker flags required by clang-sycl-linker
595 // will be communicated via the -Xlinker option.
596 if (ActiveOffloadKindMask & OFK_SYCL) {
597 CmdArgs.push_back(Elt: "--sycl-link");
598 CmdArgs.append(
599 IL: {"-Xlinker", Args.MakeArgString(Str: "-triple=" + Triple.getTriple())});
600 CmdArgs.append(IL: {"-Xlinker", Args.MakeArgString(Str: "-arch=" + Arch)});
601 }
602
603 for (StringRef Arg : Args.getAllArgValues(Id: OPT_linker_arg_EQ))
604 CmdArgs.append(IL: {"-Xlinker", Args.MakeArgString(Str: Arg)});
605 for (StringRef Arg : Args.getAllArgValues(Id: OPT_compiler_arg_EQ))
606 CmdArgs.push_back(Elt: Args.MakeArgString(Str: Arg));
607
608 if (Error Err = executeCommands(ExecutablePath: *ClangPath, Args: CmdArgs))
609 return std::move(Err);
610
611 return *TempFileOrErr;
612}
613} // namespace generic
614
615Expected<StringRef> linkDevice(ArrayRef<StringRef> InputFiles,
616 const ArgList &Args,
617 uint16_t ActiveOffloadKindMask) {
618 const llvm::Triple Triple(Args.getLastArgValue(Id: OPT_triple_EQ));
619 switch (Triple.getArch()) {
620 case Triple::nvptx:
621 case Triple::nvptx64:
622 case Triple::amdgpu:
623 case Triple::x86:
624 case Triple::x86_64:
625 case Triple::aarch64:
626 case Triple::aarch64_be:
627 case Triple::ppc64:
628 case Triple::ppc64le:
629 case Triple::spirv64:
630 case Triple::systemz:
631 case Triple::loongarch64:
632 return generic::clang(InputFiles, Args, ActiveOffloadKindMask);
633 default:
634 return createStringError(S: Triple.getArchName() +
635 " linking is not supported");
636 }
637}
638
639Error containerizeRawImage(std::unique_ptr<MemoryBuffer> &Img, OffloadKind Kind,
640 const ArgList &Args) {
641 llvm::Triple Triple(Args.getLastArgValue(Id: OPT_triple_EQ));
642 if (Kind == OFK_OpenMP && Triple.isSPIRV() &&
643 Triple.getVendor() == llvm::Triple::Intel)
644 return offloading::intel::containerizeOpenMPSPIRVImage(Binary&: Img, Triple);
645 return Error::success();
646}
647
648Expected<StringRef> writeOffloadFile(const OffloadFile &File) {
649 const OffloadBinary &Binary = *File.getBinary();
650
651 StringRef Prefix =
652 sys::path::stem(path: Binary.getMemoryBufferRef().getBufferIdentifier());
653 SmallString<128> Filename;
654 (Prefix + "-" + Binary.getTriple() + "-" + Binary.getArch())
655 .toVector(Out&: Filename);
656 auto TempFileOrErr = createOutputFile(Prefix: Filename, Extension: "o");
657 if (!TempFileOrErr)
658 return TempFileOrErr.takeError();
659
660 Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
661 FileOutputBuffer::create(FilePath: *TempFileOrErr, Size: Binary.getImage().size());
662 if (!OutputOrErr)
663 return OutputOrErr.takeError();
664 std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
665 llvm::copy(Range: Binary.getImage(), Out: Output->getBufferStart());
666 if (Error E = Output->commit())
667 return std::move(E);
668
669 return *TempFileOrErr;
670}
671
672// Compile the module to an object file using the appropriate target machine for
673// the host triple.
674Expected<StringRef> compileModule(Module &M, OffloadKind Kind) {
675 llvm::TimeTraceScope TimeScope("Compile module");
676 std::string Msg;
677 const Target *T = TargetRegistry::lookupTarget(TheTriple: M.getTargetTriple(), Error&: Msg);
678 if (!T)
679 return createStringError(S: Msg);
680
681 auto Options =
682 codegen::InitTargetOptionsFromCodeGenFlags(TheTriple: M.getTargetTriple());
683 StringRef CPU = "";
684 StringRef Features = "";
685 std::unique_ptr<TargetMachine> TM(
686 T->createTargetMachine(TT: M.getTargetTriple(), CPU, Features, Options,
687 RM: Reloc::PIC_, CM: M.getCodeModel()));
688
689 if (M.getDataLayout().isDefault())
690 M.setDataLayout(TM->createDataLayout());
691
692 int FD = -1;
693 auto TempFileOrErr = createOutputFile(
694 Prefix: ExecutableName + "." + getOffloadKindName(Name: Kind) + ".image.wrapper", Extension: "o");
695 if (!TempFileOrErr)
696 return TempFileOrErr.takeError();
697 if (std::error_code EC = sys::fs::openFileForWrite(Name: *TempFileOrErr, ResultFD&: FD))
698 return errorCodeToError(EC);
699
700 auto OS = std::make_unique<llvm::raw_fd_ostream>(args&: FD, args: true);
701
702 legacy::PassManager CodeGenPasses;
703 TargetLibraryInfoImpl TLII(M.getTargetTriple());
704 CodeGenPasses.add(P: new TargetLibraryInfoWrapperPass(TLII));
705 if (TM->addPassesToEmitFile(CodeGenPasses, *OS, nullptr,
706 CodeGenFileType::ObjectFile))
707 return createStringError(Fmt: "Failed to execute host backend");
708 CodeGenPasses.run(M);
709
710 return *TempFileOrErr;
711}
712
713/// Performs the wrapping stage with individual tool invocations for verbose
714/// printing.
715Expected<StringRef>
716wrapDeviceImagesVerbose(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,
717 const ArgList &Args, OffloadKind Kind) {
718 Expected<std::string> WrapperPath = findProgram(
719 Name: "llvm-offload-wrapper", Paths: {getExecutableDir(Name: "llvm-offload-wrapper")});
720 if (!WrapperPath)
721 return WrapperPath.takeError();
722
723 llvm::Triple Triple(
724 Args.getLastArgValue(Id: OPT_host_triple_EQ, Default: sys::getDefaultTargetTriple()));
725
726 // Generate the runtime registration bitcode from the bundled images.
727 auto BitcodeOrErr = createOutputFile(
728 Prefix: ExecutableName + "." + getOffloadKindName(Name: Kind) + ".image.wrapper", Extension: "bc");
729 if (!BitcodeOrErr)
730 return BitcodeOrErr.takeError();
731
732 SmallVector<StringRef> WrapperArgs = {
733 *WrapperPath,
734 Args.MakeArgString(Str: "--kind=" + getOffloadKindName(Name: Kind)),
735 Args.MakeArgString(Str: "--triple=" + Triple.getTriple()),
736 "-o",
737 *BitcodeOrErr,
738 };
739 if (Kind == OFK_OpenMP && Args.hasArg(Ids: OPT_relocatable))
740 WrapperArgs.push_back(Elt: "--relocatable");
741 for (const auto &Buffer : Buffers)
742 WrapperArgs.push_back(Elt: Buffer->getBufferIdentifier());
743
744 if (Error Err = executeCommands(ExecutablePath: *WrapperPath, Args: WrapperArgs))
745 return std::move(Err);
746
747 // Compile the generated registration bitcode into a host object.
748 Expected<std::string> ClangPath =
749 findProgram(Name: "clang", Paths: {getExecutableDir(Name: "clang")});
750 if (!ClangPath)
751 return ClangPath.takeError();
752
753 auto ObjectOrErr = createOutputFile(
754 Prefix: ExecutableName + "." + getOffloadKindName(Name: Kind) + ".image.wrapper", Extension: "o");
755 if (!ObjectOrErr)
756 return ObjectOrErr.takeError();
757
758 const StringRef ClangArgs[] = {
759 *ClangPath,
760 "--no-default-config",
761 Args.MakeArgString(Str: "--target=" + Triple.getTriple()),
762 "-c",
763 "-fPIC",
764 "-o",
765 *ObjectOrErr,
766 *BitcodeOrErr,
767 };
768 if (Error Err = executeCommands(ExecutablePath: *ClangPath, Args: ClangArgs))
769 return std::move(Err);
770
771 return *ObjectOrErr;
772}
773
774/// Creates the object file containing the device image and runtime
775/// registration code from the device images stored in \p Images.
776Expected<StringRef>
777wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,
778 const ArgList &Args, OffloadKind Kind) {
779 llvm::TimeTraceScope TimeScope("Wrap bundled images");
780
781 // We use the discrete tools if we are in verbose mode with '--save-temps'.
782 if (Verbose && SaveTemps && !Args.hasArg(Ids: OPT_print_wrapped_module))
783 return wrapDeviceImagesVerbose(Buffers, Args, Kind);
784
785 SmallVector<ArrayRef<char>, 4> BuffersToWrap;
786 for (const auto &Buffer : Buffers)
787 BuffersToWrap.emplace_back(
788 Args: ArrayRef<char>(Buffer->getBufferStart(), Buffer->getBufferSize()));
789
790 LLVMContext Context;
791 Module M("offload.wrapper.module", Context);
792 M.setTargetTriple(Triple(
793 Args.getLastArgValue(Id: OPT_host_triple_EQ, Default: sys::getDefaultTargetTriple())));
794
795 switch (Kind) {
796 case OFK_OpenMP:
797 if (Error Err = offloading::wrapOpenMPBinaries(
798 M, Images: BuffersToWrap, EntryArray: offloading::getOffloadEntryArray(M),
799 /*Suffix=*/"", /*Relocatable=*/Args.hasArg(Ids: OPT_relocatable)))
800 return std::move(Err);
801 break;
802 case OFK_Cuda:
803 if (Error Err = offloading::wrapCudaBinary(
804 M, Images: BuffersToWrap.front(), EntryArray: offloading::getOffloadEntryArray(M),
805 /*Suffix=*/"", /*EmitSurfacesAndTextures=*/false))
806 return std::move(Err);
807 break;
808 case OFK_HIP:
809 if (Error Err = offloading::wrapHIPBinary(
810 M, Images: BuffersToWrap.front(), EntryArray: offloading::getOffloadEntryArray(M)))
811 return std::move(Err);
812 break;
813 case OFK_SYCL: {
814 // TODO: fill these options once the Driver supports them.
815 offloading::SYCLJITOptions Options;
816 if (Error Err =
817 offloading::wrapSYCLBinaries(M, Buffer: BuffersToWrap.front(), Options))
818 return std::move(Err);
819 break;
820 }
821 default:
822 return createStringError(S: getOffloadKindName(Name: Kind) +
823 " wrapping is not supported");
824 }
825
826 if (Args.hasArg(Ids: OPT_print_wrapped_module))
827 errs() << M;
828 if (Args.hasArg(Ids: OPT_save_temps)) {
829 int FD = -1;
830 auto TempFileOrErr = createOutputFile(
831 Prefix: ExecutableName + "." + getOffloadKindName(Name: Kind) + ".image.wrapper",
832 Extension: "bc");
833 if (!TempFileOrErr)
834 return TempFileOrErr.takeError();
835 if (std::error_code EC = sys::fs::openFileForWrite(Name: *TempFileOrErr, ResultFD&: FD))
836 return errorCodeToError(EC);
837 llvm::raw_fd_ostream OS(FD, true);
838 WriteBitcodeToFile(M, Out&: OS);
839 }
840
841 auto FileOrErr = compileModule(M, Kind);
842 if (!FileOrErr)
843 return FileOrErr.takeError();
844 return *FileOrErr;
845}
846
847/// Perform the OpenMP bundling with 'llvm-offload-binary' in verbose mode.
848Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
849bundleOpenMPVerbose(ArrayRef<OffloadingImage> Images) {
850 Expected<std::string> OffloadBinaryPath = findProgram(
851 Name: "llvm-offload-binary", Paths: {getExecutableDir(Name: "llvm-offload-binary")});
852 if (!OffloadBinaryPath)
853 return OffloadBinaryPath.takeError();
854
855 BumpPtrAllocator Alloc;
856 StringSaver Saver(Alloc);
857 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
858 for (const OffloadingImage &Image : Images) {
859 StringRef ImageFile = Image.Image->getBufferIdentifier();
860 auto BinaryOrErr =
861 createOutputFile(Prefix: sys::path::stem(path: ImageFile) + "." +
862 getOffloadKindName(Name: Image.TheOffloadKind),
863 Extension: "offload");
864 if (!BinaryOrErr)
865 return BinaryOrErr.takeError();
866
867 std::string ImageArg = ("--image=file=" + ImageFile +
868 ",kind=" + getOffloadKindName(Name: Image.TheOffloadKind))
869 .str();
870 for (const auto &[Key, Value] : Image.StringData)
871 ImageArg += ("," + Key + "=" + Value).str();
872
873 SmallVector<StringRef> CmdArgs = {*OffloadBinaryPath, "-o", *BinaryOrErr,
874 Saver.save(S: ImageArg)};
875 if (Error Err = executeCommands(ExecutablePath: *OffloadBinaryPath, Args: CmdArgs))
876 return std::move(Err);
877
878 auto BufferOrErr = MemoryBuffer::getFileOrSTDIN(Filename: *BinaryOrErr);
879 if (std::error_code EC = BufferOrErr.getError()) {
880 if (DryRun)
881 BufferOrErr = MemoryBuffer::getMemBuffer(InputData: "", BufferName: *BinaryOrErr);
882 else
883 return createFileError(F: *BinaryOrErr, EC);
884 }
885 Buffers.emplace_back(Args: std::move(*BufferOrErr));
886 }
887
888 return std::move(Buffers);
889}
890
891Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
892bundleOpenMP(ArrayRef<OffloadingImage> Images) {
893 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
894 for (const OffloadingImage &Image : Images)
895 Buffers.emplace_back(
896 Args: MemoryBuffer::getMemBufferCopy(InputData: OffloadBinary::write(OffloadingData: Image)));
897
898 return std::move(Buffers);
899}
900
901Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
902bundleSYCL(ArrayRef<OffloadingImage> Images) {
903 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
904 for (const OffloadingImage &Image : Images) {
905 // clang-sycl-linker packs outputs into one binary blob. Therefore, it is
906 // passed to Offload Wrapper as is.
907 StringRef S(Image.Image->getBufferStart(), Image.Image->getBufferSize());
908 Buffers.emplace_back(
909 Args: MemoryBuffer::getMemBufferCopy(InputData: S, BufferName: Image.Image->getBufferIdentifier()));
910 }
911
912 return std::move(Buffers);
913}
914
915Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
916bundleCuda(ArrayRef<OffloadingImage> Images, const ArgList &Args) {
917 SmallVector<std::pair<StringRef, StringRef>, 4> InputFiles;
918 for (const OffloadingImage &Image : Images)
919 InputFiles.emplace_back(Args: std::make_pair(x: Image.Image->getBufferIdentifier(),
920 y: Image.StringData.lookup(Key: "arch")));
921
922 auto FileOrErr = nvptx::fatbinary(InputFiles, Args);
923 if (!FileOrErr)
924 return FileOrErr.takeError();
925
926 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError =
927 llvm::MemoryBuffer::getFileOrSTDIN(Filename: *FileOrErr);
928
929 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
930 if (std::error_code EC = ImageOrError.getError()) {
931 if (DryRun)
932 ImageOrError = MemoryBuffer::getMemBuffer(InputData: "", BufferName: *FileOrErr);
933 else
934 return createFileError(F: *FileOrErr, EC);
935 }
936 Buffers.emplace_back(Args: std::move(*ImageOrError));
937
938 return std::move(Buffers);
939}
940
941Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
942bundleHIP(ArrayRef<OffloadingImage> Images, const ArgList &Args) {
943 SmallVector<std::tuple<StringRef, StringRef, StringRef>, 4> InputFiles;
944 for (const OffloadingImage &Image : Images)
945 InputFiles.emplace_back(Args: std::make_tuple(args: Image.Image->getBufferIdentifier(),
946 args: Image.StringData.lookup(Key: "triple"),
947 args: Image.StringData.lookup(Key: "arch")));
948
949 auto FileOrErr = amdgcn::fatbinary(InputFiles, Args);
950 if (!FileOrErr)
951 return FileOrErr.takeError();
952
953 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError =
954 llvm::MemoryBuffer::getFileOrSTDIN(Filename: *FileOrErr);
955
956 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
957 if (std::error_code EC = ImageOrError.getError()) {
958 if (DryRun)
959 ImageOrError = MemoryBuffer::getMemBuffer(InputData: "", BufferName: *FileOrErr);
960 else
961 return createFileError(F: *FileOrErr, EC);
962 }
963 Buffers.emplace_back(Args: std::move(*ImageOrError));
964
965 return std::move(Buffers);
966}
967
968/// Transforms the input \p Images into the binary format the runtime expects
969/// for the given \p Kind.
970Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
971bundleLinkedOutput(ArrayRef<OffloadingImage> Images, const ArgList &Args,
972 OffloadKind Kind) {
973 llvm::TimeTraceScope TimeScope("Bundle linked output");
974 switch (Kind) {
975 case OFK_OpenMP:
976 return (Verbose && SaveTemps) ? bundleOpenMPVerbose(Images)
977 : bundleOpenMP(Images);
978 case OFK_SYCL:
979 return bundleSYCL(Images);
980 case OFK_Cuda:
981 return bundleCuda(Images, Args);
982 case OFK_HIP:
983 return bundleHIP(Images, Args);
984 default:
985 return createStringError(S: getOffloadKindName(Name: Kind) +
986 " bundling is not supported");
987 }
988}
989
990/// Returns a new ArgList containing arguments used for the device linking
991/// phase.
992DerivedArgList getLinkerArgs(ArrayRef<OffloadFile> Input,
993 const InputArgList &Args) {
994 DerivedArgList DAL(Args);
995 for (Arg *A : Args)
996 DAL.append(A);
997
998 // Set the subarchitecture and target triple for this compilation.
999 const OptTable &Tbl = getOptTable();
1000 StringRef Arch = Args.MakeArgString(Str: Input.front().getBinary()->getArch());
1001 DAL.AddJoinedArg(BaseArg: nullptr, Opt: Tbl.getOption(Opt: OPT_arch_EQ),
1002 Value: Arch == "generic" ? "" : Arch);
1003 DAL.AddJoinedArg(BaseArg: nullptr, Opt: Tbl.getOption(Opt: OPT_triple_EQ),
1004 Value: Args.MakeArgString(Str: Input.front().getBinary()->getTriple()));
1005
1006 // If every input file is bitcode we have whole program visibility as we
1007 // do only support static linking with bitcode.
1008 auto ContainsBitcode = [](const OffloadFile &F) {
1009 return identify_magic(magic: F.getBinary()->getImage()) == file_magic::bitcode;
1010 };
1011 if (llvm::all_of(Range&: Input, P: ContainsBitcode))
1012 DAL.AddFlagArg(BaseArg: nullptr, Opt: Tbl.getOption(Opt: OPT_whole_program));
1013
1014 // Forward '-Xoffload-linker' options to the appropriate backend.
1015 for (StringRef Arg : Args.getAllArgValues(Id: OPT_device_linker_args_EQ)) {
1016 auto [Triple, Value] = Arg.split(Separator: '=');
1017 llvm::Triple TT(Triple);
1018 // If this isn't a recognized triple then it's an `arg=value` option.
1019 if (TT.getArch() == Triple::ArchType::UnknownArch)
1020 DAL.AddJoinedArg(BaseArg: nullptr, Opt: Tbl.getOption(Opt: OPT_linker_arg_EQ),
1021 Value: Args.MakeArgString(Str: Arg));
1022 else if (Value.empty())
1023 DAL.AddJoinedArg(BaseArg: nullptr, Opt: Tbl.getOption(Opt: OPT_linker_arg_EQ),
1024 Value: Args.MakeArgString(Str: Triple));
1025 else if (Triple == DAL.getLastArgValue(Id: OPT_triple_EQ))
1026 DAL.AddJoinedArg(BaseArg: nullptr, Opt: Tbl.getOption(Opt: OPT_linker_arg_EQ),
1027 Value: Args.MakeArgString(Str: Value));
1028 }
1029
1030 // Forward '-Xoffload-compiler' options to the appropriate backend.
1031 for (StringRef Arg : Args.getAllArgValues(Id: OPT_device_compiler_args_EQ)) {
1032 auto [Triple, Value] = Arg.split(Separator: '=');
1033 llvm::Triple TT(Triple);
1034 // If this isn't a recognized triple then it's an `arg=value` option.
1035 if (TT.getArch() == Triple::ArchType::UnknownArch)
1036 DAL.AddJoinedArg(BaseArg: nullptr, Opt: Tbl.getOption(Opt: OPT_compiler_arg_EQ),
1037 Value: Args.MakeArgString(Str: Arg));
1038 else if (Value.empty())
1039 DAL.AddJoinedArg(BaseArg: nullptr, Opt: Tbl.getOption(Opt: OPT_compiler_arg_EQ),
1040 Value: Args.MakeArgString(Str: Triple));
1041 else if (Triple == DAL.getLastArgValue(Id: OPT_triple_EQ))
1042 DAL.AddJoinedArg(BaseArg: nullptr, Opt: Tbl.getOption(Opt: OPT_compiler_arg_EQ),
1043 Value: Args.MakeArgString(Str: Value));
1044 }
1045
1046 return DAL;
1047}
1048
1049Error handleOverrideImages(
1050 const InputArgList &Args,
1051 MapVector<OffloadKind, SmallVector<OffloadingImage, 0>> &Images) {
1052 for (StringRef Arg : Args.getAllArgValues(Id: OPT_override_image)) {
1053 OffloadKind Kind = getOffloadKind(Name: Arg.split(Separator: "=").first);
1054 StringRef Filename = Arg.split(Separator: "=").second;
1055
1056 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
1057 MemoryBuffer::getFileOrSTDIN(Filename);
1058 if (std::error_code EC = BufferOrErr.getError())
1059 return createFileError(F: Filename, EC);
1060
1061 Expected<std::unique_ptr<ObjectFile>> ElfOrErr =
1062 ObjectFile::createELFObjectFile(Object: **BufferOrErr,
1063 /*InitContent=*/false);
1064 if (!ElfOrErr)
1065 return ElfOrErr.takeError();
1066 ObjectFile &Elf = **ElfOrErr;
1067
1068 OffloadingImage TheImage{};
1069 TheImage.TheImageKind = IMG_Object;
1070 TheImage.TheOffloadKind = Kind;
1071 TheImage.StringData["triple"] =
1072 Args.MakeArgString(Str: Elf.makeTriple().getTriple());
1073 if (std::optional<StringRef> CPU = Elf.tryGetCPUName())
1074 TheImage.StringData["arch"] = Args.MakeArgString(Str: *CPU);
1075 TheImage.Image = std::move(*BufferOrErr);
1076
1077 Images[Kind].emplace_back(Args: std::move(TheImage));
1078 }
1079 return Error::success();
1080}
1081
1082/// Transforms all the extracted offloading input files into an image that can
1083/// be registered by the runtime. If NeedsWrapping is false, writes bundled
1084/// output directly without wrapping or host linking.
1085Expected<SmallVector<StringRef>>
1086linkAndWrapDeviceFiles(ArrayRef<SmallVector<OffloadFile>> LinkerInputFiles,
1087 const InputArgList &Args, char **Argv, int Argc,
1088 bool NeedsWrapping) {
1089 llvm::TimeTraceScope TimeScope("Handle all device input");
1090
1091 std::mutex ImageMtx;
1092 MapVector<OffloadKind, SmallVector<OffloadingImage, 0>> Images;
1093
1094 // Initialize the images with any overriding inputs.
1095 if (Args.hasArg(Ids: OPT_override_image))
1096 if (Error Err = handleOverrideImages(Args, Images))
1097 return std::move(Err);
1098
1099 auto Err = parallelForEachError(R&: LinkerInputFiles, Fn: [&](auto &Input) -> Error {
1100 llvm::TimeTraceScope TimeScope("Link device input");
1101
1102 // Each thread needs its own copy of the base arguments to maintain
1103 // per-device argument storage of synthetic strings.
1104 const OptTable &Tbl = getOptTable();
1105 BumpPtrAllocator Alloc;
1106 StringSaver Saver(Alloc);
1107 auto BaseArgs =
1108 Tbl.parseArgs(Argc, Argv, Unknown: OPT_INVALID, Saver, ErrorFn: [](StringRef Err) {
1109 reportError(E: createStringError(S: Err));
1110 });
1111 auto LinkerArgs = getLinkerArgs(Input, BaseArgs);
1112
1113 uint16_t ActiveOffloadKindMask = 0u;
1114 for (const auto &File : Input)
1115 ActiveOffloadKindMask |= File.getBinary()->getOffloadKind();
1116
1117 // Linking images of SYCL offload kind with images of other kind is not
1118 // supported.
1119 // TODO: Remove the above limitation.
1120 if ((ActiveOffloadKindMask & OFK_SYCL) &&
1121 ((ActiveOffloadKindMask ^ OFK_SYCL) != 0))
1122 return createStringError(Fmt: "Linking images of SYCL offload kind with "
1123 "images of any other kind is not supported");
1124
1125 // Write any remaining device inputs to an output file.
1126 SmallVector<StringRef> InputFiles;
1127 for (const OffloadFile &File : Input) {
1128 auto FileNameOrErr = writeOffloadFile(File);
1129 if (!FileNameOrErr)
1130 return FileNameOrErr.takeError();
1131 InputFiles.emplace_back(Args&: *FileNameOrErr);
1132 }
1133
1134 // Link the remaining device files using the device linker.
1135 auto OutputOrErr =
1136 linkDevice(InputFiles, LinkerArgs, ActiveOffloadKindMask);
1137 if (!OutputOrErr)
1138 return OutputOrErr.takeError();
1139
1140 // Store the offloading image for each linked output file.
1141 for (OffloadKind Kind = OFK_OpenMP; Kind != OFK_LAST;
1142 Kind = static_cast<OffloadKind>((uint16_t)(Kind) << 1)) {
1143 if ((ActiveOffloadKindMask & Kind) == 0)
1144 continue;
1145 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileOrErr =
1146 llvm::MemoryBuffer::getFileOrSTDIN(Filename: *OutputOrErr);
1147 if (std::error_code EC = FileOrErr.getError()) {
1148 if (DryRun)
1149 FileOrErr = MemoryBuffer::getMemBuffer("", *OutputOrErr);
1150 else
1151 return createFileError(*OutputOrErr, EC);
1152 }
1153
1154 // Manually containerize offloading images not in ELF format.
1155 if (Error E = containerizeRawImage(*FileOrErr, Kind, LinkerArgs))
1156 return E;
1157
1158 std::scoped_lock<decltype(ImageMtx)> Guard(ImageMtx);
1159 OffloadingImage TheImage{};
1160 TheImage.TheImageKind =
1161 Args.hasArg(Ids: OPT_embed_bitcode) ? IMG_Bitcode : IMG_Object;
1162 TheImage.TheOffloadKind = Kind;
1163 TheImage.StringData["triple"] =
1164 Args.MakeArgString(Str: LinkerArgs.getLastArgValue(OPT_triple_EQ));
1165 TheImage.StringData["arch"] =
1166 Args.MakeArgString(Str: LinkerArgs.getLastArgValue(OPT_arch_EQ));
1167 TheImage.Image = std::move(*FileOrErr);
1168
1169 Images[Kind].emplace_back(Args: std::move(TheImage));
1170 }
1171 return Error::success();
1172 });
1173 if (Err)
1174 return std::move(Err);
1175
1176 // Create a binary image of each offloading image and either embed it into a
1177 // new object file, or if all inputs were direct offload binaries, emit the
1178 // fat binary directly (e.g. .hipfb / .fatbin).
1179 SmallVector<StringRef> WrappedOutput;
1180 for (auto &[Kind, Input] : Images) {
1181 // We sort the entries before bundling so they appear in a deterministic
1182 // order in the final binary.
1183 llvm::sort(C&: Input, Comp: [](OffloadingImage &A, OffloadingImage &B) {
1184 StringRef TripleA = A.StringData.lookup(Key: "triple");
1185 StringRef TripleB = B.StringData.lookup(Key: "triple");
1186 StringRef ArchA = A.StringData.lookup(Key: "arch");
1187 StringRef ArchB = B.StringData.lookup(Key: "arch");
1188 if (TripleA != TripleB)
1189 return TripleA > TripleB;
1190 if (ArchA != ArchB)
1191 return ArchA > ArchB;
1192 return A.TheOffloadKind < B.TheOffloadKind;
1193 });
1194 auto BundledImagesOrErr = bundleLinkedOutput(Images: Input, Args, Kind);
1195 if (!BundledImagesOrErr)
1196 return BundledImagesOrErr.takeError();
1197
1198 if (!NeedsWrapping) {
1199 if (BundledImagesOrErr->size() != 1)
1200 return createStringError(
1201 Fmt: "Expected a single bundled image for direct fat binary output");
1202
1203 Expected<std::unique_ptr<FileOutputBuffer>> FOBOrErr =
1204 FileOutputBuffer::create(
1205 FilePath: ExecutableName, Size: BundledImagesOrErr->front()->getBufferSize());
1206 if (!FOBOrErr)
1207 return FOBOrErr.takeError();
1208 std::unique_ptr<FileOutputBuffer> FOB = std::move(*FOBOrErr);
1209 llvm::copy(Range: BundledImagesOrErr->front()->getBuffer(),
1210 Out: FOB->getBufferStart());
1211 if (Error E = FOB->commit())
1212 return std::move(E);
1213
1214 continue;
1215 }
1216
1217 auto OutputOrErr = wrapDeviceImages(Buffers: *BundledImagesOrErr, Args, Kind);
1218 if (!OutputOrErr)
1219 return OutputOrErr.takeError();
1220 WrappedOutput.push_back(Elt: *OutputOrErr);
1221 }
1222
1223 return WrappedOutput;
1224}
1225
1226std::optional<std::string> findFile(StringRef Dir, StringRef Root,
1227 const Twine &Name) {
1228 SmallString<128> Path;
1229 if (Dir.starts_with(Prefix: "="))
1230 sys::path::append(path&: Path, a: Root, b: Dir.substr(Start: 1), c: Name);
1231 else
1232 sys::path::append(path&: Path, a: Dir, b: Name);
1233
1234 if (sys::fs::exists(Path))
1235 return static_cast<std::string>(Path);
1236 return std::nullopt;
1237}
1238
1239std::optional<std::string>
1240findFromSearchPaths(StringRef Name, StringRef Root,
1241 ArrayRef<StringRef> SearchPaths) {
1242 for (StringRef Dir : SearchPaths)
1243 if (std::optional<std::string> File = findFile(Dir, Root, Name))
1244 return File;
1245 return std::nullopt;
1246}
1247
1248std::optional<std::string>
1249searchLibraryBaseName(StringRef Name, StringRef Root,
1250 ArrayRef<StringRef> SearchPaths) {
1251 for (StringRef Dir : SearchPaths) {
1252 if (std::optional<std::string> File =
1253 findFile(Dir, Root, Name: "lib" + Name + ".so"))
1254 return File;
1255 if (std::optional<std::string> File =
1256 findFile(Dir, Root, Name: "lib" + Name + ".a"))
1257 return File;
1258 }
1259 return std::nullopt;
1260}
1261
1262/// Search for static libraries in the linker's library path given input like
1263/// `-lfoo` or `-l:libfoo.a`.
1264std::optional<std::string> searchLibrary(StringRef Input, StringRef Root,
1265 ArrayRef<StringRef> SearchPaths) {
1266 if (Input.starts_with(Prefix: ":"))
1267 return findFromSearchPaths(Name: Input.drop_front(), Root, SearchPaths);
1268 if (Input.ends_with(Suffix: ".lib"))
1269 return findFromSearchPaths(Name: Input, Root, SearchPaths);
1270 return searchLibraryBaseName(Name: Input, Root, SearchPaths);
1271}
1272
1273/// In verbose mode we need to replay the extracted files so the user can
1274/// reproduce the generated. This only prints the steps that would result in the
1275/// same output files given the input.
1276Error emitExtractCommands(
1277 ArrayRef<SmallVector<OffloadFile>> InputsForTarget,
1278 const DenseMap<StringRef, StringRef> &SourceForImage) {
1279 Expected<std::string> OffloadBinaryPath = findProgram(
1280 Name: "llvm-offload-binary", Paths: {getExecutableDir(Name: "llvm-offload-binary")});
1281 if (!OffloadBinaryPath)
1282 return OffloadBinaryPath.takeError();
1283
1284 BumpPtrAllocator Alloc;
1285 StringSaver Saver(Alloc);
1286 MapVector<StringRef, SmallVector<StringRef>> Commands;
1287 DenseSet<StringRef> Seen;
1288 for (const auto &Input : InputsForTarget) {
1289 for (const OffloadFile &File : Input) {
1290 const OffloadBinary &Binary = *File.getBinary();
1291 StringRef Identifier = Binary.getMemoryBufferRef().getBufferIdentifier();
1292 StringRef Source = SourceForImage.lookup(Val: Identifier);
1293 if (Source.empty())
1294 Source = Identifier;
1295
1296 auto OutputOrErr =
1297 createOutputFile(Prefix: sys::path::stem(path: Identifier) + "-" +
1298 Binary.getTriple() + "-" + Binary.getArch(),
1299 Extension: "o");
1300 if (!OutputOrErr)
1301 return OutputOrErr.takeError();
1302
1303 std::string ImageArg =
1304 ("--image=kind=" + getOffloadKindName(Name: Binary.getOffloadKind()) +
1305 ",triple=" + Binary.getTriple())
1306 .str();
1307 if (!Binary.getArch().empty())
1308 ImageArg += (",arch=" + Binary.getArch()).str();
1309 file_magic Magic;
1310 if (!identify_magic(path: Source, result&: Magic) && Magic == file_magic::archive)
1311 ImageArg += (",member=" + sys::path::filename(path: Identifier)).str();
1312 ImageArg += (",file=" + *OutputOrErr).str();
1313
1314 // Shared images only need to be extracted once per source.
1315 StringRef SavedImage = Saver.save(S: ImageArg);
1316 if (!Seen.insert(V: Saver.save(S: Source + "\x01" + SavedImage)).second)
1317 continue;
1318 Commands[Source].push_back(Elt: SavedImage);
1319 }
1320 }
1321
1322 for (const auto &[Source, Images] : Commands) {
1323 SmallVector<StringRef> CmdArgs = {*OffloadBinaryPath, Source};
1324 llvm::append_range(C&: CmdArgs, R: Images);
1325 printCommands(CmdArgs);
1326 }
1327 return Error::success();
1328}
1329
1330/// Search the input files and libraries for embedded device offloading code
1331/// and add it to the list of files to be linked. Files coming from static
1332/// libraries are only added to the input if they are used by an existing
1333/// input file. Returns a list of input files intended for a single linking job.
1334Expected<SmallVector<SmallVector<OffloadFile>>>
1335getDeviceInput(const ArgList &Args) {
1336 llvm::TimeTraceScope TimeScope("ExtractDeviceCode");
1337
1338 // Skip all the input if the user is overriding the output.
1339 if (Args.hasArg(Ids: OPT_override_image))
1340 return SmallVector<SmallVector<OffloadFile>>();
1341
1342 StringRef Root = Args.getLastArgValue(Id: OPT_sysroot_EQ);
1343 SmallVector<StringRef> LibraryPaths;
1344 for (const opt::Arg *Arg : Args.filtered(Ids: OPT_library_path, Ids: OPT_libpath))
1345 LibraryPaths.push_back(Elt: Arg->getValue());
1346
1347 BumpPtrAllocator Alloc;
1348 StringSaver Saver(Alloc);
1349
1350 // Try to extract device code from the linker input files.
1351 bool WholeArchive = Args.hasArg(Ids: OPT_wholearchive_flag);
1352 SmallVector<OffloadFile> ObjectFilesToExtract;
1353 SmallVector<OffloadFile> ArchiveFilesToExtract;
1354 DenseMap<StringRef, StringRef> SourceForImage;
1355 for (const opt::Arg *Arg : Args.filtered(
1356 Ids: OPT_INPUT, Ids: OPT_library, Ids: OPT_whole_archive, Ids: OPT_no_whole_archive)) {
1357 if (Arg->getOption().matches(ID: OPT_whole_archive) ||
1358 Arg->getOption().matches(ID: OPT_no_whole_archive)) {
1359 WholeArchive = Arg->getOption().matches(ID: OPT_whole_archive);
1360 continue;
1361 }
1362
1363 std::optional<std::string> Filename =
1364 Arg->getOption().matches(ID: OPT_library)
1365 ? searchLibrary(Input: Arg->getValue(), Root, SearchPaths: LibraryPaths)
1366 : std::string(Arg->getValue());
1367
1368 if (!Filename && Arg->getOption().matches(ID: OPT_library))
1369 return createStringError(Fmt: "unable to find library -l%s", Vals: Arg->getValue());
1370
1371 if (!Filename || !sys::fs::exists(Path: *Filename) ||
1372 sys::fs::is_directory(Path: *Filename))
1373 continue;
1374
1375 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
1376 MemoryBuffer::getFileOrSTDIN(Filename: *Filename);
1377 if (std::error_code EC = BufferOrErr.getError())
1378 return createFileError(F: *Filename, EC);
1379
1380 MemoryBufferRef Buffer = **BufferOrErr;
1381 if (identify_magic(magic: Buffer.getBuffer()) == file_magic::elf_shared_object)
1382 continue;
1383
1384 SmallVector<OffloadFile> Binaries;
1385 if (Error Err = extractOffloadBinaries(Buffer, Binaries))
1386 return std::move(Err);
1387
1388 for (auto &Binary : Binaries) {
1389 if (Verbose && SaveTemps)
1390 SourceForImage.try_emplace(
1391 Key: Binary.getBinary()->getMemoryBufferRef().getBufferIdentifier(),
1392 Args: Saver.save(S: StringRef(*Filename)));
1393 if (identify_magic(magic: Buffer.getBuffer()) == file_magic::archive &&
1394 !WholeArchive)
1395 ArchiveFilesToExtract.emplace_back(Args: std::move(Binary));
1396 else
1397 ObjectFilesToExtract.emplace_back(Args: std::move(Binary));
1398 }
1399 }
1400
1401 // Handle the most specific target-ids first so a generic input merges last.
1402 llvm::stable_sort(Range&: ObjectFilesToExtract,
1403 C: [](const OffloadFile &A, const OffloadFile &B) {
1404 return A.getBinary()->getArch().count(C: ':') >
1405 B.getBinary()->getArch().count(C: ':');
1406 });
1407
1408 // Link all standard input files and update the list of symbols.
1409 MapVector<OffloadFile::TargetID, SmallVector<OffloadFile, 0>> InputFiles;
1410 for (OffloadFile &Binary : ObjectFilesToExtract) {
1411 if (!Binary.getBinary())
1412 continue;
1413
1414 OffloadFile::TargetID Target = Binary;
1415 SmallVector<OffloadFile::TargetID> CompatibleTargets;
1416 for (const auto &[ID, Input] : InputFiles)
1417 if (object::areTargetsEquivalent(LHS: Target, RHS: ID))
1418 CompatibleTargets.emplace_back(Args: ID);
1419
1420 // Seed a new image when no existing target can provide for this input.
1421 if (CompatibleTargets.empty())
1422 CompatibleTargets.emplace_back(Args&: Target);
1423
1424 for (const auto &[Index, ID] : llvm::enumerate(First&: CompatibleTargets)) {
1425 // If another target needs this binary it must be copied instead.
1426 if (Index == CompatibleTargets.size() - 1)
1427 InputFiles[ID].emplace_back(Args: std::move(Binary));
1428 else
1429 InputFiles[ID].emplace_back(Args: Binary.copy());
1430 }
1431 }
1432
1433 llvm::DenseSet<StringRef> ShouldExtract;
1434 for (StringRef Arg : Args.getAllArgValues(Id: OPT_should_extract))
1435 ShouldExtract.insert(V: Saver.save(S: Arg));
1436
1437 // We only extract archive members from the fat binary if we find a used or
1438 // requested target. Unlike normal static archive handling, we just extract
1439 // every object file contained in the archive.
1440 for (OffloadFile &Binary : ArchiveFilesToExtract) {
1441 if (!Binary.getBinary())
1442 continue;
1443
1444 SmallVector<OffloadFile::TargetID> CompatibleTargets = {Binary};
1445 for (const auto &[ID, Input] : InputFiles)
1446 if (OffloadFile::TargetID(Binary) != ID &&
1447 object::areTargetsCompatible(Provided: Binary, Requested: ID))
1448 CompatibleTargets.emplace_back(Args: ID);
1449
1450 for (const auto &[Index, ID] : llvm::enumerate(First&: CompatibleTargets)) {
1451 // Only extract if we have an object matching this target or it
1452 // was specifically requested.
1453 if (!InputFiles.count(Key: ID) && !ShouldExtract.contains(V: ID.second))
1454 continue;
1455
1456 // If another target needs this binary it must be copied instead.
1457 if (Index == CompatibleTargets.size() - 1)
1458 InputFiles[ID].emplace_back(Args: std::move(Binary));
1459 else
1460 InputFiles[ID].emplace_back(Args: Binary.copy());
1461 }
1462 }
1463
1464 SmallVector<SmallVector<OffloadFile>> InputsForTarget;
1465 for (auto &[ID, Input] : InputFiles)
1466 InputsForTarget.emplace_back(Args: std::move(Input));
1467
1468 if (Verbose && SaveTemps)
1469 if (Error Err = emitExtractCommands(InputsForTarget, SourceForImage))
1470 return std::move(Err);
1471
1472 return std::move(InputsForTarget);
1473}
1474
1475} // namespace
1476
1477int main(int Argc, char **Argv) {
1478 InitLLVM X(Argc, Argv);
1479 InitializeAllTargetInfos();
1480 InitializeAllTargets();
1481 InitializeAllTargetMCs();
1482 InitializeAllAsmParsers();
1483 InitializeAllAsmPrinters();
1484
1485 LinkerExecutable = Argv[0];
1486 sys::PrintStackTraceOnErrorSignal(Argv0: Argv[0]);
1487
1488 const OptTable &Tbl = getOptTable();
1489 BumpPtrAllocator Alloc;
1490 StringSaver Saver(Alloc);
1491 auto Args = Tbl.parseArgs(Argc, Argv, Unknown: OPT_INVALID, Saver, ErrorFn: [&](StringRef Err) {
1492 reportError(E: createStringError(S: Err));
1493 });
1494
1495 if (Args.hasArg(Ids: OPT_help) || Args.hasArg(Ids: OPT_help_hidden)) {
1496 Tbl.printHelp(
1497 OS&: outs(),
1498 Usage: "clang-linker-wrapper [options] -- <options to pass to the linker>",
1499 Title: "\nA wrapper utility over the host linker. It scans the input files\n"
1500 "for sections that require additional processing prior to linking.\n"
1501 "It will then transparently pass all arguments and input to the\n"
1502 "specified host linker to create the final binary.\n",
1503 ShowHidden: Args.hasArg(Ids: OPT_help_hidden), ShowAllAliases: Args.hasArg(Ids: OPT_help_hidden));
1504 return EXIT_SUCCESS;
1505 }
1506 if (Args.hasArg(Ids: OPT_version)) {
1507 printVersion(OS&: outs());
1508 return EXIT_SUCCESS;
1509 }
1510
1511 // This forwards '-mllvm' arguments to LLVM if present.
1512 SmallVector<const char *> NewArgv = {Argv[0]};
1513 for (const opt::Arg *Arg : Args.filtered(Ids: OPT_mllvm))
1514 NewArgv.push_back(Elt: Arg->getValue());
1515 for (const opt::Arg *Arg : Args.filtered(Ids: OPT_offload_opt_eq_minus))
1516 NewArgv.push_back(Elt: Arg->getValue());
1517 SmallVector<PassPlugin, 1> PluginList;
1518 PassPlugins.setCallback([&](const std::string &PluginPath) {
1519 auto Plugin = PassPlugin::Load(Filename: PluginPath);
1520 if (!Plugin)
1521 reportFatalUsageError(Err: Plugin.takeError());
1522 PluginList.emplace_back(Args&: Plugin.get());
1523 });
1524 cl::ParseCommandLineOptions(argc: NewArgv.size(), argv: &NewArgv[0]);
1525
1526 Verbose = Args.hasArg(Ids: OPT_verbose);
1527 DryRun = Args.hasArg(Ids: OPT_dry_run);
1528 SaveTemps = Args.hasArg(Ids: OPT_save_temps);
1529 CudaBinaryPath = Args.getLastArgValue(Id: OPT_cuda_path_EQ).str();
1530 CanonicalPrefixes = !Args.hasArg(Ids: OPT_no_canonical_prefixes);
1531
1532 llvm::Triple Triple(
1533 Args.getLastArgValue(Id: OPT_host_triple_EQ, Default: sys::getDefaultTargetTriple()));
1534 if (Args.hasArg(Ids: OPT_o))
1535 ExecutableName = Args.getLastArgValue(Id: OPT_o, Default: "a.out");
1536 else if (Args.hasArg(Ids: OPT_out))
1537 ExecutableName = Args.getLastArgValue(Id: OPT_out, Default: "a.exe");
1538 else
1539 ExecutableName = Triple.isOSWindows() ? "a.exe" : "a.out";
1540
1541 parallel::strategy = hardware_concurrency(ThreadCount: 1);
1542 if (auto *Arg = Args.getLastArg(Ids: OPT_wrapper_jobs)) {
1543 StringRef Val = Arg->getValue();
1544 if (Val.equals_insensitive(RHS: "jobserver"))
1545 parallel::strategy = jobserver_concurrency();
1546 else {
1547 unsigned Threads = 0;
1548 if (!llvm::to_integer(S: Val, Num&: Threads) || Threads == 0)
1549 reportError(E: createStringError(
1550 Fmt: "%s: expected a positive integer or 'jobserver', got '%s'",
1551 Vals: Arg->getSpelling().data(), Vals: Val.data()));
1552 else
1553 parallel::strategy = hardware_concurrency(ThreadCount: Threads);
1554 }
1555 }
1556
1557 if (Args.hasArg(Ids: OPT_wrapper_time_trace_eq)) {
1558 unsigned Granularity;
1559 if (Args.getLastArgValue(Id: OPT_wrapper_time_trace_granularity, Default: "500")
1560 .getAsInteger(Radix: 10, Result&: Granularity))
1561 reportError(
1562 E: createStringError(Fmt: "invalid value for time trace granularity"));
1563 timeTraceProfilerInitialize(TimeTraceGranularity: Granularity, ProcName: Argv[0]);
1564 }
1565
1566 {
1567 llvm::TimeTraceScope TimeScope("Execute linker wrapper");
1568
1569 // Extract the device input files stored in the host fat binary.
1570 auto DeviceInputFiles = getDeviceInput(Args);
1571 if (!DeviceInputFiles)
1572 reportError(E: DeviceInputFiles.takeError());
1573
1574 // Check if we should emit fat binary directly without wrapping or host
1575 // linking.
1576 bool EmitFatbinOnly = Args.hasArg(Ids: OPT_emit_fatbin_only);
1577
1578 // Link and process the device images. The function may emit a direct fat
1579 // binary if --emit-fatbin-only is specified.
1580 auto FilesOrErr = linkAndWrapDeviceFiles(LinkerInputFiles: *DeviceInputFiles, Args, Argv,
1581 Argc, NeedsWrapping: !EmitFatbinOnly);
1582 if (!FilesOrErr)
1583 reportError(E: FilesOrErr.takeError());
1584
1585 // Run the host linking job with the rendered arguments.
1586 if (!EmitFatbinOnly) {
1587 if (Error Err = runLinker(Files: *FilesOrErr, Args))
1588 reportError(E: std::move(Err));
1589 }
1590 }
1591
1592 if (const opt::Arg *Arg = Args.getLastArg(Ids: OPT_wrapper_time_trace_eq)) {
1593 if (Error Err = timeTraceProfilerWrite(PreferredFileName: Arg->getValue(), FallbackFileName: ExecutableName))
1594 reportError(E: std::move(Err));
1595 timeTraceProfilerCleanup();
1596 }
1597
1598 // Remove the temporary files created.
1599 if (!SaveTemps)
1600 for (const auto &TempFile : TempFiles)
1601 if (std::error_code EC = sys::fs::remove(path: TempFile))
1602 reportError(E: createFileError(F: TempFile, EC));
1603
1604 return EXIT_SUCCESS;
1605}
1606