1//=-------- clang-sycl-linker/ClangSYCLLinker.cpp - SYCL Linker util -------=//
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 executes a sequence of steps required to link device code in SYCL
10// device images. SYCL device code linking requires a complex sequence of steps
11// that include linking of llvm bitcode files, linking device library files
12// with the fully linked source bitcode file(s), running several SYCL specific
13// post-link steps on the fully linked bitcode file(s), and finally generating
14// target-specific device code.
15//===---------------------------------------------------------------------===//
16
17#include "clang/Basic/OffloadArch.h"
18#include "clang/Basic/Version.h"
19
20#include "llvm/ADT/StringExtras.h"
21#include "llvm/BinaryFormat/Magic.h"
22#include "llvm/Bitcode/BitcodeWriter.h"
23#include "llvm/CodeGen/CommandFlags.h"
24#include "llvm/IR/DiagnosticPrinter.h"
25#include "llvm/IR/LLVMContext.h"
26#include "llvm/IRReader/IRReader.h"
27#include "llvm/LTO/LTO.h"
28#include "llvm/Linker/Linker.h"
29#include "llvm/MC/TargetRegistry.h"
30#include "llvm/Object/Binary.h"
31#include "llvm/Object/IRObjectFile.h"
32#include "llvm/Object/OffloadBinary.h"
33#include "llvm/Option/ArgList.h"
34#include "llvm/Option/OptTable.h"
35#include "llvm/Option/Option.h"
36#include "llvm/Support/CommandLine.h"
37#include "llvm/Support/FileSystem.h"
38#include "llvm/Support/FormatVariadic.h"
39#include "llvm/Support/InitLLVM.h"
40#include "llvm/Support/Path.h"
41#include "llvm/Support/Program.h"
42#include "llvm/Support/Signals.h"
43#include "llvm/Support/StringSaver.h"
44#include "llvm/Support/TargetSelect.h"
45#include "llvm/Support/TimeProfiler.h"
46#include "llvm/Support/WithColor.h"
47#include "llvm/Target/TargetMachine.h"
48
49using namespace llvm;
50using namespace llvm::opt;
51using namespace llvm::object;
52using namespace clang;
53
54/// Save intermediary results.
55static bool SaveTemps = false;
56
57/// Print arguments without executing.
58static bool DryRun = false;
59
60/// Print verbose output.
61static bool Verbose = false;
62
63/// Filename of the output being created.
64static StringRef OutputFile;
65
66/// Directory to dump SPIR-V IR if requested by user.
67static SmallString<128> SPIRVDumpDir;
68
69using OffloadingImage = OffloadBinary::OffloadingImage;
70
71static void printVersion(raw_ostream &OS) {
72 OS << clang::getClangToolFullVersion(ToolName: "clang-sycl-linker") << '\n';
73}
74
75/// The value of `argv[0]` when run.
76static const char *Executable;
77
78/// Temporary files to be cleaned up.
79static SmallVector<SmallString<128>> TempFiles;
80
81namespace {
82// Must not overlap with llvm::opt::DriverFlag.
83enum LinkerFlags { LinkerOnlyOption = (1 << 4) };
84
85enum ID {
86 OPT_INVALID = 0, // This is not an option ID.
87#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
88#include "SYCLLinkOpts.inc"
89 LastOption
90#undef OPTION
91};
92
93#define OPTTABLE_STR_TABLE_CODE
94#include "SYCLLinkOpts.inc"
95#undef OPTTABLE_STR_TABLE_CODE
96
97#define OPTTABLE_PREFIXES_TABLE_CODE
98#include "SYCLLinkOpts.inc"
99#undef OPTTABLE_PREFIXES_TABLE_CODE
100
101static constexpr OptTable::Info InfoTable[] = {
102#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
103#include "SYCLLinkOpts.inc"
104#undef OPTION
105};
106
107class LinkerOptTable : public opt::GenericOptTable {
108public:
109 LinkerOptTable()
110 : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {}
111};
112
113const OptTable &getOptTable() {
114 static const LinkerOptTable *Table = []() {
115 auto Result = std::make_unique<LinkerOptTable>();
116 return Result.release();
117 }();
118 return *Table;
119}
120
121[[noreturn]] void reportError(Error E) {
122 outs().flush();
123 logAllUnhandledErrors(E: std::move(E), OS&: WithColor::error(OS&: errs(), Prefix: Executable));
124 exit(EXIT_FAILURE);
125}
126
127std::string getMainExecutable(const char *Name) {
128 void *Ptr = (void *)(intptr_t)&getMainExecutable;
129 auto COWPath = sys::fs::getMainExecutable(argv0: Name, MainExecAddr: Ptr);
130 return sys::path::parent_path(path: COWPath).str();
131}
132
133Expected<StringRef> createTempFile(const ArgList &Args, const Twine &Prefix,
134 StringRef Extension) {
135 SmallString<128> OutputFile;
136 if (Args.hasArg(Ids: OPT_save_temps)) {
137 // Generate a unique path name without creating a file
138 sys::fs::createUniquePath(Model: Prefix + "-%%%%%%." + Extension, ResultPath&: OutputFile,
139 /*MakeAbsolute=*/false);
140 } else {
141 if (std::error_code EC =
142 sys::fs::createTemporaryFile(Prefix, Suffix: Extension, ResultPath&: OutputFile))
143 return createFileError(F: OutputFile, EC);
144 }
145
146 TempFiles.emplace_back(Args: std::move(OutputFile));
147 return TempFiles.back();
148}
149
150Expected<std::string> findProgram(const ArgList &Args, StringRef Name,
151 ArrayRef<StringRef> Paths) {
152 if (Args.hasArg(Ids: OPT_dry_run))
153 return Name.str();
154 ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths);
155 if (!Path)
156 Path = sys::findProgramByName(Name);
157 if (!Path)
158 return createStringError(EC: Path.getError(),
159 S: "Unable to find '" + Name + "' in path");
160 return *Path;
161}
162
163void printCommands(ArrayRef<StringRef> CmdArgs) {
164 if (CmdArgs.empty())
165 return;
166
167 llvm::errs() << " \"" << CmdArgs.front() << "\" ";
168 llvm::errs() << llvm::join(Begin: std::next(x: CmdArgs.begin()), End: CmdArgs.end(), Separator: " ")
169 << "\n";
170}
171
172/// Execute the command \p ExecutablePath with the arguments \p Args.
173Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) {
174 if (Verbose || DryRun)
175 printCommands(CmdArgs: Args);
176
177 if (!DryRun)
178 if (sys::ExecuteAndWait(Program: ExecutablePath, Args))
179 return createStringError(
180 Fmt: "'%s' failed", Vals: sys::path::filename(path: ExecutablePath).str().c_str());
181 return Error::success();
182}
183
184Expected<SmallVector<std::string>> getInput(const ArgList &Args) {
185 // Collect all input bitcode files to be passed to the device linking stage.
186 SmallVector<std::string> BitcodeFiles;
187 for (const opt::Arg *Arg : Args.filtered(Ids: OPT_INPUT)) {
188 std::optional<std::string> Filename = std::string(Arg->getValue());
189 if (!Filename || !sys::fs::exists(Path: *Filename) ||
190 sys::fs::is_directory(Path: *Filename))
191 continue;
192 file_magic Magic;
193 if (auto EC = identify_magic(path: *Filename, result&: Magic))
194 return createStringError(S: "Failed to open file " + *Filename);
195 // TODO: Current use case involves LLVM IR bitcode files as input.
196 // This will be extended to support SPIR-V IR files.
197 if (Magic != file_magic::bitcode)
198 return createStringError(Fmt: "Unsupported file type");
199 BitcodeFiles.push_back(Elt: *Filename);
200 }
201 return BitcodeFiles;
202}
203
204/// Handle cases where input file is a LLVM IR bitcode file.
205/// When clang-sycl-linker is called via clang-linker-wrapper tool, input files
206/// are LLVM IR bitcode files.
207// TODO: Support SPIR-V IR files.
208Expected<std::unique_ptr<Module>> getBitcodeModule(StringRef File,
209 LLVMContext &C) {
210 SMDiagnostic Err;
211
212 auto M = getLazyIRFileModule(Filename: File, Err, Context&: C);
213 if (M)
214 return std::move(M);
215 return createStringError(S: Err.getMessage());
216}
217
218/// Gather all SYCL device library files that will be linked with input device
219/// files.
220/// The list of files and its location are passed from driver.
221Expected<SmallVector<std::string>> getSYCLDeviceLibs(const ArgList &Args) {
222 SmallVector<std::string> DeviceLibFiles;
223 StringRef LibraryPath;
224 if (Arg *A = Args.getLastArg(Ids: OPT_library_path_EQ))
225 LibraryPath = A->getValue();
226 if (Arg *A = Args.getLastArg(Ids: OPT_device_libs_EQ)) {
227 if (A->getValues().size() == 0)
228 return createStringError(
229 EC: inconvertibleErrorCode(),
230 S: "Number of device library files cannot be zero.");
231 for (StringRef Val : A->getValues()) {
232 SmallString<128> LibName(LibraryPath);
233 llvm::sys::path::append(path&: LibName, a: Val);
234 if (llvm::sys::fs::exists(Path: LibName))
235 DeviceLibFiles.push_back(Elt: std::string(LibName));
236 else
237 return createStringError(EC: inconvertibleErrorCode(),
238 S: "\'" + std::string(LibName) + "\'" +
239 " SYCL device library file is not found.");
240 }
241 }
242 return DeviceLibFiles;
243}
244
245/// Following tasks are performed:
246/// 1. Link all SYCL device bitcode images into one image. Device linking is
247/// performed using the linkInModule API.
248/// 2. Gather all SYCL device library bitcode images.
249/// 3. Link all the images gathered in Step 2 with the output of Step 1 using
250/// linkInModule API. LinkOnlyNeeded flag is used.
251Expected<StringRef> linkDeviceCode(ArrayRef<std::string> InputFiles,
252 const ArgList &Args, LLVMContext &C) {
253 llvm::TimeTraceScope TimeScope("SYCL link device code");
254
255 assert(InputFiles.size() && "No inputs to link");
256
257 auto LinkerOutput = std::make_unique<Module>(args: "sycl-device-link", args&: C);
258 Linker L(*LinkerOutput);
259 // Link SYCL device input files.
260 for (auto &File : InputFiles) {
261 auto ModOrErr = getBitcodeModule(File, C);
262 if (!ModOrErr)
263 return ModOrErr.takeError();
264 if (L.linkInModule(Src: std::move(*ModOrErr)))
265 return createStringError(Fmt: "Could not link IR");
266 }
267
268 // Get all SYCL device library files, if any.
269 auto SYCLDeviceLibFiles = getSYCLDeviceLibs(Args);
270 if (!SYCLDeviceLibFiles)
271 return SYCLDeviceLibFiles.takeError();
272
273 // Link in SYCL device library files.
274 const llvm::Triple Triple(Args.getLastArgValue(Id: OPT_triple_EQ));
275 for (auto &File : *SYCLDeviceLibFiles) {
276 auto LibMod = getBitcodeModule(File, C);
277 if (!LibMod)
278 return LibMod.takeError();
279 if ((*LibMod)->getTargetTriple() == Triple) {
280 unsigned Flags = Linker::Flags::LinkOnlyNeeded;
281 if (L.linkInModule(Src: std::move(*LibMod), Flags))
282 return createStringError(Fmt: "Could not link IR");
283 }
284 }
285
286 // Dump linked output for testing.
287 if (Args.hasArg(Ids: OPT_print_linked_module))
288 outs() << *LinkerOutput;
289
290 // Create a new file to write the linked device file to.
291 auto BitcodeOutput =
292 createTempFile(Args, Prefix: sys::path::filename(path: OutputFile), Extension: "bc");
293 if (!BitcodeOutput)
294 return BitcodeOutput.takeError();
295
296 // Write the final output into 'BitcodeOutput' file.
297 int FD = -1;
298 if (std::error_code EC = sys::fs::openFileForWrite(Name: *BitcodeOutput, ResultFD&: FD))
299 return errorCodeToError(EC);
300 llvm::raw_fd_ostream OS(FD, true);
301 WriteBitcodeToFile(M: *LinkerOutput, Out&: OS);
302
303 if (Verbose) {
304 std::string Inputs = llvm::join(Begin: InputFiles.begin(), End: InputFiles.end(), Separator: ", ");
305 std::string LibInputs = llvm::join(Begin: (*SYCLDeviceLibFiles).begin(),
306 End: (*SYCLDeviceLibFiles).end(), Separator: ", ");
307 errs() << formatv(
308 Fmt: "sycl-device-link: inputs: {0} libfiles: {1} output: {2}\n", Vals&: Inputs,
309 Vals&: LibInputs, Vals&: *BitcodeOutput);
310 }
311
312 return *BitcodeOutput;
313}
314
315/// Run LLVM to SPIR-V translation.
316/// Converts 'File' from LLVM bitcode to SPIR-V format using SPIR-V backend.
317/// 'Args' encompasses all arguments required for linking device code and will
318/// be parsed to generate options required to be passed into the backend.
319static Error runSPIRVCodeGen(StringRef File, const ArgList &Args,
320 StringRef OutputFile, LLVMContext &C) {
321 llvm::TimeTraceScope TimeScope("SPIR-V code generation");
322
323 // Parse input module.
324 SMDiagnostic Err;
325 std::unique_ptr<Module> M = parseIRFile(Filename: File, Err, Context&: C);
326 if (!M)
327 return createStringError(S: Err.getMessage());
328
329 if (Error Err = M->materializeAll())
330 return Err;
331
332 Triple TargetTriple(Args.getLastArgValue(Id: OPT_triple_EQ));
333 M->setTargetTriple(TargetTriple);
334
335 // Get a handle to SPIR-V target backend.
336 std::string Msg;
337 const Target *T = TargetRegistry::lookupTarget(TheTriple: M->getTargetTriple(), Error&: Msg);
338 if (!T)
339 return createStringError(S: Msg + ": " + M->getTargetTriple().str());
340
341 // Allocate SPIR-V target machine.
342 TargetOptions Options;
343 std::optional<Reloc::Model> RM;
344 std::optional<CodeModel::Model> CM;
345 std::unique_ptr<TargetMachine> TM(
346 T->createTargetMachine(TT: M->getTargetTriple(), /* CPU */ "",
347 /* Features */ "", Options, RM, CM));
348 if (!TM)
349 return createStringError(Fmt: "Could not allocate target machine!");
350
351 // Set data layout if needed.
352 if (M->getDataLayout().isDefault())
353 M->setDataLayout(TM->createDataLayout());
354
355 // Open output file for writing.
356 int FD = -1;
357 if (std::error_code EC = sys::fs::openFileForWrite(Name: OutputFile, ResultFD&: FD))
358 return errorCodeToError(EC);
359 auto OS = std::make_unique<llvm::raw_fd_ostream>(args&: FD, args: true);
360
361 // Run SPIR-V codegen passes to generate SPIR-V file.
362 legacy::PassManager CodeGenPasses;
363 TargetLibraryInfoImpl TLII(M->getTargetTriple());
364 CodeGenPasses.add(P: new TargetLibraryInfoWrapperPass(TLII));
365 if (TM->addPassesToEmitFile(CodeGenPasses, *OS, nullptr,
366 CodeGenFileType::ObjectFile))
367 return createStringError(Fmt: "Failed to execute SPIR-V Backend");
368 CodeGenPasses.run(M&: *M);
369
370 if (Verbose)
371 errs() << formatv(Fmt: "SPIR-V Backend: input: {0}, output: {1}\n", Vals&: File,
372 Vals&: OutputFile);
373
374 return Error::success();
375}
376
377/// Run AOT compilation for Intel CPU.
378/// Calls opencl-aot tool to generate device code for the Intel OpenCL CPU
379/// Runtime.
380/// \param InputFile The input SPIR-V file.
381/// \param OutputFile The output file name.
382/// \param Args Encompasses all arguments required for linking and wrapping
383/// device code and will be parsed to generate options required to be passed
384/// into the SYCL AOT compilation step.
385static Error runAOTCompileIntelCPU(StringRef InputFile, StringRef OutputFile,
386 const ArgList &Args) {
387 SmallVector<StringRef, 8> CmdArgs;
388 Expected<std::string> OpenCLAOTPath =
389 findProgram(Args, Name: "opencl-aot", Paths: {getMainExecutable(Name: "opencl-aot")});
390 if (!OpenCLAOTPath)
391 return OpenCLAOTPath.takeError();
392
393 CmdArgs.push_back(Elt: *OpenCLAOTPath);
394 CmdArgs.push_back(Elt: "--device=cpu");
395 StringRef ExtraArgs = Args.getLastArgValue(Id: OPT_opencl_aot_options_EQ);
396 ExtraArgs.split(A&: CmdArgs, Separator: " ", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
397 CmdArgs.push_back(Elt: "-o");
398 CmdArgs.push_back(Elt: OutputFile);
399 CmdArgs.push_back(Elt: InputFile);
400 if (Error Err = executeCommands(ExecutablePath: *OpenCLAOTPath, Args: CmdArgs))
401 return Err;
402 return Error::success();
403}
404
405/// Run AOT compilation for Intel GPU.
406/// Calls ocloc tool to generate device code for the Intel Graphics Compute
407/// Runtime.
408/// \param InputFile The input SPIR-V file.
409/// \param OutputFile The output file name.
410/// \param Args Encompasses all arguments required for linking and wrapping
411/// device code and will be parsed to generate options required to be passed
412/// into the SYCL AOT compilation step.
413static Error runAOTCompileIntelGPU(StringRef InputFile, StringRef OutputFile,
414 const ArgList &Args) {
415 SmallVector<StringRef, 8> CmdArgs;
416 Expected<std::string> OclocPath =
417 findProgram(Args, Name: "ocloc", Paths: {getMainExecutable(Name: "ocloc")});
418 if (!OclocPath)
419 return OclocPath.takeError();
420
421 CmdArgs.push_back(Elt: *OclocPath);
422 // The next line prevents ocloc from modifying the image name
423 CmdArgs.push_back(Elt: "-output_no_suffix");
424 CmdArgs.push_back(Elt: "-spirv_input");
425
426 StringRef Arch(Args.getLastArgValue(Id: OPT_arch_EQ));
427 if (Arch.empty())
428 return createStringError(EC: inconvertibleErrorCode(),
429 S: "Arch must be specified for AOT compilation");
430 CmdArgs.push_back(Elt: "-device");
431 CmdArgs.push_back(Elt: Arch);
432
433 StringRef ExtraArgs = Args.getLastArgValue(Id: OPT_ocloc_options_EQ);
434 ExtraArgs.split(A&: CmdArgs, Separator: " ", /*MaxSplit=*/-1, /*KeepEmpty=*/false);
435
436 CmdArgs.push_back(Elt: "-output");
437 CmdArgs.push_back(Elt: OutputFile);
438 CmdArgs.push_back(Elt: "-file");
439 CmdArgs.push_back(Elt: InputFile);
440 if (Error Err = executeCommands(ExecutablePath: *OclocPath, Args: CmdArgs))
441 return Err;
442 return Error::success();
443}
444
445/// Run AOT compilation for Intel CPU/GPU.
446/// \param InputFile The input SPIR-V file.
447/// \param OutputFile The output file name.
448/// \param Args Encompasses all arguments required for linking and wrapping
449/// device code and will be parsed to generate options required to be passed
450/// into the SYCL AOT compilation step.
451static Error runAOTCompile(StringRef InputFile, StringRef OutputFile,
452 const ArgList &Args) {
453 StringRef Arch = Args.getLastArgValue(Id: OPT_arch_EQ);
454 OffloadArch OffloadArch = StringToOffloadArch(S: Arch);
455 if (IsIntelGPUOffloadArch(Arch: OffloadArch))
456 return runAOTCompileIntelGPU(InputFile, OutputFile, Args);
457 if (IsIntelCPUOffloadArch(Arch: OffloadArch))
458 return runAOTCompileIntelCPU(InputFile, OutputFile, Args);
459
460 return createStringError(EC: inconvertibleErrorCode(), S: "Unsupported arch");
461}
462
463// TODO: Consider using LLVM-IR metadata to identify globals of interest
464bool isKernel(const Function &F) {
465 const llvm::CallingConv::ID CC = F.getCallingConv();
466 return CC == llvm::CallingConv::SPIR_KERNEL ||
467 CC == llvm::CallingConv::AMDGPU_KERNEL ||
468 CC == llvm::CallingConv::PTX_Kernel;
469}
470
471/// Performs the following steps:
472/// 1. Link input device code (user code and SYCL device library code).
473/// 2. Run SPIR-V code generation.
474Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) {
475 llvm::TimeTraceScope TimeScope("SYCL device linking");
476
477 LLVMContext C;
478
479 // Link all input bitcode files and SYCL device library files, if any.
480 auto LinkedFile = linkDeviceCode(InputFiles: Files, Args, C);
481 if (!LinkedFile)
482 return LinkedFile.takeError();
483
484 // TODO: SYCL post link functionality involves device code splitting and will
485 // result in multiple bitcode codes.
486 // The following lines are placeholders to represent multiple files and will
487 // be refactored once SYCL post link support is available.
488 SmallVector<std::string> SplitModules;
489 SplitModules.emplace_back(Args&: *LinkedFile);
490
491 // Generate symbol table.
492 SmallVector<std::string> SymbolTable;
493 for (size_t I = 0, E = SplitModules.size(); I != E; ++I) {
494 Expected<std::unique_ptr<Module>> ModOrErr =
495 getBitcodeModule(File: SplitModules[I], C);
496 if (!ModOrErr)
497 return ModOrErr.takeError();
498
499 SmallVector<StringRef> Symbols;
500 for (Function &F : **ModOrErr) {
501 if (isKernel(F))
502 Symbols.push_back(Elt: F.getName());
503 }
504 SymbolTable.emplace_back(Args: llvm::join(Begin: Symbols.begin(), End: Symbols.end(), Separator: "\n"));
505 }
506
507 bool IsAOTCompileNeeded = IsIntelOffloadArch(
508 Arch: StringToOffloadArch(S: Args.getLastArgValue(Id: OPT_arch_EQ)));
509
510 // SPIR-V code generation step.
511 for (size_t I = 0, E = SplitModules.size(); I != E; ++I) {
512 StringRef Stem = OutputFile.rsplit(Separator: '.').first;
513 std::string SPVFile = (Stem + "_" + Twine(I) + ".spv").str();
514 if (Error Err = runSPIRVCodeGen(File: SplitModules[I], Args, OutputFile: SPVFile, C))
515 return Err;
516 if (!IsAOTCompileNeeded) {
517 SplitModules[I] = SPVFile;
518 } else {
519 // AOT compilation step.
520 std::string AOTFile = (Stem + "_" + Twine(I) + ".out").str();
521 if (Error Err = runAOTCompile(InputFile: SPVFile, OutputFile: AOTFile, Args))
522 return Err;
523 SplitModules[I] = AOTFile;
524 }
525 }
526
527 // Write the final output into file.
528 int FD = -1;
529 if (std::error_code EC = sys::fs::openFileForWrite(Name: OutputFile, ResultFD&: FD))
530 return errorCodeToError(EC);
531 llvm::raw_fd_ostream FS(FD, /*shouldClose=*/true);
532
533 for (size_t I = 0, E = SplitModules.size(); I != E; ++I) {
534 auto File = SplitModules[I];
535 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileOrErr =
536 llvm::MemoryBuffer::getFileOrSTDIN(Filename: File);
537 if (std::error_code EC = FileOrErr.getError()) {
538 if (DryRun)
539 FileOrErr = MemoryBuffer::getMemBuffer(InputData: "");
540 else
541 return createFileError(F: File, EC);
542 }
543 OffloadingImage TheImage{};
544 // TODO: TheImageKind should be
545 // `IsAOTCompileNeeded ? IMG_Object : IMG_SPIRV;`
546 // For that we need to update SYCL Runtime to align with the ImageKind enum.
547 // Temporarily it is initalized to IMG_None, because in that case, SYCL
548 // Runtime has a heuristic to understand what the Image Kind is, so at least
549 // it works.
550 TheImage.TheImageKind = IMG_None;
551 TheImage.TheOffloadKind = OFK_SYCL;
552 TheImage.StringData["triple"] =
553 Args.MakeArgString(Str: Args.getLastArgValue(Id: OPT_triple_EQ));
554 TheImage.StringData["arch"] =
555 Args.MakeArgString(Str: Args.getLastArgValue(Id: OPT_arch_EQ));
556 TheImage.StringData["symbols"] = SymbolTable[I];
557 TheImage.Image = std::move(*FileOrErr);
558
559 llvm::SmallString<0> Buffer = OffloadBinary::write(TheImage);
560 if (Buffer.size() % OffloadBinary::getAlignment() != 0)
561 return createStringError(Fmt: "Offload binary has invalid size alignment");
562 FS << Buffer;
563 }
564 return Error::success();
565}
566
567} // namespace
568
569int main(int argc, char **argv) {
570 InitLLVM X(argc, argv);
571 InitializeAllTargetInfos();
572 InitializeAllTargets();
573 InitializeAllTargetMCs();
574 InitializeAllAsmParsers();
575 InitializeAllAsmPrinters();
576
577 Executable = argv[0];
578 sys::PrintStackTraceOnErrorSignal(Argv0: argv[0]);
579
580 const OptTable &Tbl = getOptTable();
581 BumpPtrAllocator Alloc;
582 StringSaver Saver(Alloc);
583 auto Args = Tbl.parseArgs(Argc: argc, Argv: argv, Unknown: OPT_INVALID, Saver, ErrorFn: [&](StringRef Err) {
584 reportError(E: createStringError(EC: inconvertibleErrorCode(), S: Err));
585 });
586
587 if (Args.hasArg(Ids: OPT_help) || Args.hasArg(Ids: OPT_help_hidden)) {
588 Tbl.printHelp(
589 OS&: outs(), Usage: "clang-sycl-linker [options] <options to sycl link steps>",
590 Title: "A utility that wraps around several steps required to link SYCL "
591 "device files.\n"
592 "This enables LLVM IR linking, post-linking and code generation for "
593 "SYCL targets.",
594 ShowHidden: Args.hasArg(Ids: OPT_help_hidden), ShowAllAliases: Args.hasArg(Ids: OPT_help_hidden));
595 return EXIT_SUCCESS;
596 }
597
598 if (Args.hasArg(Ids: OPT_version))
599 printVersion(OS&: outs());
600
601 Verbose = Args.hasArg(Ids: OPT_verbose);
602 DryRun = Args.hasArg(Ids: OPT_dry_run);
603 SaveTemps = Args.hasArg(Ids: OPT_save_temps);
604
605 if (!Args.hasArg(Ids: OPT_o))
606 reportError(E: createStringError(Fmt: "Output file must be specified"));
607 OutputFile = Args.getLastArgValue(Id: OPT_o);
608
609 if (!Args.hasArg(Ids: OPT_triple_EQ))
610 reportError(E: createStringError(Fmt: "Target triple must be specified"));
611
612 if (Args.hasArg(Ids: OPT_spirv_dump_device_code_EQ)) {
613 Arg *A = Args.getLastArg(Ids: OPT_spirv_dump_device_code_EQ);
614 SmallString<128> Dir(A->getValue());
615 if (Dir.empty())
616 llvm::sys::path::native(path&: Dir = "./");
617 else
618 Dir.append(RHS: llvm::sys::path::get_separator());
619
620 SPIRVDumpDir = Dir;
621 }
622
623 // Get the input files to pass to the linking stage.
624 auto FilesOrErr = getInput(Args);
625 if (!FilesOrErr)
626 reportError(E: FilesOrErr.takeError());
627
628 // Run SYCL linking process on the generated inputs.
629 if (Error Err = runSYCLLink(Files: *FilesOrErr, Args))
630 reportError(E: std::move(Err));
631
632 // Remove the temporary files created.
633 if (!Args.hasArg(Ids: OPT_save_temps))
634 for (const auto &TempFile : TempFiles)
635 if (std::error_code EC = sys::fs::remove(path: TempFile))
636 reportError(E: createFileError(F: TempFile, EC));
637
638 return EXIT_SUCCESS;
639}
640