brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.4 KiB · de20e74 Raw
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