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