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