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