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 "OffloadWrapper.h"
18 #include "clang/Basic/Version.h"
19 #include "llvm/BinaryFormat/Magic.h"
20 #include "llvm/Bitcode/BitcodeWriter.h"
21 #include "llvm/CodeGen/CommandFlags.h"
22 #include "llvm/IR/Constants.h"
23 #include "llvm/IR/DiagnosticPrinter.h"
24 #include "llvm/IR/Module.h"
25 #include "llvm/IRReader/IRReader.h"
26 #include "llvm/LTO/LTO.h"
27 #include "llvm/MC/TargetRegistry.h"
28 #include "llvm/Object/Archive.h"
29 #include "llvm/Object/ArchiveWriter.h"
30 #include "llvm/Object/Binary.h"
31 #include "llvm/Object/ELFObjectFile.h"
32 #include "llvm/Object/IRObjectFile.h"
33 #include "llvm/Object/ObjectFile.h"
34 #include "llvm/Object/OffloadBinary.h"
35 #include "llvm/Option/ArgList.h"
36 #include "llvm/Option/OptTable.h"
37 #include "llvm/Option/Option.h"
38 #include "llvm/Support/CommandLine.h"
39 #include "llvm/Support/Errc.h"
40 #include "llvm/Support/FileOutputBuffer.h"
41 #include "llvm/Support/FileSystem.h"
42 #include "llvm/Support/Host.h"
43 #include "llvm/Support/InitLLVM.h"
44 #include "llvm/Support/MemoryBuffer.h"
45 #include "llvm/Support/Path.h"
46 #include "llvm/Support/Program.h"
47 #include "llvm/Support/Signals.h"
48 #include "llvm/Support/SourceMgr.h"
49 #include "llvm/Support/StringSaver.h"
50 #include "llvm/Support/TargetSelect.h"
51 #include "llvm/Support/WithColor.h"
52 #include "llvm/Support/raw_ostream.h"
53 #include "llvm/Target/TargetMachine.h"
54
55 using namespace llvm;
56 using namespace llvm::opt;
57 using namespace llvm::object;
58
59 /// Path of the current binary.
60 static const char *LinkerExecutable;
61
62 /// Ssave intermediary results.
63 static bool SaveTemps = false;
64
65 /// Print arguments without executing.
66 static bool DryRun = false;
67
68 /// Print verbose output.
69 static bool Verbose = false;
70
71 /// Filename of the executable being created.
72 static StringRef ExecutableName;
73
74 /// Binary path for the CUDA installation.
75 static std::string CudaBinaryPath;
76
77 /// Temporary files created by the linker wrapper.
78 static std::list<SmallString<128>> TempFiles;
79
80 /// Codegen flags for LTO backend.
81 static codegen::RegisterCodeGenFlags CodeGenFlags;
82
83 /// Global flag to indicate that the LTO pipeline threw an error.
84 static std::atomic<bool> LTOError;
85
86 using OffloadingImage = OffloadBinary::OffloadingImage;
87
88 /// A class to contain the binary information for a single OffloadBinary.
89 class OffloadFile : public OwningBinary<OffloadBinary> {
90 public:
91 using TargetID = std::pair<StringRef, StringRef>;
92
OffloadFile(std::unique_ptr<OffloadBinary> Binary,std::unique_ptr<MemoryBuffer> Buffer)93 OffloadFile(std::unique_ptr<OffloadBinary> Binary,
94 std::unique_ptr<MemoryBuffer> Buffer)
95 : OwningBinary<OffloadBinary>(std::move(Binary), std::move(Buffer)) {}
96
97 /// We use the Triple and Architecture pair to group linker inputs together.
98 /// This conversion function lets us use these files in a hash-map.
operator TargetID() const99 operator TargetID() const {
100 return std::make_pair(getBinary()->getTriple(), getBinary()->getArch());
101 }
102 };
103
104 namespace llvm {
105 // Provide DenseMapInfo so that OffloadKind can be used in a DenseMap.
106 template <> struct DenseMapInfo<OffloadKind> {
getEmptyKeyllvm::DenseMapInfo107 static inline OffloadKind getEmptyKey() { return OFK_LAST; }
getTombstoneKeyllvm::DenseMapInfo108 static inline OffloadKind getTombstoneKey() {
109 return static_cast<OffloadKind>(OFK_LAST + 1);
110 }
getHashValuellvm::DenseMapInfo111 static unsigned getHashValue(const OffloadKind &Val) { return Val; }
112
isEqualllvm::DenseMapInfo113 static bool isEqual(const OffloadKind &LHS, const OffloadKind &RHS) {
114 return LHS == RHS;
115 }
116 };
117 } // namespace llvm
118
119 namespace {
120 using std::error_code;
121
122 /// Must not overlap with llvm::opt::DriverFlag.
123 enum WrapperFlags {
124 WrapperOnlyOption = (1 << 4), // Options only used by the linker wrapper.
125 DeviceOnlyOption = (1 << 5), // Options only used for device linking.
126 };
127
128 enum ID {
129 OPT_INVALID = 0, // This is not an option ID.
130 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
131 HELPTEXT, METAVAR, VALUES) \
132 OPT_##ID,
133 #include "LinkerWrapperOpts.inc"
134 LastOption
135 #undef OPTION
136 };
137
138 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
139 #include "LinkerWrapperOpts.inc"
140 #undef PREFIX
141
142 static const OptTable::Info InfoTable[] = {
143 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
144 HELPTEXT, METAVAR, VALUES) \
145 {PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, Option::KIND##Class, \
146 PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS, VALUES},
147 #include "LinkerWrapperOpts.inc"
148 #undef OPTION
149 };
150
151 class WrapperOptTable : public opt::OptTable {
152 public:
WrapperOptTable()153 WrapperOptTable() : OptTable(InfoTable) {}
154 };
155
getOptTable()156 const OptTable &getOptTable() {
157 static const WrapperOptTable *Table = []() {
158 auto Result = std::make_unique<WrapperOptTable>();
159 return Result.release();
160 }();
161 return *Table;
162 }
163
164 Error extractFromBuffer(std::unique_ptr<MemoryBuffer> Buffer,
165 SmallVectorImpl<OffloadFile> &DeviceFiles);
166
printCommands(ArrayRef<StringRef> CmdArgs)167 void printCommands(ArrayRef<StringRef> CmdArgs) {
168 if (CmdArgs.empty())
169 return;
170
171 llvm::errs() << " \"" << CmdArgs.front() << "\" ";
172 for (auto IC = std::next(CmdArgs.begin()), IE = CmdArgs.end(); IC != IE; ++IC)
173 llvm::errs() << *IC << (std::next(IC) != IE ? " " : "\n");
174 }
175
reportError(Error E)176 [[noreturn]] void reportError(Error E) {
177 outs().flush();
178 logAllUnhandledErrors(std::move(E),
179 WithColor::error(errs(), LinkerExecutable));
180 exit(EXIT_FAILURE);
181 }
182
183 /// Create an extra user-specified \p OffloadFile.
184 /// TODO: We should find a way to wrap these as libraries instead.
getInputBitcodeLibrary(StringRef Input)185 Expected<OffloadFile> getInputBitcodeLibrary(StringRef Input) {
186 auto DeviceAndPath = StringRef(Input).split('=');
187 auto StringAndArch = DeviceAndPath.first.rsplit('-');
188 auto KindAndTriple = StringAndArch.first.split('-');
189
190 llvm::ErrorOr<std::unique_ptr<MemoryBuffer>> ImageOrError =
191 llvm::MemoryBuffer::getFileOrSTDIN(DeviceAndPath.second);
192 if (std::error_code EC = ImageOrError.getError())
193 return createFileError(DeviceAndPath.second, EC);
194
195 OffloadingImage Image{};
196 Image.TheImageKind = IMG_Bitcode;
197 Image.TheOffloadKind = getOffloadKind(KindAndTriple.first);
198 Image.StringData = {{"triple", KindAndTriple.second},
199 {"arch", StringAndArch.second}};
200 Image.Image = std::move(*ImageOrError);
201
202 std::unique_ptr<MemoryBuffer> Binary = OffloadBinary::write(Image);
203 auto NewBinaryOrErr = OffloadBinary::create(*Binary);
204 if (!NewBinaryOrErr)
205 return NewBinaryOrErr.takeError();
206 return OffloadFile(std::move(*NewBinaryOrErr), std::move(Binary));
207 }
208
getMainExecutable(const char * Name)209 std::string getMainExecutable(const char *Name) {
210 void *Ptr = (void *)(intptr_t)&getMainExecutable;
211 auto COWPath = sys::fs::getMainExecutable(Name, Ptr);
212 return sys::path::parent_path(COWPath).str();
213 }
214
215 /// Get a temporary filename suitable for output.
createOutputFile(const Twine & Prefix,StringRef Extension)216 Expected<StringRef> createOutputFile(const Twine &Prefix, StringRef Extension) {
217 SmallString<128> OutputFile;
218 if (SaveTemps) {
219 (Prefix + "." + Extension).toNullTerminatedStringRef(OutputFile);
220 } else {
221 if (std::error_code EC =
222 sys::fs::createTemporaryFile(Prefix, Extension, OutputFile))
223 return createFileError(OutputFile, EC);
224 }
225
226 TempFiles.emplace_back(std::move(OutputFile));
227 return TempFiles.back();
228 }
229
230 /// Execute the command \p ExecutablePath with the arguments \p Args.
executeCommands(StringRef ExecutablePath,ArrayRef<StringRef> Args)231 Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) {
232 if (Verbose || DryRun)
233 printCommands(Args);
234
235 if (!DryRun)
236 if (sys::ExecuteAndWait(ExecutablePath, Args))
237 return createStringError(inconvertibleErrorCode(),
238 "'" + sys::path::filename(ExecutablePath) + "'" +
239 " failed");
240 return Error::success();
241 }
242
findProgram(StringRef Name,ArrayRef<StringRef> Paths)243 Expected<std::string> findProgram(StringRef Name, ArrayRef<StringRef> Paths) {
244
245 ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths);
246 if (!Path)
247 Path = sys::findProgramByName(Name);
248 if (!Path && DryRun)
249 return Name.str();
250 if (!Path)
251 return createStringError(Path.getError(),
252 "Unable to find '" + Name + "' in path");
253 return *Path;
254 }
255
256 /// Runs the wrapped linker job with the newly created input.
runLinker(ArrayRef<StringRef> Files,const ArgList & Args)257 Error runLinker(ArrayRef<StringRef> Files, const ArgList &Args) {
258 llvm::TimeTraceScope TimeScope("Execute host linker");
259
260 // Render the linker arguments and add the newly created image. We add it
261 // after the output file to ensure it is linked with the correct libraries.
262 StringRef LinkerPath = Args.getLastArgValue(OPT_linker_path_EQ);
263 ArgStringList NewLinkerArgs;
264 for (const opt::Arg *Arg : Args) {
265 // Do not forward arguments only intended for the linker wrapper.
266 if (Arg->getOption().hasFlag(WrapperOnlyOption))
267 continue;
268
269 Arg->render(Args, NewLinkerArgs);
270 if (Arg->getOption().matches(OPT_o))
271 llvm::transform(Files, std::back_inserter(NewLinkerArgs),
272 [&](StringRef Arg) { return Args.MakeArgString(Arg); });
273 }
274
275 SmallVector<StringRef> LinkerArgs({LinkerPath});
276 for (StringRef Arg : NewLinkerArgs)
277 LinkerArgs.push_back(Arg);
278 if (Error Err = executeCommands(LinkerPath, LinkerArgs))
279 return Err;
280 return Error::success();
281 }
282
printVersion(raw_ostream & OS)283 void printVersion(raw_ostream &OS) {
284 OS << clang::getClangToolFullVersion("clang-linker-wrapper") << '\n';
285 }
286
287 /// Attempts to extract all the embedded device images contained inside the
288 /// buffer \p Contents. The buffer is expected to contain a valid offloading
289 /// binary format.
extractOffloadFiles(MemoryBufferRef Contents,SmallVectorImpl<OffloadFile> & DeviceFiles)290 Error extractOffloadFiles(MemoryBufferRef Contents,
291 SmallVectorImpl<OffloadFile> &DeviceFiles) {
292 uint64_t Offset = 0;
293 // There could be multiple offloading binaries stored at this section.
294 while (Offset < Contents.getBuffer().size()) {
295 std::unique_ptr<MemoryBuffer> Buffer =
296 MemoryBuffer::getMemBuffer(Contents.getBuffer().drop_front(Offset), "",
297 /*RequiresNullTerminator*/ false);
298 auto BinaryOrErr = OffloadBinary::create(*Buffer);
299 if (!BinaryOrErr)
300 return BinaryOrErr.takeError();
301 OffloadBinary &Binary = **BinaryOrErr;
302
303 // Create a new owned binary with a copy of the original memory.
304 std::unique_ptr<MemoryBuffer> BufferCopy = MemoryBuffer::getMemBufferCopy(
305 Binary.getData().take_front(Binary.getSize()),
306 Contents.getBufferIdentifier());
307 auto NewBinaryOrErr = OffloadBinary::create(*BufferCopy);
308 if (!NewBinaryOrErr)
309 return NewBinaryOrErr.takeError();
310 DeviceFiles.emplace_back(std::move(*NewBinaryOrErr), std::move(BufferCopy));
311
312 Offset += Binary.getSize();
313 }
314
315 return Error::success();
316 }
317
318 // Extract offloading binaries from an Object file \p Obj.
extractFromBinary(const ObjectFile & Obj,SmallVectorImpl<OffloadFile> & DeviceFiles)319 Error extractFromBinary(const ObjectFile &Obj,
320 SmallVectorImpl<OffloadFile> &DeviceFiles) {
321 for (ELFSectionRef Sec : Obj.sections()) {
322 if (Sec.getType() != ELF::SHT_LLVM_OFFLOADING)
323 continue;
324
325 Expected<StringRef> Buffer = Sec.getContents();
326 if (!Buffer)
327 return Buffer.takeError();
328
329 MemoryBufferRef Contents(*Buffer, Obj.getFileName());
330 if (Error Err = extractOffloadFiles(Contents, DeviceFiles))
331 return Err;
332 }
333
334 return Error::success();
335 }
336
extractFromBitcode(std::unique_ptr<MemoryBuffer> Buffer,SmallVectorImpl<OffloadFile> & DeviceFiles)337 Error extractFromBitcode(std::unique_ptr<MemoryBuffer> Buffer,
338 SmallVectorImpl<OffloadFile> &DeviceFiles) {
339 LLVMContext Context;
340 SMDiagnostic Err;
341 std::unique_ptr<Module> M = getLazyIRModule(std::move(Buffer), Err, Context);
342 if (!M)
343 return createStringError(inconvertibleErrorCode(),
344 "Failed to create module");
345
346 // Extract offloading data from globals referenced by the
347 // `llvm.embedded.object` metadata with the `.llvm.offloading` section.
348 auto *MD = M->getNamedMetadata("llvm.embedded.objects");
349 if (!MD)
350 return Error::success();
351
352 for (const MDNode *Op : MD->operands()) {
353 if (Op->getNumOperands() < 2)
354 continue;
355
356 MDString *SectionID = dyn_cast<MDString>(Op->getOperand(1));
357 if (!SectionID || SectionID->getString() != ".llvm.offloading")
358 continue;
359
360 GlobalVariable *GV =
361 mdconst::dyn_extract_or_null<GlobalVariable>(Op->getOperand(0));
362 if (!GV)
363 continue;
364
365 auto *CDS = dyn_cast<ConstantDataSequential>(GV->getInitializer());
366 if (!CDS)
367 continue;
368
369 MemoryBufferRef Contents(CDS->getAsString(), M->getName());
370 if (Error Err = extractOffloadFiles(Contents, DeviceFiles))
371 return Err;
372 }
373
374 return Error::success();
375 }
376
extractFromArchive(const Archive & Library,SmallVectorImpl<OffloadFile> & DeviceFiles)377 Error extractFromArchive(const Archive &Library,
378 SmallVectorImpl<OffloadFile> &DeviceFiles) {
379 // Try to extract device code from each file stored in the static archive.
380 Error Err = Error::success();
381 for (auto Child : Library.children(Err)) {
382 auto ChildBufferOrErr = Child.getMemoryBufferRef();
383 if (!ChildBufferOrErr)
384 return ChildBufferOrErr.takeError();
385 std::unique_ptr<MemoryBuffer> ChildBuffer =
386 MemoryBuffer::getMemBuffer(*ChildBufferOrErr, false);
387
388 // Check if the buffer has the required alignment.
389 if (!isAddrAligned(Align(OffloadBinary::getAlignment()),
390 ChildBuffer->getBufferStart()))
391 ChildBuffer = MemoryBuffer::getMemBufferCopy(
392 ChildBufferOrErr->getBuffer(),
393 ChildBufferOrErr->getBufferIdentifier());
394
395 if (Error Err = extractFromBuffer(std::move(ChildBuffer), DeviceFiles))
396 return Err;
397 }
398
399 if (Err)
400 return Err;
401 return Error::success();
402 }
403
404 /// Extracts embedded device offloading code from a memory \p Buffer to a list
405 /// of \p DeviceFiles.
extractFromBuffer(std::unique_ptr<MemoryBuffer> Buffer,SmallVectorImpl<OffloadFile> & DeviceFiles)406 Error extractFromBuffer(std::unique_ptr<MemoryBuffer> Buffer,
407 SmallVectorImpl<OffloadFile> &DeviceFiles) {
408 file_magic Type = identify_magic(Buffer->getBuffer());
409 switch (Type) {
410 case file_magic::bitcode:
411 return extractFromBitcode(std::move(Buffer), DeviceFiles);
412 case file_magic::elf_relocatable: {
413 Expected<std::unique_ptr<ObjectFile>> ObjFile =
414 ObjectFile::createObjectFile(*Buffer, Type);
415 if (!ObjFile)
416 return ObjFile.takeError();
417 return extractFromBinary(*ObjFile->get(), DeviceFiles);
418 }
419 case file_magic::archive: {
420 Expected<std::unique_ptr<llvm::object::Archive>> LibFile =
421 object::Archive::create(*Buffer);
422 if (!LibFile)
423 return LibFile.takeError();
424 return extractFromArchive(*LibFile->get(), DeviceFiles);
425 }
426 default:
427 return Error::success();
428 }
429 }
430
431 namespace nvptx {
assemble(StringRef InputFile,const ArgList & Args,bool RDC=true)432 Expected<StringRef> assemble(StringRef InputFile, const ArgList &Args,
433 bool RDC = true) {
434 llvm::TimeTraceScope TimeScope("NVPTX Assembler");
435 // NVPTX uses the ptxas binary to create device object files.
436 Expected<std::string> PtxasPath = findProgram("ptxas", {CudaBinaryPath});
437 if (!PtxasPath)
438 return PtxasPath.takeError();
439
440 const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
441 StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
442 // Create a new file to write the linked device image to. Assume that the
443 // input filename already has the device and architecture.
444 auto TempFileOrErr = createOutputFile(sys::path::stem(InputFile), "cubin");
445 if (!TempFileOrErr)
446 return TempFileOrErr.takeError();
447
448 SmallVector<StringRef, 16> CmdArgs;
449 StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2");
450 CmdArgs.push_back(*PtxasPath);
451 CmdArgs.push_back(Triple.isArch64Bit() ? "-m64" : "-m32");
452 if (Verbose)
453 CmdArgs.push_back("-v");
454 for (StringRef Arg : Args.getAllArgValues(OPT_ptxas_arg))
455 CmdArgs.push_back(Args.MakeArgString(Arg));
456 CmdArgs.push_back("-o");
457 CmdArgs.push_back(*TempFileOrErr);
458 CmdArgs.push_back(Args.MakeArgString("-" + OptLevel));
459 CmdArgs.push_back("--gpu-name");
460 CmdArgs.push_back(Arch);
461 if (Args.hasArg(OPT_debug))
462 CmdArgs.push_back("-g");
463 if (RDC)
464 CmdArgs.push_back("-c");
465
466 CmdArgs.push_back(InputFile);
467
468 if (Error Err = executeCommands(*PtxasPath, CmdArgs))
469 return std::move(Err);
470
471 return *TempFileOrErr;
472 }
473
link(ArrayRef<StringRef> InputFiles,const ArgList & Args)474 Expected<StringRef> link(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
475 llvm::TimeTraceScope TimeScope("NVPTX linker");
476 // NVPTX uses the nvlink binary to link device object files.
477 Expected<std::string> NvlinkPath = findProgram("nvlink", {CudaBinaryPath});
478 if (!NvlinkPath)
479 return NvlinkPath.takeError();
480
481 const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
482 StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
483
484 // Create a new file to write the linked device image to.
485 auto TempFileOrErr =
486 createOutputFile(sys::path::filename(ExecutableName) + "-device-" +
487 Triple.getArchName() + "-" + Arch,
488 "out");
489 if (!TempFileOrErr)
490 return TempFileOrErr.takeError();
491
492 SmallVector<StringRef, 16> CmdArgs;
493 CmdArgs.push_back(*NvlinkPath);
494 CmdArgs.push_back(Triple.isArch64Bit() ? "-m64" : "-m32");
495 if (Args.hasArg(OPT_debug))
496 CmdArgs.push_back("-g");
497 if (Verbose)
498 CmdArgs.push_back("-v");
499 CmdArgs.push_back("-o");
500 CmdArgs.push_back(*TempFileOrErr);
501 CmdArgs.push_back("-arch");
502 CmdArgs.push_back(Arch);
503
504 // Add extracted input files.
505 for (StringRef Input : InputFiles)
506 CmdArgs.push_back(Input);
507
508 for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ))
509 CmdArgs.push_back(Args.MakeArgString(Arg));
510 if (Error Err = executeCommands(*NvlinkPath, CmdArgs))
511 return std::move(Err);
512
513 return *TempFileOrErr;
514 }
515
516 Expected<StringRef>
fatbinary(ArrayRef<std::pair<StringRef,StringRef>> InputFiles,const ArgList & Args)517 fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,
518 const ArgList &Args) {
519 llvm::TimeTraceScope TimeScope("NVPTX fatbinary");
520 // NVPTX uses the fatbinary program to bundle the linked images.
521 Expected<std::string> FatBinaryPath =
522 findProgram("fatbinary", {CudaBinaryPath});
523 if (!FatBinaryPath)
524 return FatBinaryPath.takeError();
525
526 llvm::Triple Triple(
527 Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
528
529 // Create a new file to write the linked device image to.
530 auto TempFileOrErr = createOutputFile(
531 sys::path::filename(ExecutableName) + "-device", "fatbin");
532 if (!TempFileOrErr)
533 return TempFileOrErr.takeError();
534
535 SmallVector<StringRef, 16> CmdArgs;
536 CmdArgs.push_back(*FatBinaryPath);
537 CmdArgs.push_back(Triple.isArch64Bit() ? "-64" : "-32");
538 CmdArgs.push_back("--create");
539 CmdArgs.push_back(*TempFileOrErr);
540 for (const auto &FileAndArch : InputFiles)
541 CmdArgs.push_back(
542 Args.MakeArgString("--image=profile=" + std::get<1>(FileAndArch) +
543 ",file=" + std::get<0>(FileAndArch)));
544
545 if (Error Err = executeCommands(*FatBinaryPath, CmdArgs))
546 return std::move(Err);
547
548 return *TempFileOrErr;
549 }
550 } // namespace nvptx
551
552 namespace amdgcn {
link(ArrayRef<StringRef> InputFiles,const ArgList & Args)553 Expected<StringRef> link(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
554 llvm::TimeTraceScope TimeScope("AMDGPU linker");
555 // AMDGPU uses lld to link device object files.
556 Expected<std::string> LLDPath =
557 findProgram("lld", {getMainExecutable("lld")});
558 if (!LLDPath)
559 return LLDPath.takeError();
560
561 const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
562 StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
563
564 // Create a new file to write the linked device image to.
565 auto TempFileOrErr =
566 createOutputFile(sys::path::filename(ExecutableName) + "-" +
567 Triple.getArchName() + "-" + Arch,
568 "out");
569 if (!TempFileOrErr)
570 return TempFileOrErr.takeError();
571 std::string ArchArg = ("-plugin-opt=mcpu=" + Arch).str();
572
573 SmallVector<StringRef, 16> CmdArgs;
574 CmdArgs.push_back(*LLDPath);
575 CmdArgs.push_back("-flavor");
576 CmdArgs.push_back("gnu");
577 CmdArgs.push_back("--no-undefined");
578 CmdArgs.push_back("-shared");
579 CmdArgs.push_back("-plugin-opt=-amdgpu-internalize-symbols");
580 CmdArgs.push_back(ArchArg);
581 CmdArgs.push_back("-o");
582 CmdArgs.push_back(*TempFileOrErr);
583
584 // Add extracted input files.
585 for (StringRef Input : InputFiles)
586 CmdArgs.push_back(Input);
587
588 for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ))
589 CmdArgs.push_back(Args.MakeArgString(Arg));
590 if (Error Err = executeCommands(*LLDPath, CmdArgs))
591 return std::move(Err);
592
593 return *TempFileOrErr;
594 }
595
596 Expected<StringRef>
fatbinary(ArrayRef<std::pair<StringRef,StringRef>> InputFiles,const ArgList & Args)597 fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,
598 const ArgList &Args) {
599 llvm::TimeTraceScope TimeScope("AMDGPU Fatbinary");
600
601 // AMDGPU uses the clang-offload-bundler to bundle the linked images.
602 Expected<std::string> OffloadBundlerPath = findProgram(
603 "clang-offload-bundler", {getMainExecutable("clang-offload-bundler")});
604 if (!OffloadBundlerPath)
605 return OffloadBundlerPath.takeError();
606
607 llvm::Triple Triple(
608 Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
609
610 // Create a new file to write the linked device image to.
611 auto TempFileOrErr = createOutputFile(sys::path::filename(ExecutableName) +
612 "-device-" + Triple.getArchName(),
613 "hipfb");
614 if (!TempFileOrErr)
615 return TempFileOrErr.takeError();
616
617 BumpPtrAllocator Alloc;
618 StringSaver Saver(Alloc);
619
620 SmallVector<StringRef, 16> CmdArgs;
621 CmdArgs.push_back(*OffloadBundlerPath);
622 CmdArgs.push_back("-type=o");
623 CmdArgs.push_back("-bundle-align=4096");
624
625 SmallVector<StringRef> Targets = {"-targets=host-x86_64-unknown-linux"};
626 for (const auto &FileAndArch : InputFiles)
627 Targets.push_back(
628 Saver.save("hipv4-amdgcn-amd-amdhsa--" + std::get<1>(FileAndArch)));
629 CmdArgs.push_back(Saver.save(llvm::join(Targets, ",")));
630
631 CmdArgs.push_back("-input=/dev/null");
632 for (const auto &FileAndArch : InputFiles)
633 CmdArgs.push_back(Saver.save("-input=" + std::get<0>(FileAndArch)));
634
635 CmdArgs.push_back(Saver.save("-output=" + *TempFileOrErr));
636
637 if (Error Err = executeCommands(*OffloadBundlerPath, CmdArgs))
638 return std::move(Err);
639
640 return *TempFileOrErr;
641 }
642 } // namespace amdgcn
643
644 namespace generic {
645
getLDMOption(const llvm::Triple & T)646 const char *getLDMOption(const llvm::Triple &T) {
647 switch (T.getArch()) {
648 case llvm::Triple::x86:
649 if (T.isOSIAMCU())
650 return "elf_iamcu";
651 return "elf_i386";
652 case llvm::Triple::aarch64:
653 return "aarch64linux";
654 case llvm::Triple::aarch64_be:
655 return "aarch64linuxb";
656 case llvm::Triple::ppc64:
657 return "elf64ppc";
658 case llvm::Triple::ppc64le:
659 return "elf64lppc";
660 case llvm::Triple::x86_64:
661 if (T.isX32())
662 return "elf32_x86_64";
663 return "elf_x86_64";
664 case llvm::Triple::ve:
665 return "elf64ve";
666 default:
667 return nullptr;
668 }
669 }
670
link(ArrayRef<StringRef> InputFiles,const ArgList & Args)671 Expected<StringRef> link(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
672 llvm::TimeTraceScope TimeScope("Generic linker");
673 const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
674 StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
675
676 // Create a new file to write the linked device image to.
677 auto TempFileOrErr =
678 createOutputFile(sys::path::filename(ExecutableName) + "-" +
679 Triple.getArchName() + "-" + Arch,
680 "out");
681 if (!TempFileOrErr)
682 return TempFileOrErr.takeError();
683
684 // Use the host linker to perform generic offloading. Use the same libraries
685 // and paths as the host application does.
686 SmallVector<StringRef, 16> CmdArgs;
687 CmdArgs.push_back(Args.getLastArgValue(OPT_linker_path_EQ));
688 CmdArgs.push_back("-m");
689 CmdArgs.push_back(getLDMOption(Triple));
690 CmdArgs.push_back("-shared");
691
692 ArgStringList LinkerArgs;
693 for (const opt::Arg *Arg : Args) {
694 auto Op = Arg->getOption();
695 if (Op.matches(OPT_library) || Op.matches(OPT_library_path) ||
696 Op.matches(OPT_as_needed) || Op.matches(OPT_no_as_needed) ||
697 Op.matches(OPT_rpath) || Op.matches(OPT_dynamic_linker))
698 Arg->render(Args, LinkerArgs);
699 }
700 for (StringRef Arg : LinkerArgs)
701 CmdArgs.push_back(Arg);
702
703 CmdArgs.push_back("-Bsymbolic");
704 CmdArgs.push_back("-o");
705 CmdArgs.push_back(*TempFileOrErr);
706
707 // Add extracted input files.
708 for (StringRef Input : InputFiles)
709 CmdArgs.push_back(Input);
710
711 for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ))
712 CmdArgs.push_back(Args.MakeArgString(Arg));
713 if (Error Err =
714 executeCommands(Args.getLastArgValue(OPT_linker_path_EQ), CmdArgs))
715 return std::move(Err);
716
717 return *TempFileOrErr;
718 }
719 } // namespace generic
720
linkDevice(ArrayRef<StringRef> InputFiles,const ArgList & Args)721 Expected<StringRef> linkDevice(ArrayRef<StringRef> InputFiles,
722 const ArgList &Args) {
723 const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
724 switch (Triple.getArch()) {
725 case Triple::nvptx:
726 case Triple::nvptx64:
727 return nvptx::link(InputFiles, Args);
728 case Triple::amdgcn:
729 return amdgcn::link(InputFiles, Args);
730 case Triple::x86:
731 case Triple::x86_64:
732 case Triple::aarch64:
733 case Triple::aarch64_be:
734 case Triple::ppc64:
735 case Triple::ppc64le:
736 return generic::link(InputFiles, Args);
737 default:
738 return createStringError(inconvertibleErrorCode(),
739 Triple.getArchName() +
740 " linking is not supported");
741 }
742 }
743
diagnosticHandler(const DiagnosticInfo & DI)744 void diagnosticHandler(const DiagnosticInfo &DI) {
745 std::string ErrStorage;
746 raw_string_ostream OS(ErrStorage);
747 DiagnosticPrinterRawOStream DP(OS);
748 DI.print(DP);
749
750 switch (DI.getSeverity()) {
751 case DS_Error:
752 WithColor::error(errs(), LinkerExecutable) << ErrStorage << "\n";
753 LTOError = true;
754 break;
755 case DS_Warning:
756 WithColor::warning(errs(), LinkerExecutable) << ErrStorage << "\n";
757 break;
758 case DS_Note:
759 WithColor::note(errs(), LinkerExecutable) << ErrStorage << "\n";
760 break;
761 case DS_Remark:
762 WithColor::remark(errs()) << ErrStorage << "\n";
763 break;
764 }
765 }
766
767 // Get the list of target features from the input file and unify them such that
768 // if there are multiple +xxx or -xxx features we only keep the last one.
getTargetFeatures(ArrayRef<OffloadFile> InputFiles)769 std::vector<std::string> getTargetFeatures(ArrayRef<OffloadFile> InputFiles) {
770 SmallVector<StringRef> Features;
771 for (const OffloadFile &File : InputFiles) {
772 for (auto Arg : llvm::split(File.getBinary()->getString("feature"), ","))
773 Features.emplace_back(Arg);
774 }
775
776 // Only add a feature if it hasn't been seen before starting from the end.
777 std::vector<std::string> UnifiedFeatures;
778 DenseSet<StringRef> UsedFeatures;
779 for (StringRef Feature : llvm::reverse(Features)) {
780 if (UsedFeatures.insert(Feature.drop_front()).second)
781 UnifiedFeatures.push_back(Feature.str());
782 }
783
784 return UnifiedFeatures;
785 }
786
getCGOptLevel(unsigned OptLevel)787 CodeGenOpt::Level getCGOptLevel(unsigned OptLevel) {
788 switch (OptLevel) {
789 case 0:
790 return CodeGenOpt::None;
791 case 1:
792 return CodeGenOpt::Less;
793 case 2:
794 return CodeGenOpt::Default;
795 case 3:
796 return CodeGenOpt::Aggressive;
797 }
798 llvm_unreachable("Invalid optimization level");
799 }
800
801 template <typename ModuleHook = function_ref<bool(size_t, const Module &)>>
createLTO(const ArgList & Args,const std::vector<std::string> & Features,ModuleHook Hook=[](size_t,const Module &){})802 std::unique_ptr<lto::LTO> createLTO(
803 const ArgList &Args, const std::vector<std::string> &Features,
804 ModuleHook Hook = [](size_t, const Module &) { return true; }) {
805 const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
806 StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
807 lto::Config Conf;
808 lto::ThinBackend Backend;
809 // TODO: Handle index-only thin-LTO
810 Backend =
811 lto::createInProcessThinBackend(llvm::heavyweight_hardware_concurrency());
812
813 Conf.CPU = Arch.str();
814 Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple);
815
816 StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2");
817 Conf.MAttrs = Features;
818 Conf.CGOptLevel = getCGOptLevel(OptLevel[1] - '0');
819 Conf.OptLevel = OptLevel[1] - '0';
820 if (Conf.OptLevel > 0)
821 Conf.UseDefaultPipeline = true;
822 Conf.DefaultTriple = Triple.getTriple();
823
824 LTOError = false;
825 Conf.DiagHandler = diagnosticHandler;
826
827 Conf.PTO.LoopVectorization = Conf.OptLevel > 1;
828 Conf.PTO.SLPVectorization = Conf.OptLevel > 1;
829
830 if (SaveTemps) {
831 std::string TempName = (sys::path::filename(ExecutableName) + "-device-" +
832 Triple.getTriple() + "-" + Arch)
833 .str();
__anon18832d830502(size_t Task, const Module &M) 834 Conf.PostInternalizeModuleHook = [=](size_t Task, const Module &M) {
835 std::string File = !Task ? TempName + ".bc"
836 : TempName + "." + std::to_string(Task) + ".bc";
837 error_code EC;
838 raw_fd_ostream LinkedBitcode(File, EC, sys::fs::OF_None);
839 if (EC)
840 reportError(errorCodeToError(EC));
841 WriteBitcodeToFile(M, LinkedBitcode);
842 return true;
843 };
844 }
845 Conf.PostOptModuleHook = Hook;
846 Conf.CGFileType = Triple.isNVPTX() ? CGFT_AssemblyFile : CGFT_ObjectFile;
847
848 // TODO: Handle remark files
849 Conf.HasWholeProgramVisibility = Args.hasArg(OPT_whole_program);
850
851 return std::make_unique<lto::LTO>(std::move(Conf), Backend);
852 }
853
854 // Returns true if \p S is valid as a C language identifier and will be given
855 // `__start_` and `__stop_` symbols.
isValidCIdentifier(StringRef S)856 bool isValidCIdentifier(StringRef S) {
857 return !S.empty() && (isAlpha(S[0]) || S[0] == '_') &&
858 std::all_of(S.begin() + 1, S.end(),
859 [](char C) { return C == '_' || isAlnum(C); });
860 }
861
linkBitcodeFiles(SmallVectorImpl<OffloadFile> & InputFiles,SmallVectorImpl<StringRef> & OutputFiles,const ArgList & Args)862 Error linkBitcodeFiles(SmallVectorImpl<OffloadFile> &InputFiles,
863 SmallVectorImpl<StringRef> &OutputFiles,
864 const ArgList &Args) {
865 llvm::TimeTraceScope TimeScope("Link bitcode files");
866 const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
867
868 SmallVector<OffloadFile, 4> BitcodeInputFiles;
869 DenseSet<StringRef> UsedInRegularObj;
870 DenseSet<StringRef> UsedInSharedLib;
871 BumpPtrAllocator Alloc;
872 StringSaver Saver(Alloc);
873
874 // Search for bitcode files in the input and create an LTO input file. If it
875 // is not a bitcode file, scan its symbol table for symbols we need to save.
876 for (OffloadFile &File : InputFiles) {
877 MemoryBufferRef Buffer = MemoryBufferRef(File.getBinary()->getImage(), "");
878
879 file_magic Type = identify_magic(Buffer.getBuffer());
880 switch (Type) {
881 case file_magic::bitcode: {
882 BitcodeInputFiles.emplace_back(std::move(File));
883 continue;
884 }
885 case file_magic::elf_relocatable:
886 case file_magic::elf_shared_object: {
887 Expected<std::unique_ptr<ObjectFile>> ObjFile =
888 ObjectFile::createObjectFile(Buffer);
889 if (!ObjFile)
890 continue;
891
892 for (SymbolRef Sym : (*ObjFile)->symbols()) {
893 Expected<StringRef> Name = Sym.getName();
894 if (!Name)
895 return Name.takeError();
896
897 // Record if we've seen these symbols in any object or shared libraries.
898 if ((*ObjFile)->isRelocatableObject())
899 UsedInRegularObj.insert(Saver.save(*Name));
900 else
901 UsedInSharedLib.insert(Saver.save(*Name));
902 }
903 continue;
904 }
905 default:
906 continue;
907 }
908 }
909
910 if (BitcodeInputFiles.empty())
911 return Error::success();
912
913 // Remove all the bitcode files that we moved from the original input.
914 llvm::erase_if(InputFiles, [](OffloadFile &F) { return !F.getBinary(); });
915
916 // LTO Module hook to output bitcode without running the backend.
917 SmallVector<StringRef, 4> BitcodeOutput;
918 auto OutputBitcode = [&](size_t, const Module &M) {
919 auto TempFileOrErr = createOutputFile(sys::path::filename(ExecutableName) +
920 "-jit-" + Triple.getTriple(),
921 "bc");
922 if (!TempFileOrErr)
923 reportError(TempFileOrErr.takeError());
924
925 std::error_code EC;
926 raw_fd_ostream LinkedBitcode(*TempFileOrErr, EC, sys::fs::OF_None);
927 if (EC)
928 reportError(errorCodeToError(EC));
929 WriteBitcodeToFile(M, LinkedBitcode);
930 BitcodeOutput.push_back(*TempFileOrErr);
931 return false;
932 };
933
934 // We assume visibility of the whole program if every input file was bitcode.
935 auto Features = getTargetFeatures(BitcodeInputFiles);
936 auto LTOBackend = Args.hasArg(OPT_embed_bitcode)
937 ? createLTO(Args, Features, OutputBitcode)
938 : createLTO(Args, Features);
939
940 // We need to resolve the symbols so the LTO backend knows which symbols need
941 // to be kept or can be internalized. This is a simplified symbol resolution
942 // scheme to approximate the full resolution a linker would do.
943 uint64_t Idx = 0;
944 DenseSet<StringRef> PrevailingSymbols;
945 for (auto &BitcodeInput : BitcodeInputFiles) {
946 // Get a semi-unique buffer identifier for Thin-LTO.
947 StringRef Identifier = Saver.save(
948 std::to_string(Idx++) + "." +
949 BitcodeInput.getBinary()->getMemoryBufferRef().getBufferIdentifier());
950 MemoryBufferRef Buffer =
951 MemoryBufferRef(BitcodeInput.getBinary()->getImage(), Identifier);
952 Expected<std::unique_ptr<lto::InputFile>> BitcodeFileOrErr =
953 llvm::lto::InputFile::create(Buffer);
954 if (!BitcodeFileOrErr)
955 return BitcodeFileOrErr.takeError();
956
957 // Save the input file and the buffer associated with its memory.
958 const auto Symbols = (*BitcodeFileOrErr)->symbols();
959 SmallVector<lto::SymbolResolution, 16> Resolutions(Symbols.size());
960 size_t Idx = 0;
961 for (auto &Sym : Symbols) {
962 lto::SymbolResolution &Res = Resolutions[Idx++];
963
964 // We will use this as the prevailing symbol definition in LTO unless
965 // it is undefined or another definition has already been used.
966 Res.Prevailing =
967 !Sym.isUndefined() &&
968 PrevailingSymbols.insert(Saver.save(Sym.getName())).second;
969
970 // We need LTO to preseve the following global symbols:
971 // 1) Symbols used in regular objects.
972 // 2) Sections that will be given a __start/__stop symbol.
973 // 3) Prevailing symbols that are needed visible to external libraries.
974 Res.VisibleToRegularObj =
975 UsedInRegularObj.contains(Sym.getName()) ||
976 isValidCIdentifier(Sym.getSectionName()) ||
977 (Res.Prevailing &&
978 (Sym.getVisibility() != GlobalValue::HiddenVisibility &&
979 !Sym.canBeOmittedFromSymbolTable()));
980
981 // Identify symbols that must be exported dynamically and can be
982 // referenced by other files.
983 Res.ExportDynamic =
984 Sym.getVisibility() != GlobalValue::HiddenVisibility &&
985 (UsedInSharedLib.contains(Sym.getName()) ||
986 !Sym.canBeOmittedFromSymbolTable());
987
988 // The final definition will reside in this linkage unit if the symbol is
989 // defined and local to the module. This only checks for bitcode files,
990 // full assertion will require complete symbol resolution.
991 Res.FinalDefinitionInLinkageUnit =
992 Sym.getVisibility() != GlobalValue::DefaultVisibility &&
993 (!Sym.isUndefined() && !Sym.isCommon());
994
995 // We do not support linker redefined symbols (e.g. --wrap) for device
996 // image linking, so the symbols will not be changed after LTO.
997 Res.LinkerRedefined = false;
998 }
999
1000 // Add the bitcode file with its resolved symbols to the LTO job.
1001 if (Error Err = LTOBackend->add(std::move(*BitcodeFileOrErr), Resolutions))
1002 return Err;
1003 }
1004
1005 // Run the LTO job to compile the bitcode.
1006 size_t MaxTasks = LTOBackend->getMaxTasks();
1007 SmallVector<StringRef> Files(MaxTasks);
1008 auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
1009 int FD = -1;
1010 auto &TempFile = Files[Task];
1011 StringRef Extension = (Triple.isNVPTX()) ? "s" : "o";
1012 std::string TaskStr = Task ? "." + std::to_string(Task) : "";
1013 auto TempFileOrErr =
1014 createOutputFile(sys::path::filename(ExecutableName) + "-device-" +
1015 Triple.getTriple() + TaskStr,
1016 Extension);
1017 if (!TempFileOrErr)
1018 reportError(TempFileOrErr.takeError());
1019 TempFile = *TempFileOrErr;
1020 if (std::error_code EC = sys::fs::openFileForWrite(TempFile, FD))
1021 reportError(errorCodeToError(EC));
1022 return std::make_unique<CachedFileStream>(
1023 std::make_unique<llvm::raw_fd_ostream>(FD, true));
1024 };
1025
1026 if (Error Err = LTOBackend->run(AddStream))
1027 return Err;
1028
1029 if (LTOError)
1030 return createStringError(inconvertibleErrorCode(),
1031 "Errors encountered inside the LTO pipeline.");
1032
1033 // If we are embedding bitcode we only need the intermediate output.
1034 bool SingleOutput = Files.size() == 1;
1035 if (Args.hasArg(OPT_embed_bitcode)) {
1036 if (BitcodeOutput.size() != 1 || !SingleOutput)
1037 return createStringError(inconvertibleErrorCode(),
1038 "Cannot embed bitcode with multiple files.");
1039 OutputFiles.push_back(static_cast<std::string>(BitcodeOutput.front()));
1040 return Error::success();
1041 }
1042
1043 // Is we are compiling for NVPTX we need to run the assembler first.
1044 if (Triple.isNVPTX()) {
1045 for (StringRef &File : Files) {
1046 auto FileOrErr = nvptx::assemble(File, Args, !SingleOutput);
1047 if (!FileOrErr)
1048 return FileOrErr.takeError();
1049 File = *FileOrErr;
1050 }
1051 }
1052
1053 // Append the new inputs to the device linker input.
1054 for (StringRef File : Files)
1055 OutputFiles.push_back(File);
1056
1057 return Error::success();
1058 }
1059
writeOffloadFile(const OffloadFile & File)1060 Expected<StringRef> writeOffloadFile(const OffloadFile &File) {
1061 const OffloadBinary &Binary = *File.getBinary();
1062
1063 StringRef Prefix =
1064 sys::path::stem(Binary.getMemoryBufferRef().getBufferIdentifier());
1065 StringRef Suffix = getImageKindName(Binary.getImageKind());
1066
1067 auto TempFileOrErr = createOutputFile(
1068 Prefix + "-" + Binary.getTriple() + "-" + Binary.getArch(), Suffix);
1069 if (!TempFileOrErr)
1070 return TempFileOrErr.takeError();
1071
1072 Expected<std::unique_ptr<FileOutputBuffer>> OutputOrErr =
1073 FileOutputBuffer::create(*TempFileOrErr, Binary.getImage().size());
1074 if (!OutputOrErr)
1075 return OutputOrErr.takeError();
1076 std::unique_ptr<FileOutputBuffer> Output = std::move(*OutputOrErr);
1077 std::copy(Binary.getImage().bytes_begin(), Binary.getImage().bytes_end(),
1078 Output->getBufferStart());
1079 if (Error E = Output->commit())
1080 return std::move(E);
1081
1082 return *TempFileOrErr;
1083 }
1084
1085 // Compile the module to an object file using the appropriate target machine for
1086 // the host triple.
compileModule(Module & M)1087 Expected<StringRef> compileModule(Module &M) {
1088 llvm::TimeTraceScope TimeScope("Compile module");
1089 std::string Msg;
1090 const Target *T = TargetRegistry::lookupTarget(M.getTargetTriple(), Msg);
1091 if (!T)
1092 return createStringError(inconvertibleErrorCode(), Msg);
1093
1094 auto Options =
1095 codegen::InitTargetOptionsFromCodeGenFlags(Triple(M.getTargetTriple()));
1096 StringRef CPU = "";
1097 StringRef Features = "";
1098 std::unique_ptr<TargetMachine> TM(
1099 T->createTargetMachine(M.getTargetTriple(), CPU, Features, Options,
1100 Reloc::PIC_, M.getCodeModel()));
1101
1102 if (M.getDataLayout().isDefault())
1103 M.setDataLayout(TM->createDataLayout());
1104
1105 int FD = -1;
1106 auto TempFileOrErr =
1107 createOutputFile(sys::path::filename(ExecutableName) + "-wrapper", "o");
1108 if (!TempFileOrErr)
1109 return TempFileOrErr.takeError();
1110 if (std::error_code EC = sys::fs::openFileForWrite(*TempFileOrErr, FD))
1111 return errorCodeToError(EC);
1112
1113 auto OS = std::make_unique<llvm::raw_fd_ostream>(FD, true);
1114
1115 legacy::PassManager CodeGenPasses;
1116 TargetLibraryInfoImpl TLII(Triple(M.getTargetTriple()));
1117 CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII));
1118 if (TM->addPassesToEmitFile(CodeGenPasses, *OS, nullptr, CGFT_ObjectFile))
1119 return createStringError(inconvertibleErrorCode(),
1120 "Failed to execute host backend");
1121 CodeGenPasses.run(M);
1122
1123 return *TempFileOrErr;
1124 }
1125
1126 /// Creates the object file containing the device image and runtime
1127 /// registration code from the device images stored in \p Images.
1128 Expected<StringRef>
wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,const ArgList & Args,OffloadKind Kind)1129 wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,
1130 const ArgList &Args, OffloadKind Kind) {
1131 llvm::TimeTraceScope TimeScope("Wrap bundled images");
1132
1133 SmallVector<ArrayRef<char>, 4> BuffersToWrap;
1134 for (const auto &Buffer : Buffers)
1135 BuffersToWrap.emplace_back(
1136 ArrayRef<char>(Buffer->getBufferStart(), Buffer->getBufferSize()));
1137
1138 LLVMContext Context;
1139 Module M("offload.wrapper.module", Context);
1140 M.setTargetTriple(
1141 Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
1142
1143 switch (Kind) {
1144 case OFK_OpenMP:
1145 if (Error Err = wrapOpenMPBinaries(M, BuffersToWrap))
1146 return std::move(Err);
1147 break;
1148 case OFK_Cuda:
1149 if (Error Err = wrapCudaBinary(M, BuffersToWrap.front()))
1150 return std::move(Err);
1151 break;
1152 case OFK_HIP:
1153 if (Error Err = wrapHIPBinary(M, BuffersToWrap.front()))
1154 return std::move(Err);
1155 break;
1156 default:
1157 return createStringError(inconvertibleErrorCode(),
1158 getOffloadKindName(Kind) +
1159 " wrapping is not supported");
1160 }
1161
1162 if (Args.hasArg(OPT_print_wrapped_module))
1163 errs() << M;
1164
1165 auto FileOrErr = compileModule(M);
1166 if (!FileOrErr)
1167 return FileOrErr.takeError();
1168 return *FileOrErr;
1169 }
1170
1171 Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
bundleOpenMP(ArrayRef<OffloadingImage> Images)1172 bundleOpenMP(ArrayRef<OffloadingImage> Images) {
1173 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
1174 for (const OffloadingImage &Image : Images)
1175 Buffers.emplace_back(OffloadBinary::write(Image));
1176
1177 return std::move(Buffers);
1178 }
1179
1180 Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
bundleCuda(ArrayRef<OffloadingImage> Images,const ArgList & Args)1181 bundleCuda(ArrayRef<OffloadingImage> Images, const ArgList &Args) {
1182 SmallVector<std::pair<StringRef, StringRef>, 4> InputFiles;
1183 for (const OffloadingImage &Image : Images)
1184 InputFiles.emplace_back(std::make_pair(Image.Image->getBufferIdentifier(),
1185 Image.StringData.lookup("arch")));
1186
1187 Triple TheTriple = Triple(Images.front().StringData.lookup("triple"));
1188 auto FileOrErr = nvptx::fatbinary(InputFiles, Args);
1189 if (!FileOrErr)
1190 return FileOrErr.takeError();
1191
1192 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError =
1193 llvm::MemoryBuffer::getFileOrSTDIN(*FileOrErr);
1194
1195 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
1196 if (std::error_code EC = ImageOrError.getError())
1197 return createFileError(*FileOrErr, EC);
1198 Buffers.emplace_back(std::move(*ImageOrError));
1199
1200 return std::move(Buffers);
1201 }
1202
1203 Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
bundleHIP(ArrayRef<OffloadingImage> Images,const ArgList & Args)1204 bundleHIP(ArrayRef<OffloadingImage> Images, const ArgList &Args) {
1205 SmallVector<std::pair<StringRef, StringRef>, 4> InputFiles;
1206 for (const OffloadingImage &Image : Images)
1207 InputFiles.emplace_back(std::make_pair(Image.Image->getBufferIdentifier(),
1208 Image.StringData.lookup("arch")));
1209
1210 Triple TheTriple = Triple(Images.front().StringData.lookup("triple"));
1211 auto FileOrErr = amdgcn::fatbinary(InputFiles, Args);
1212 if (!FileOrErr)
1213 return FileOrErr.takeError();
1214
1215 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> ImageOrError =
1216 llvm::MemoryBuffer::getFileOrSTDIN(*FileOrErr);
1217
1218 SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
1219 if (std::error_code EC = ImageOrError.getError())
1220 return createFileError(*FileOrErr, EC);
1221 Buffers.emplace_back(std::move(*ImageOrError));
1222
1223 return std::move(Buffers);
1224 }
1225
1226 /// Transforms the input \p Images into the binary format the runtime expects
1227 /// for the given \p Kind.
1228 Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
bundleLinkedOutput(ArrayRef<OffloadingImage> Images,const ArgList & Args,OffloadKind Kind)1229 bundleLinkedOutput(ArrayRef<OffloadingImage> Images, const ArgList &Args,
1230 OffloadKind Kind) {
1231 llvm::TimeTraceScope TimeScope("Bundle linked output");
1232 switch (Kind) {
1233 case OFK_OpenMP:
1234 return bundleOpenMP(Images);
1235 case OFK_Cuda:
1236 return bundleCuda(Images, Args);
1237 case OFK_HIP:
1238 return bundleHIP(Images, Args);
1239 default:
1240 return createStringError(inconvertibleErrorCode(),
1241 getOffloadKindName(Kind) +
1242 " bundling is not supported");
1243 }
1244 }
1245
1246 /// Returns a new ArgList containg arguments used for the device linking phase.
getLinkerArgs(ArrayRef<OffloadFile> Input,const InputArgList & Args)1247 DerivedArgList getLinkerArgs(ArrayRef<OffloadFile> Input,
1248 const InputArgList &Args) {
1249 DerivedArgList DAL = DerivedArgList(DerivedArgList(Args));
1250 for (Arg *A : Args)
1251 DAL.append(A);
1252
1253 // Set the subarchitecture and target triple for this compilation.
1254 const OptTable &Tbl = getOptTable();
1255 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_arch_EQ),
1256 Args.MakeArgString(Input.front().getBinary()->getArch()));
1257 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_triple_EQ),
1258 Args.MakeArgString(Input.front().getBinary()->getTriple()));
1259
1260 // If every input file is bitcode we have whole program visibility as we do
1261 // only support static linking with bitcode.
1262 auto ContainsBitcode = [](const OffloadFile &F) {
1263 return identify_magic(F.getBinary()->getImage()) == file_magic::bitcode;
1264 };
1265 if (llvm::all_of(Input, ContainsBitcode))
1266 DAL.AddFlagArg(nullptr, Tbl.getOption(OPT_whole_program));
1267
1268 // Forward '-Xoffload-linker' options to the appropriate backend.
1269 for (StringRef Arg : Args.getAllArgValues(OPT_device_linker_args_EQ)) {
1270 auto TripleAndValue = Arg.split('=');
1271 if (TripleAndValue.second.empty())
1272 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ),
1273 Args.MakeArgString(TripleAndValue.first));
1274 else if (TripleAndValue.first == DAL.getLastArgValue(OPT_triple_EQ))
1275 DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ),
1276 Args.MakeArgString(TripleAndValue.second));
1277 }
1278
1279 return DAL;
1280 }
1281
1282 /// Transforms all the extracted offloading input files into an image that can
1283 /// be registered by the runtime.
1284 Expected<SmallVector<StringRef>>
linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> & LinkerInputFiles,const InputArgList & Args)1285 linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles,
1286 const InputArgList &Args) {
1287 llvm::TimeTraceScope TimeScope("Handle all device input");
1288
1289 DenseMap<OffloadFile::TargetID, SmallVector<OffloadFile, 4>> InputsForTarget;
1290 for (auto &File : LinkerInputFiles)
1291 InputsForTarget[File].emplace_back(std::move(File));
1292 LinkerInputFiles.clear();
1293
1294 DenseMap<OffloadKind, SmallVector<OffloadingImage, 2>> Images;
1295 for (auto &InputForTarget : InputsForTarget) {
1296 llvm::TimeTraceScope TimeScope("Link device input");
1297
1298 SmallVector<OffloadFile, 4> &Input = InputForTarget.getSecond();
1299 auto LinkerArgs = getLinkerArgs(Input, Args);
1300
1301 DenseSet<OffloadKind> ActiveOffloadKinds;
1302 for (const auto &File : Input)
1303 ActiveOffloadKinds.insert(File.getBinary()->getOffloadKind());
1304
1305 // First link and remove all the input files containing bitcode.
1306 SmallVector<StringRef> InputFiles;
1307 if (Error Err = linkBitcodeFiles(Input, InputFiles, LinkerArgs))
1308 return std::move(Err);
1309
1310 // Write any remaining device inputs to an output file for the linker job.
1311 for (const OffloadFile &File : Input) {
1312 auto FileNameOrErr = writeOffloadFile(File);
1313 if (!FileNameOrErr)
1314 return FileNameOrErr.takeError();
1315 InputFiles.emplace_back(*FileNameOrErr);
1316 }
1317
1318 // Link the remaining device files, if necessary, using the device linker.
1319 llvm::Triple Triple(LinkerArgs.getLastArgValue(OPT_triple_EQ));
1320 bool RequiresLinking =
1321 !Args.hasArg(OPT_embed_bitcode) &&
1322 !(Input.empty() && InputFiles.size() == 1 && Triple.isNVPTX());
1323 auto OutputOrErr = RequiresLinking ? linkDevice(InputFiles, LinkerArgs)
1324 : InputFiles.front();
1325 if (!OutputOrErr)
1326 return OutputOrErr.takeError();
1327
1328 // Store the offloading image for each linked output file.
1329 for (OffloadKind Kind : ActiveOffloadKinds) {
1330 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileOrErr =
1331 llvm::MemoryBuffer::getFileOrSTDIN(*OutputOrErr);
1332 if (std::error_code EC = FileOrErr.getError())
1333 return createFileError(*OutputOrErr, EC);
1334
1335 OffloadingImage TheImage{};
1336 TheImage.TheImageKind = IMG_Object;
1337 TheImage.TheOffloadKind = Kind;
1338 TheImage.StringData = {
1339 {"triple", LinkerArgs.getLastArgValue(OPT_triple_EQ)},
1340 {"arch", LinkerArgs.getLastArgValue(OPT_arch_EQ)}};
1341 TheImage.Image = std::move(*FileOrErr);
1342 Images[Kind].emplace_back(std::move(TheImage));
1343 }
1344 }
1345
1346 // Create a binary image of each offloading image and embed it into a new
1347 // object file.
1348 SmallVector<StringRef> WrappedOutput;
1349 for (const auto &KindAndImages : Images) {
1350 OffloadKind Kind = KindAndImages.first;
1351 auto BundledImagesOrErr =
1352 bundleLinkedOutput(KindAndImages.second, Args, Kind);
1353 if (!BundledImagesOrErr)
1354 return BundledImagesOrErr.takeError();
1355 auto OutputOrErr = wrapDeviceImages(*BundledImagesOrErr, Args, Kind);
1356 if (!OutputOrErr)
1357 return OutputOrErr.takeError();
1358 WrappedOutput.push_back(*OutputOrErr);
1359 }
1360
1361 return WrappedOutput;
1362 }
1363
findFile(StringRef Dir,StringRef Root,const Twine & Name)1364 Optional<std::string> findFile(StringRef Dir, StringRef Root,
1365 const Twine &Name) {
1366 SmallString<128> Path;
1367 if (Dir.startswith("="))
1368 sys::path::append(Path, Root, Dir.substr(1), Name);
1369 else
1370 sys::path::append(Path, Dir, Name);
1371
1372 if (sys::fs::exists(Path))
1373 return static_cast<std::string>(Path);
1374 return None;
1375 }
1376
findFromSearchPaths(StringRef Name,StringRef Root,ArrayRef<StringRef> SearchPaths)1377 Optional<std::string> findFromSearchPaths(StringRef Name, StringRef Root,
1378 ArrayRef<StringRef> SearchPaths) {
1379 for (StringRef Dir : SearchPaths)
1380 if (Optional<std::string> File = findFile(Dir, Root, Name))
1381 return File;
1382 return None;
1383 }
1384
searchLibraryBaseName(StringRef Name,StringRef Root,ArrayRef<StringRef> SearchPaths)1385 Optional<std::string> searchLibraryBaseName(StringRef Name, StringRef Root,
1386 ArrayRef<StringRef> SearchPaths) {
1387 for (StringRef Dir : SearchPaths) {
1388 if (Optional<std::string> File = findFile(Dir, Root, "lib" + Name + ".so"))
1389 return None;
1390 if (Optional<std::string> File = findFile(Dir, Root, "lib" + Name + ".a"))
1391 return File;
1392 }
1393 return None;
1394 }
1395
1396 /// Search for static libraries in the linker's library path given input like
1397 /// `-lfoo` or `-l:libfoo.a`.
searchLibrary(StringRef Input,StringRef Root,ArrayRef<StringRef> SearchPaths)1398 Optional<std::string> searchLibrary(StringRef Input, StringRef Root,
1399 ArrayRef<StringRef> SearchPaths) {
1400 if (Input.startswith(":"))
1401 return findFromSearchPaths(Input.drop_front(), Root, SearchPaths);
1402 return searchLibraryBaseName(Input, Root, SearchPaths);
1403 }
1404
1405 /// Search the input files and libraries for embedded device offloading code and
1406 /// add it to the list of files to be linked. Files coming from static libraries
1407 /// are only added to the input if they are used by an existing input file.
getDeviceInput(const ArgList & Args)1408 Expected<SmallVector<OffloadFile>> getDeviceInput(const ArgList &Args) {
1409 llvm::TimeTraceScope TimeScope("ExtractDeviceCode");
1410
1411 StringRef Root = Args.getLastArgValue(OPT_sysroot_EQ);
1412 SmallVector<StringRef> LibraryPaths;
1413 for (const opt::Arg *Arg : Args.filtered(OPT_library_path))
1414 LibraryPaths.push_back(Arg->getValue());
1415
1416 // Try to extract device code from the linker input files.
1417 SmallVector<OffloadFile> InputFiles;
1418 SmallVector<OffloadFile> LazyInputFiles;
1419 for (const opt::Arg *Arg : Args.filtered(OPT_INPUT)) {
1420 StringRef Filename = Arg->getValue();
1421 if (!sys::fs::exists(Filename) || sys::fs::is_directory(Filename))
1422 continue;
1423
1424 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
1425 MemoryBuffer::getFileOrSTDIN(Filename);
1426 if (std::error_code EC = BufferOrErr.getError())
1427 reportError(createFileError(Filename, EC));
1428
1429 bool IsLazy =
1430 identify_magic((*BufferOrErr)->getBuffer()) == file_magic::archive;
1431 if (Error Err = extractFromBuffer(std::move(*BufferOrErr),
1432 IsLazy ? LazyInputFiles : InputFiles))
1433 reportError(std::move(Err));
1434 }
1435
1436 // Try to extract input from input libraries.
1437 for (const opt::Arg *Arg : Args.filtered(OPT_library)) {
1438 if (auto Library = searchLibrary(Arg->getValue(), Root, LibraryPaths)) {
1439 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
1440 MemoryBuffer::getFileOrSTDIN(*Library);
1441 if (std::error_code EC = BufferOrErr.getError())
1442 reportError(createFileError(*Library, EC));
1443
1444 if (Error Err =
1445 extractFromBuffer(std::move(*BufferOrErr), LazyInputFiles))
1446 reportError(std::move(Err));
1447 }
1448 }
1449
1450 for (StringRef Library : Args.getAllArgValues(OPT_bitcode_library_EQ)) {
1451 auto FileOrErr = getInputBitcodeLibrary(Library);
1452 if (!FileOrErr)
1453 reportError(FileOrErr.takeError());
1454 InputFiles.push_back(std::move(*FileOrErr));
1455 }
1456
1457 DenseSet<OffloadFile::TargetID> IsTargetUsed;
1458 for (const auto &File : InputFiles)
1459 IsTargetUsed.insert(File);
1460
1461 // We should only include input files that are used.
1462 // TODO: Only load a library if it defined undefined symbols in the input.
1463 for (auto &LazyFile : LazyInputFiles)
1464 if (IsTargetUsed.contains(LazyFile))
1465 InputFiles.emplace_back(std::move(LazyFile));
1466
1467 return std::move(InputFiles);
1468 }
1469
1470 } // namespace
1471
main(int Argc,char ** Argv)1472 int main(int Argc, char **Argv) {
1473 InitLLVM X(Argc, Argv);
1474 InitializeAllTargetInfos();
1475 InitializeAllTargets();
1476 InitializeAllTargetMCs();
1477 InitializeAllAsmParsers();
1478 InitializeAllAsmPrinters();
1479
1480 LinkerExecutable = Argv[0];
1481 sys::PrintStackTraceOnErrorSignal(Argv[0]);
1482
1483 const OptTable &Tbl = getOptTable();
1484 BumpPtrAllocator Alloc;
1485 StringSaver Saver(Alloc);
1486 auto Args = Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, [&](StringRef Err) {
1487 reportError(createStringError(inconvertibleErrorCode(), Err));
1488 });
1489
1490 if (Args.hasArg(OPT_help) || Args.hasArg(OPT_help_hidden)) {
1491 Tbl.printHelp(
1492 outs(),
1493 "clang-linker-wrapper [options] -- <options to passed to the linker>",
1494 "\nA wrapper utility over the host linker. It scans the input files\n"
1495 "for sections that require additional processing prior to linking.\n"
1496 "The will then transparently pass all arguments and input to the\n"
1497 "specified host linker to create the final binary.\n",
1498 Args.hasArg(OPT_help_hidden), Args.hasArg(OPT_help_hidden));
1499 return EXIT_SUCCESS;
1500 }
1501 if (Args.hasArg(OPT_v)) {
1502 printVersion(outs());
1503 return EXIT_SUCCESS;
1504 }
1505
1506 // This forwards '-mllvm' arguments to LLVM if present.
1507 SmallVector<const char *> NewArgv = {Argv[0]};
1508 for (const opt::Arg *Arg : Args.filtered(OPT_mllvm))
1509 NewArgv.push_back(Arg->getValue());
1510 for (const opt::Arg *Arg : Args.filtered(OPT_offload_opt_eq_minus))
1511 NewArgv.push_back(Args.MakeArgString(StringRef("-") + Arg->getValue()));
1512 cl::ParseCommandLineOptions(NewArgv.size(), &NewArgv[0]);
1513
1514 Verbose = Args.hasArg(OPT_verbose);
1515 DryRun = Args.hasArg(OPT_dry_run);
1516 SaveTemps = Args.hasArg(OPT_save_temps);
1517 ExecutableName = Args.getLastArgValue(OPT_o, "a.out");
1518 CudaBinaryPath = Args.getLastArgValue(OPT_cuda_path_EQ).str();
1519 if (!CudaBinaryPath.empty())
1520 CudaBinaryPath = CudaBinaryPath + "/bin";
1521
1522 if (Args.hasArg(OPT_wrapper_time_trace_eq)) {
1523 unsigned Granularity;
1524 Args.getLastArgValue(OPT_wrapper_time_trace_granularity, "500")
1525 .getAsInteger(10, Granularity);
1526 timeTraceProfilerInitialize(Granularity, Argv[0]);
1527 }
1528
1529 {
1530 llvm::TimeTraceScope TimeScope("Execute linker wrapper");
1531
1532 // Extract the device input files stored in the host fat binary.
1533 auto DeviceInputFiles = getDeviceInput(Args);
1534 if (!DeviceInputFiles)
1535 reportError(DeviceInputFiles.takeError());
1536
1537 // Link and wrap the device images extracted from the linker input.
1538 auto FilesOrErr = linkAndWrapDeviceFiles(*DeviceInputFiles, Args);
1539 if (!FilesOrErr)
1540 reportError(FilesOrErr.takeError());
1541
1542 // Run the host linking job with the rendered arguments.
1543 if (Error Err = runLinker(*FilesOrErr, Args))
1544 reportError(std::move(Err));
1545 }
1546
1547 if (const opt::Arg *Arg = Args.getLastArg(OPT_wrapper_time_trace_eq)) {
1548 if (Error Err = timeTraceProfilerWrite(Arg->getValue(), ExecutableName))
1549 reportError(std::move(Err));
1550 timeTraceProfilerCleanup();
1551 }
1552
1553 // Remove the temporary files created.
1554 if (!SaveTemps)
1555 for (const auto &TempFile : TempFiles)
1556 if (std::error_code EC = sys::fs::remove(TempFile))
1557 reportError(createFileError(TempFile, EC));
1558
1559 return EXIT_SUCCESS;
1560 }
1561