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