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