640 lines · cpp
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-exception6//7//===---------------------------------------------------------------------===//8//9// This tool executes a sequence of steps required to link device code in SYCL10// device images. SYCL device code linking requires a complex sequence of steps11// that include linking of llvm bitcode files, linking device library files12// with the fully linked source bitcode file(s), running several SYCL specific13// post-link steps on the fully linked bitcode file(s), and finally generating14// 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("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 LastOption90#undef OPTION91};92 93#define OPTTABLE_STR_TABLE_CODE94#include "SYCLLinkOpts.inc"95#undef OPTTABLE_STR_TABLE_CODE96 97#define OPTTABLE_PREFIXES_TABLE_CODE98#include "SYCLLinkOpts.inc"99#undef OPTTABLE_PREFIXES_TABLE_CODE100 101static constexpr OptTable::Info InfoTable[] = {102#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),103#include "SYCLLinkOpts.inc"104#undef OPTION105};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(std::move(E), WithColor::error(errs(), 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(Name, Ptr);130 return sys::path::parent_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(OPT_save_temps)) {137 // Generate a unique path name without creating a file138 sys::fs::createUniquePath(Prefix + "-%%%%%%." + Extension, OutputFile,139 /*MakeAbsolute=*/false);140 } else {141 if (std::error_code EC =142 sys::fs::createTemporaryFile(Prefix, Extension, OutputFile))143 return createFileError(OutputFile, EC);144 }145 146 TempFiles.emplace_back(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(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(Path.getError(),159 "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(std::next(CmdArgs.begin()), CmdArgs.end(), " ")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(Args);176 177 if (!DryRun)178 if (sys::ExecuteAndWait(ExecutablePath, Args))179 return createStringError(180 "'%s' failed", sys::path::filename(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(OPT_INPUT)) {188 std::optional<std::string> Filename = std::string(Arg->getValue());189 if (!Filename || !sys::fs::exists(*Filename) ||190 sys::fs::is_directory(*Filename))191 continue;192 file_magic Magic;193 if (auto EC = identify_magic(*Filename, Magic))194 return createStringError("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("Unsupported file type");199 BitcodeFiles.push_back(*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 files206/// 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(File, Err, C);213 if (M)214 return std::move(M);215 return createStringError(Err.getMessage());216}217 218/// Gather all SYCL device library files that will be linked with input device219/// 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(OPT_library_path_EQ))225 LibraryPath = A->getValue();226 if (Arg *A = Args.getLastArg(OPT_device_libs_EQ)) {227 if (A->getValues().size() == 0)228 return createStringError(229 inconvertibleErrorCode(),230 "Number of device library files cannot be zero.");231 for (StringRef Val : A->getValues()) {232 SmallString<128> LibName(LibraryPath);233 llvm::sys::path::append(LibName, Val);234 if (llvm::sys::fs::exists(LibName))235 DeviceLibFiles.push_back(std::string(LibName));236 else237 return createStringError(inconvertibleErrorCode(),238 "\'" + 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 is247/// 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 using250/// 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>("sycl-device-link", 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(std::move(*ModOrErr)))265 return createStringError("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(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(std::move(*LibMod), Flags))282 return createStringError("Could not link IR");283 }284 }285 286 // Dump linked output for testing.287 if (Args.hasArg(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, sys::path::filename(OutputFile), "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(*BitcodeOutput, FD))299 return errorCodeToError(EC);300 llvm::raw_fd_ostream OS(FD, true);301 WriteBitcodeToFile(*LinkerOutput, OS);302 303 if (Verbose) {304 std::string Inputs = llvm::join(InputFiles.begin(), InputFiles.end(), ", ");305 std::string LibInputs = llvm::join((*SYCLDeviceLibFiles).begin(),306 (*SYCLDeviceLibFiles).end(), ", ");307 errs() << formatv(308 "sycl-device-link: inputs: {0} libfiles: {1} output: {2}\n", Inputs,309 LibInputs, *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 will318/// 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(File, Err, C);326 if (!M)327 return createStringError(Err.getMessage());328 329 if (Error Err = M->materializeAll())330 return Err;331 332 Triple TargetTriple(Args.getLastArgValue(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(M->getTargetTriple(), Msg);338 if (!T)339 return createStringError(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(M->getTargetTriple(), /* CPU */ "",347 /* Features */ "", Options, RM, CM));348 if (!TM)349 return createStringError("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(OutputFile, FD))358 return errorCodeToError(EC);359 auto OS = std::make_unique<llvm::raw_fd_ostream>(FD, 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(new TargetLibraryInfoWrapperPass(TLII));365 if (TM->addPassesToEmitFile(CodeGenPasses, *OS, nullptr,366 CodeGenFileType::ObjectFile))367 return createStringError("Failed to execute SPIR-V Backend");368 CodeGenPasses.run(*M);369 370 if (Verbose)371 errs() << formatv("SPIR-V Backend: input: {0}, output: {1}\n", File,372 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 CPU379/// 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 wrapping383/// device code and will be parsed to generate options required to be passed384/// 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, "opencl-aot", {getMainExecutable("opencl-aot")});390 if (!OpenCLAOTPath)391 return OpenCLAOTPath.takeError();392 393 CmdArgs.push_back(*OpenCLAOTPath);394 CmdArgs.push_back("--device=cpu");395 StringRef ExtraArgs = Args.getLastArgValue(OPT_opencl_aot_options_EQ);396 ExtraArgs.split(CmdArgs, " ", /*MaxSplit=*/-1, /*KeepEmpty=*/false);397 CmdArgs.push_back("-o");398 CmdArgs.push_back(OutputFile);399 CmdArgs.push_back(InputFile);400 if (Error Err = executeCommands(*OpenCLAOTPath, 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 Compute407/// 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 wrapping411/// device code and will be parsed to generate options required to be passed412/// 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, "ocloc", {getMainExecutable("ocloc")});418 if (!OclocPath)419 return OclocPath.takeError();420 421 CmdArgs.push_back(*OclocPath);422 // The next line prevents ocloc from modifying the image name423 CmdArgs.push_back("-output_no_suffix");424 CmdArgs.push_back("-spirv_input");425 426 StringRef Arch(Args.getLastArgValue(OPT_arch_EQ));427 if (Arch.empty())428 return createStringError(inconvertibleErrorCode(),429 "Arch must be specified for AOT compilation");430 CmdArgs.push_back("-device");431 CmdArgs.push_back(Arch);432 433 StringRef ExtraArgs = Args.getLastArgValue(OPT_ocloc_options_EQ);434 ExtraArgs.split(CmdArgs, " ", /*MaxSplit=*/-1, /*KeepEmpty=*/false);435 436 CmdArgs.push_back("-output");437 CmdArgs.push_back(OutputFile);438 CmdArgs.push_back("-file");439 CmdArgs.push_back(InputFile);440 if (Error Err = executeCommands(*OclocPath, 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 wrapping449/// device code and will be parsed to generate options required to be passed450/// into the SYCL AOT compilation step.451static Error runAOTCompile(StringRef InputFile, StringRef OutputFile,452 const ArgList &Args) {453 StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);454 OffloadArch OffloadArch = StringToOffloadArch(Arch);455 if (IsIntelGPUOffloadArch(OffloadArch))456 return runAOTCompileIntelGPU(InputFile, OutputFile, Args);457 if (IsIntelCPUOffloadArch(OffloadArch))458 return runAOTCompileIntelCPU(InputFile, OutputFile, Args);459 460 return createStringError(inconvertibleErrorCode(), "Unsupported arch");461}462 463// TODO: Consider using LLVM-IR metadata to identify globals of interest464bool 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(Files, Args, C);481 if (!LinkedFile)482 return LinkedFile.takeError();483 484 // TODO: SYCL post link functionality involves device code splitting and will485 // result in multiple bitcode codes.486 // The following lines are placeholders to represent multiple files and will487 // be refactored once SYCL post link support is available.488 SmallVector<std::string> SplitModules;489 SplitModules.emplace_back(*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(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(F.getName());503 }504 SymbolTable.emplace_back(llvm::join(Symbols.begin(), Symbols.end(), "\n"));505 }506 507 bool IsAOTCompileNeeded = IsIntelOffloadArch(508 StringToOffloadArch(Args.getLastArgValue(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('.').first;513 std::string SPVFile = (Stem + "_" + Twine(I) + ".spv").str();514 if (Error Err = runSPIRVCodeGen(SplitModules[I], Args, 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(SPVFile, 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(OutputFile, 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(File);537 if (std::error_code EC = FileOrErr.getError()) {538 if (DryRun)539 FileOrErr = MemoryBuffer::getMemBuffer("");540 else541 return createFileError(File, EC);542 }543 OffloadingImage TheImage{};544 // TODO: TheImageKind should be545 // `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, SYCL548 // Runtime has a heuristic to understand what the Image Kind is, so at least549 // it works.550 TheImage.TheImageKind = IMG_None;551 TheImage.TheOffloadKind = OFK_SYCL;552 TheImage.StringData["triple"] =553 Args.MakeArgString(Args.getLastArgValue(OPT_triple_EQ));554 TheImage.StringData["arch"] =555 Args.MakeArgString(Args.getLastArgValue(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("Offload binary has invalid size alignment");562 FS << Buffer;563 }564 return Error::success();565}566 567} // namespace568 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(argv[0]);579 580 const OptTable &Tbl = getOptTable();581 BumpPtrAllocator Alloc;582 StringSaver Saver(Alloc);583 auto Args = Tbl.parseArgs(argc, argv, OPT_INVALID, Saver, [&](StringRef Err) {584 reportError(createStringError(inconvertibleErrorCode(), Err));585 });586 587 if (Args.hasArg(OPT_help) || Args.hasArg(OPT_help_hidden)) {588 Tbl.printHelp(589 outs(), "clang-sycl-linker [options] <options to sycl link steps>",590 "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 Args.hasArg(OPT_help_hidden), Args.hasArg(OPT_help_hidden));595 return EXIT_SUCCESS;596 }597 598 if (Args.hasArg(OPT_version))599 printVersion(outs());600 601 Verbose = Args.hasArg(OPT_verbose);602 DryRun = Args.hasArg(OPT_dry_run);603 SaveTemps = Args.hasArg(OPT_save_temps);604 605 if (!Args.hasArg(OPT_o))606 reportError(createStringError("Output file must be specified"));607 OutputFile = Args.getLastArgValue(OPT_o);608 609 if (!Args.hasArg(OPT_triple_EQ))610 reportError(createStringError("Target triple must be specified"));611 612 if (Args.hasArg(OPT_spirv_dump_device_code_EQ)) {613 Arg *A = Args.getLastArg(OPT_spirv_dump_device_code_EQ);614 SmallString<128> Dir(A->getValue());615 if (Dir.empty())616 llvm::sys::path::native(Dir = "./");617 else618 Dir.append(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(FilesOrErr.takeError());627 628 // Run SYCL linking process on the generated inputs.629 if (Error Err = runSYCLLink(*FilesOrErr, Args))630 reportError(std::move(Err));631 632 // Remove the temporary files created.633 if (!Args.hasArg(OPT_save_temps))634 for (const auto &TempFile : TempFiles)635 if (std::error_code EC = sys::fs::remove(TempFile))636 reportError(createFileError(TempFile, EC));637 638 return EXIT_SUCCESS;639}640