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