1eeee5a44SArvind Sudarsanam //=-------- clang-sycl-linker/ClangSYCLLinker.cpp - SYCL Linker util -------=// 2eeee5a44SArvind Sudarsanam // 3eeee5a44SArvind Sudarsanam // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4eeee5a44SArvind Sudarsanam // See https://llvm.org/LICENSE.txt for license information. 5eeee5a44SArvind Sudarsanam // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6eeee5a44SArvind Sudarsanam // 7eeee5a44SArvind Sudarsanam //===---------------------------------------------------------------------===// 8eeee5a44SArvind Sudarsanam // 9eeee5a44SArvind Sudarsanam // This tool executes a sequence of steps required to link device code in SYCL 10eeee5a44SArvind Sudarsanam // device images. SYCL device code linking requires a complex sequence of steps 11eeee5a44SArvind Sudarsanam // that include linking of llvm bitcode files, linking device library files 12eeee5a44SArvind Sudarsanam // with the fully linked source bitcode file(s), running several SYCL specific 13eeee5a44SArvind Sudarsanam // post-link steps on the fully linked bitcode file(s), and finally generating 14eeee5a44SArvind Sudarsanam // target-specific device code. 15eeee5a44SArvind Sudarsanam //===---------------------------------------------------------------------===// 16eeee5a44SArvind Sudarsanam 17eeee5a44SArvind Sudarsanam #include "clang/Basic/Version.h" 18eeee5a44SArvind Sudarsanam 19eeee5a44SArvind Sudarsanam #include "llvm/ADT/StringExtras.h" 20eeee5a44SArvind Sudarsanam #include "llvm/BinaryFormat/Magic.h" 21eeee5a44SArvind Sudarsanam #include "llvm/Bitcode/BitcodeWriter.h" 22eeee5a44SArvind Sudarsanam #include "llvm/CodeGen/CommandFlags.h" 23eeee5a44SArvind Sudarsanam #include "llvm/IR/DiagnosticPrinter.h" 24eeee5a44SArvind Sudarsanam #include "llvm/IRReader/IRReader.h" 25eeee5a44SArvind Sudarsanam #include "llvm/LTO/LTO.h" 26eeee5a44SArvind Sudarsanam #include "llvm/Object/Archive.h" 27eeee5a44SArvind Sudarsanam #include "llvm/Object/ArchiveWriter.h" 28eeee5a44SArvind Sudarsanam #include "llvm/Object/Binary.h" 29eeee5a44SArvind Sudarsanam #include "llvm/Object/ELFObjectFile.h" 30eeee5a44SArvind Sudarsanam #include "llvm/Object/IRObjectFile.h" 31eeee5a44SArvind Sudarsanam #include "llvm/Object/ObjectFile.h" 32eeee5a44SArvind Sudarsanam #include "llvm/Object/OffloadBinary.h" 33eeee5a44SArvind Sudarsanam #include "llvm/Option/ArgList.h" 34eeee5a44SArvind Sudarsanam #include "llvm/Option/OptTable.h" 35eeee5a44SArvind Sudarsanam #include "llvm/Option/Option.h" 36eeee5a44SArvind Sudarsanam #include "llvm/Remarks/HotnessThresholdParser.h" 37eeee5a44SArvind Sudarsanam #include "llvm/Support/CommandLine.h" 38eeee5a44SArvind Sudarsanam #include "llvm/Support/FileOutputBuffer.h" 39eeee5a44SArvind Sudarsanam #include "llvm/Support/FileSystem.h" 40eeee5a44SArvind Sudarsanam #include "llvm/Support/InitLLVM.h" 41eeee5a44SArvind Sudarsanam #include "llvm/Support/MemoryBuffer.h" 42eeee5a44SArvind Sudarsanam #include "llvm/Support/Path.h" 43eeee5a44SArvind Sudarsanam #include "llvm/Support/Program.h" 44eeee5a44SArvind Sudarsanam #include "llvm/Support/Signals.h" 45eeee5a44SArvind Sudarsanam #include "llvm/Support/StringSaver.h" 46eeee5a44SArvind Sudarsanam #include "llvm/Support/TargetSelect.h" 47eeee5a44SArvind Sudarsanam #include "llvm/Support/TimeProfiler.h" 48eeee5a44SArvind Sudarsanam #include "llvm/Support/WithColor.h" 49eeee5a44SArvind Sudarsanam 50eeee5a44SArvind Sudarsanam using namespace llvm; 51eeee5a44SArvind Sudarsanam using namespace llvm::opt; 52eeee5a44SArvind Sudarsanam using namespace llvm::object; 53eeee5a44SArvind Sudarsanam 54eeee5a44SArvind Sudarsanam /// Save intermediary results. 55eeee5a44SArvind Sudarsanam static bool SaveTemps = false; 56eeee5a44SArvind Sudarsanam 57eeee5a44SArvind Sudarsanam /// Print arguments without executing. 58eeee5a44SArvind Sudarsanam static bool DryRun = false; 59eeee5a44SArvind Sudarsanam 60eeee5a44SArvind Sudarsanam /// Print verbose output. 61eeee5a44SArvind Sudarsanam static bool Verbose = false; 62eeee5a44SArvind Sudarsanam 63eeee5a44SArvind Sudarsanam /// Filename of the output being created. 64eeee5a44SArvind Sudarsanam static StringRef OutputFile; 65eeee5a44SArvind Sudarsanam 66eeee5a44SArvind Sudarsanam /// Directory to dump SPIR-V IR if requested by user. 67eeee5a44SArvind Sudarsanam static SmallString<128> SPIRVDumpDir; 68eeee5a44SArvind Sudarsanam 69eeee5a44SArvind Sudarsanam static void printVersion(raw_ostream &OS) { 70eeee5a44SArvind Sudarsanam OS << clang::getClangToolFullVersion("clang-sycl-linker") << '\n'; 71eeee5a44SArvind Sudarsanam } 72eeee5a44SArvind Sudarsanam 73eeee5a44SArvind Sudarsanam /// The value of `argv[0]` when run. 74eeee5a44SArvind Sudarsanam static const char *Executable; 75eeee5a44SArvind Sudarsanam 76eeee5a44SArvind Sudarsanam /// Temporary files to be cleaned up. 77eeee5a44SArvind Sudarsanam static SmallVector<SmallString<128>> TempFiles; 78eeee5a44SArvind Sudarsanam 79eeee5a44SArvind Sudarsanam namespace { 80eeee5a44SArvind Sudarsanam // Must not overlap with llvm::opt::DriverFlag. 81eeee5a44SArvind Sudarsanam enum LinkerFlags { LinkerOnlyOption = (1 << 4) }; 82eeee5a44SArvind Sudarsanam 83eeee5a44SArvind Sudarsanam enum ID { 84eeee5a44SArvind Sudarsanam OPT_INVALID = 0, // This is not an option ID. 85eeee5a44SArvind Sudarsanam #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), 86eeee5a44SArvind Sudarsanam #include "SYCLLinkOpts.inc" 87eeee5a44SArvind Sudarsanam LastOption 88eeee5a44SArvind Sudarsanam #undef OPTION 89eeee5a44SArvind Sudarsanam }; 90eeee5a44SArvind Sudarsanam 91*dd647e3eSChandler Carruth #define OPTTABLE_STR_TABLE_CODE 92eeee5a44SArvind Sudarsanam #include "SYCLLinkOpts.inc" 93*dd647e3eSChandler Carruth #undef OPTTABLE_STR_TABLE_CODE 94*dd647e3eSChandler Carruth 95*dd647e3eSChandler Carruth #define OPTTABLE_PREFIXES_TABLE_CODE 96*dd647e3eSChandler Carruth #include "SYCLLinkOpts.inc" 97*dd647e3eSChandler Carruth #undef OPTTABLE_PREFIXES_TABLE_CODE 98eeee5a44SArvind Sudarsanam 99eeee5a44SArvind Sudarsanam static constexpr OptTable::Info InfoTable[] = { 100eeee5a44SArvind Sudarsanam #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), 101eeee5a44SArvind Sudarsanam #include "SYCLLinkOpts.inc" 102eeee5a44SArvind Sudarsanam #undef OPTION 103eeee5a44SArvind Sudarsanam }; 104eeee5a44SArvind Sudarsanam 105eeee5a44SArvind Sudarsanam class LinkerOptTable : public opt::GenericOptTable { 106eeee5a44SArvind Sudarsanam public: 107*dd647e3eSChandler Carruth LinkerOptTable() 108*dd647e3eSChandler Carruth : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) {} 109eeee5a44SArvind Sudarsanam }; 110eeee5a44SArvind Sudarsanam 111eeee5a44SArvind Sudarsanam const OptTable &getOptTable() { 112eeee5a44SArvind Sudarsanam static const LinkerOptTable *Table = []() { 113eeee5a44SArvind Sudarsanam auto Result = std::make_unique<LinkerOptTable>(); 114eeee5a44SArvind Sudarsanam return Result.release(); 115eeee5a44SArvind Sudarsanam }(); 116eeee5a44SArvind Sudarsanam return *Table; 117eeee5a44SArvind Sudarsanam } 118eeee5a44SArvind Sudarsanam 119eeee5a44SArvind Sudarsanam [[noreturn]] void reportError(Error E) { 120eeee5a44SArvind Sudarsanam outs().flush(); 121eeee5a44SArvind Sudarsanam logAllUnhandledErrors(std::move(E), WithColor::error(errs(), Executable)); 122eeee5a44SArvind Sudarsanam exit(EXIT_FAILURE); 123eeee5a44SArvind Sudarsanam } 124eeee5a44SArvind Sudarsanam 125eeee5a44SArvind Sudarsanam std::string getMainExecutable(const char *Name) { 126eeee5a44SArvind Sudarsanam void *Ptr = (void *)(intptr_t)&getMainExecutable; 127eeee5a44SArvind Sudarsanam auto COWPath = sys::fs::getMainExecutable(Name, Ptr); 128eeee5a44SArvind Sudarsanam return sys::path::parent_path(COWPath).str(); 129eeee5a44SArvind Sudarsanam } 130eeee5a44SArvind Sudarsanam 131eeee5a44SArvind Sudarsanam Expected<StringRef> createTempFile(const ArgList &Args, const Twine &Prefix, 132eeee5a44SArvind Sudarsanam StringRef Extension) { 133eeee5a44SArvind Sudarsanam SmallString<128> OutputFile; 134eeee5a44SArvind Sudarsanam if (Args.hasArg(OPT_save_temps)) { 135eeee5a44SArvind Sudarsanam // Generate a unique path name without creating a file 136eeee5a44SArvind Sudarsanam sys::fs::createUniquePath(Prefix + "-%%%%%%." + Extension, OutputFile, 137eeee5a44SArvind Sudarsanam /*MakeAbsolute=*/false); 138eeee5a44SArvind Sudarsanam } else { 139eeee5a44SArvind Sudarsanam if (std::error_code EC = 140eeee5a44SArvind Sudarsanam sys::fs::createTemporaryFile(Prefix, Extension, OutputFile)) 141eeee5a44SArvind Sudarsanam return createFileError(OutputFile, EC); 142eeee5a44SArvind Sudarsanam } 143eeee5a44SArvind Sudarsanam 144eeee5a44SArvind Sudarsanam TempFiles.emplace_back(std::move(OutputFile)); 145eeee5a44SArvind Sudarsanam return TempFiles.back(); 146eeee5a44SArvind Sudarsanam } 147eeee5a44SArvind Sudarsanam 148eeee5a44SArvind Sudarsanam Expected<std::string> findProgram(const ArgList &Args, StringRef Name, 149eeee5a44SArvind Sudarsanam ArrayRef<StringRef> Paths) { 150eeee5a44SArvind Sudarsanam if (Args.hasArg(OPT_dry_run)) 151eeee5a44SArvind Sudarsanam return Name.str(); 152eeee5a44SArvind Sudarsanam ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths); 153eeee5a44SArvind Sudarsanam if (!Path) 154eeee5a44SArvind Sudarsanam Path = sys::findProgramByName(Name); 155eeee5a44SArvind Sudarsanam if (!Path) 156eeee5a44SArvind Sudarsanam return createStringError(Path.getError(), 157eeee5a44SArvind Sudarsanam "Unable to find '" + Name + "' in path"); 158eeee5a44SArvind Sudarsanam return *Path; 159eeee5a44SArvind Sudarsanam } 160eeee5a44SArvind Sudarsanam 161eeee5a44SArvind Sudarsanam void printCommands(ArrayRef<StringRef> CmdArgs) { 162eeee5a44SArvind Sudarsanam if (CmdArgs.empty()) 163eeee5a44SArvind Sudarsanam return; 164eeee5a44SArvind Sudarsanam 165eeee5a44SArvind Sudarsanam llvm::errs() << " \"" << CmdArgs.front() << "\" "; 166eeee5a44SArvind Sudarsanam llvm::errs() << llvm::join(std::next(CmdArgs.begin()), CmdArgs.end(), " ") 167eeee5a44SArvind Sudarsanam << "\n"; 168eeee5a44SArvind Sudarsanam } 169eeee5a44SArvind Sudarsanam 170eeee5a44SArvind Sudarsanam /// Execute the command \p ExecutablePath with the arguments \p Args. 171eeee5a44SArvind Sudarsanam Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) { 172eeee5a44SArvind Sudarsanam if (Verbose || DryRun) 173eeee5a44SArvind Sudarsanam printCommands(Args); 174eeee5a44SArvind Sudarsanam 175eeee5a44SArvind Sudarsanam if (!DryRun) 176eeee5a44SArvind Sudarsanam if (sys::ExecuteAndWait(ExecutablePath, Args)) 177eeee5a44SArvind Sudarsanam return createStringError( 178eeee5a44SArvind Sudarsanam "'%s' failed", sys::path::filename(ExecutablePath).str().c_str()); 179eeee5a44SArvind Sudarsanam return Error::success(); 180eeee5a44SArvind Sudarsanam } 181eeee5a44SArvind Sudarsanam 182eeee5a44SArvind Sudarsanam Expected<SmallVector<std::string>> getInput(const ArgList &Args) { 183eeee5a44SArvind Sudarsanam // Collect all input bitcode files to be passed to llvm-link. 184eeee5a44SArvind Sudarsanam SmallVector<std::string> BitcodeFiles; 185eeee5a44SArvind Sudarsanam for (const opt::Arg *Arg : Args.filtered(OPT_INPUT)) { 186eeee5a44SArvind Sudarsanam std::optional<std::string> Filename = std::string(Arg->getValue()); 187eeee5a44SArvind Sudarsanam if (!Filename || !sys::fs::exists(*Filename) || 188eeee5a44SArvind Sudarsanam sys::fs::is_directory(*Filename)) 189eeee5a44SArvind Sudarsanam continue; 190eeee5a44SArvind Sudarsanam file_magic Magic; 191eeee5a44SArvind Sudarsanam if (auto EC = identify_magic(*Filename, Magic)) 192eeee5a44SArvind Sudarsanam return createStringError("Failed to open file " + *Filename); 193eeee5a44SArvind Sudarsanam // TODO: Current use case involves LLVM IR bitcode files as input. 194eeee5a44SArvind Sudarsanam // This will be extended to support objects and SPIR-V IR files. 195eeee5a44SArvind Sudarsanam if (Magic != file_magic::bitcode) 196eeee5a44SArvind Sudarsanam return createStringError("Unsupported file type"); 197eeee5a44SArvind Sudarsanam BitcodeFiles.push_back(*Filename); 198eeee5a44SArvind Sudarsanam } 199eeee5a44SArvind Sudarsanam return BitcodeFiles; 200eeee5a44SArvind Sudarsanam } 201eeee5a44SArvind Sudarsanam 202eeee5a44SArvind Sudarsanam /// Link all SYCL device input files into one before adding device library 203eeee5a44SArvind Sudarsanam /// files. Device linking is performed using llvm-link tool. 204eeee5a44SArvind Sudarsanam /// 'InputFiles' is the list of all LLVM IR device input files. 205eeee5a44SArvind Sudarsanam /// 'Args' encompasses all arguments required for linking device code and will 206eeee5a44SArvind Sudarsanam /// be parsed to generate options required to be passed into llvm-link. 207eeee5a44SArvind Sudarsanam Expected<StringRef> linkDeviceInputFiles(ArrayRef<std::string> InputFiles, 208eeee5a44SArvind Sudarsanam const ArgList &Args) { 209eeee5a44SArvind Sudarsanam llvm::TimeTraceScope TimeScope("SYCL LinkDeviceInputFiles"); 210eeee5a44SArvind Sudarsanam 211eeee5a44SArvind Sudarsanam assert(InputFiles.size() && "No inputs to llvm-link"); 212eeee5a44SArvind Sudarsanam // Early check to see if there is only one input. 213eeee5a44SArvind Sudarsanam if (InputFiles.size() < 2) 214eeee5a44SArvind Sudarsanam return InputFiles[0]; 215eeee5a44SArvind Sudarsanam 216eeee5a44SArvind Sudarsanam Expected<std::string> LLVMLinkPath = 217eeee5a44SArvind Sudarsanam findProgram(Args, "llvm-link", {getMainExecutable("llvm-link")}); 218eeee5a44SArvind Sudarsanam if (!LLVMLinkPath) 219eeee5a44SArvind Sudarsanam return LLVMLinkPath.takeError(); 220eeee5a44SArvind Sudarsanam 221eeee5a44SArvind Sudarsanam SmallVector<StringRef> CmdArgs; 222eeee5a44SArvind Sudarsanam CmdArgs.push_back(*LLVMLinkPath); 223eeee5a44SArvind Sudarsanam for (auto &File : InputFiles) 224eeee5a44SArvind Sudarsanam CmdArgs.push_back(File); 225eeee5a44SArvind Sudarsanam // Create a new file to write the linked device file to. 226eeee5a44SArvind Sudarsanam auto OutFileOrErr = 227eeee5a44SArvind Sudarsanam createTempFile(Args, sys::path::filename(OutputFile), "bc"); 228eeee5a44SArvind Sudarsanam if (!OutFileOrErr) 229eeee5a44SArvind Sudarsanam return OutFileOrErr.takeError(); 230eeee5a44SArvind Sudarsanam CmdArgs.push_back("-o"); 231eeee5a44SArvind Sudarsanam CmdArgs.push_back(*OutFileOrErr); 232eeee5a44SArvind Sudarsanam CmdArgs.push_back("--suppress-warnings"); 233eeee5a44SArvind Sudarsanam if (Error Err = executeCommands(*LLVMLinkPath, CmdArgs)) 234eeee5a44SArvind Sudarsanam return std::move(Err); 23583ce977bSNick Sarnie return Args.MakeArgString(*OutFileOrErr); 236eeee5a44SArvind Sudarsanam } 237eeee5a44SArvind Sudarsanam 238eeee5a44SArvind Sudarsanam // This utility function is used to gather all SYCL device library files that 239eeee5a44SArvind Sudarsanam // will be linked with input device files. 240eeee5a44SArvind Sudarsanam // The list of files and its location are passed from driver. 241eeee5a44SArvind Sudarsanam Expected<SmallVector<std::string>> getSYCLDeviceLibs(const ArgList &Args) { 242eeee5a44SArvind Sudarsanam SmallVector<std::string> DeviceLibFiles; 243eeee5a44SArvind Sudarsanam StringRef LibraryPath; 244eeee5a44SArvind Sudarsanam if (Arg *A = Args.getLastArg(OPT_library_path_EQ)) 245eeee5a44SArvind Sudarsanam LibraryPath = A->getValue(); 246eeee5a44SArvind Sudarsanam if (LibraryPath.empty()) 247eeee5a44SArvind Sudarsanam return DeviceLibFiles; 248eeee5a44SArvind Sudarsanam if (Arg *A = Args.getLastArg(OPT_device_libs_EQ)) { 249eeee5a44SArvind Sudarsanam if (A->getValues().size() == 0) 250eeee5a44SArvind Sudarsanam return createStringError( 251eeee5a44SArvind Sudarsanam inconvertibleErrorCode(), 252eeee5a44SArvind Sudarsanam "Number of device library files cannot be zero."); 253eeee5a44SArvind Sudarsanam for (StringRef Val : A->getValues()) { 254eeee5a44SArvind Sudarsanam SmallString<128> LibName(LibraryPath); 255eeee5a44SArvind Sudarsanam llvm::sys::path::append(LibName, Val); 256eeee5a44SArvind Sudarsanam if (llvm::sys::fs::exists(LibName)) 257eeee5a44SArvind Sudarsanam DeviceLibFiles.push_back(std::string(LibName)); 258eeee5a44SArvind Sudarsanam else 259eeee5a44SArvind Sudarsanam return createStringError(inconvertibleErrorCode(), 260eeee5a44SArvind Sudarsanam "\'" + std::string(LibName) + "\'" + 261eeee5a44SArvind Sudarsanam " SYCL device library file is not found."); 262eeee5a44SArvind Sudarsanam } 263eeee5a44SArvind Sudarsanam } 264eeee5a44SArvind Sudarsanam return DeviceLibFiles; 265eeee5a44SArvind Sudarsanam } 266eeee5a44SArvind Sudarsanam 267eeee5a44SArvind Sudarsanam /// Link all device library files and input file into one LLVM IR file. This 268eeee5a44SArvind Sudarsanam /// linking is performed using llvm-link tool. 269eeee5a44SArvind Sudarsanam /// 'InputFiles' is the list of all LLVM IR device input files. 270eeee5a44SArvind Sudarsanam /// 'Args' encompasses all arguments required for linking device code and will 271eeee5a44SArvind Sudarsanam /// be parsed to generate options required to be passed into llvm-link tool. 272eeee5a44SArvind Sudarsanam static Expected<StringRef> linkDeviceLibFiles(StringRef InputFile, 273eeee5a44SArvind Sudarsanam const ArgList &Args) { 274eeee5a44SArvind Sudarsanam llvm::TimeTraceScope TimeScope("LinkDeviceLibraryFiles"); 275eeee5a44SArvind Sudarsanam 276eeee5a44SArvind Sudarsanam auto SYCLDeviceLibFiles = getSYCLDeviceLibs(Args); 277eeee5a44SArvind Sudarsanam if (!SYCLDeviceLibFiles) 278eeee5a44SArvind Sudarsanam return SYCLDeviceLibFiles.takeError(); 279eeee5a44SArvind Sudarsanam if ((*SYCLDeviceLibFiles).empty()) 280eeee5a44SArvind Sudarsanam return InputFile; 281eeee5a44SArvind Sudarsanam 282eeee5a44SArvind Sudarsanam Expected<std::string> LLVMLinkPath = 283eeee5a44SArvind Sudarsanam findProgram(Args, "llvm-link", {getMainExecutable("llvm-link")}); 284eeee5a44SArvind Sudarsanam if (!LLVMLinkPath) 285eeee5a44SArvind Sudarsanam return LLVMLinkPath.takeError(); 286eeee5a44SArvind Sudarsanam 287eeee5a44SArvind Sudarsanam // Create a new file to write the linked device file to. 288eeee5a44SArvind Sudarsanam auto OutFileOrErr = 289eeee5a44SArvind Sudarsanam createTempFile(Args, sys::path::filename(OutputFile), "bc"); 290eeee5a44SArvind Sudarsanam if (!OutFileOrErr) 291eeee5a44SArvind Sudarsanam return OutFileOrErr.takeError(); 292eeee5a44SArvind Sudarsanam 293eeee5a44SArvind Sudarsanam SmallVector<StringRef, 8> CmdArgs; 294eeee5a44SArvind Sudarsanam CmdArgs.push_back(*LLVMLinkPath); 295eeee5a44SArvind Sudarsanam CmdArgs.push_back("-only-needed"); 296eeee5a44SArvind Sudarsanam CmdArgs.push_back(InputFile); 297eeee5a44SArvind Sudarsanam for (auto &File : *SYCLDeviceLibFiles) 298eeee5a44SArvind Sudarsanam CmdArgs.push_back(File); 299eeee5a44SArvind Sudarsanam CmdArgs.push_back("-o"); 300eeee5a44SArvind Sudarsanam CmdArgs.push_back(*OutFileOrErr); 301eeee5a44SArvind Sudarsanam CmdArgs.push_back("--suppress-warnings"); 302eeee5a44SArvind Sudarsanam if (Error Err = executeCommands(*LLVMLinkPath, CmdArgs)) 303eeee5a44SArvind Sudarsanam return std::move(Err); 304eeee5a44SArvind Sudarsanam return *OutFileOrErr; 305eeee5a44SArvind Sudarsanam } 306eeee5a44SArvind Sudarsanam 307eeee5a44SArvind Sudarsanam /// Add any llvm-spirv option that relies on a specific Triple in addition 308eeee5a44SArvind Sudarsanam /// to user supplied options. 309eeee5a44SArvind Sudarsanam static void getSPIRVTransOpts(const ArgList &Args, 310eeee5a44SArvind Sudarsanam SmallVector<StringRef, 8> &TranslatorArgs, 311eeee5a44SArvind Sudarsanam const llvm::Triple Triple) { 312eeee5a44SArvind Sudarsanam // Enable NonSemanticShaderDebugInfo.200 for non-Windows 313eeee5a44SArvind Sudarsanam const bool IsWindowsMSVC = 314eeee5a44SArvind Sudarsanam Triple.isWindowsMSVCEnvironment() || Args.hasArg(OPT_is_windows_msvc_env); 315eeee5a44SArvind Sudarsanam const bool EnableNonSemanticDebug = !IsWindowsMSVC; 316eeee5a44SArvind Sudarsanam if (EnableNonSemanticDebug) { 317eeee5a44SArvind Sudarsanam TranslatorArgs.push_back( 318eeee5a44SArvind Sudarsanam "-spirv-debug-info-version=nonsemantic-shader-200"); 319eeee5a44SArvind Sudarsanam } else { 320eeee5a44SArvind Sudarsanam TranslatorArgs.push_back("-spirv-debug-info-version=ocl-100"); 321eeee5a44SArvind Sudarsanam // Prevent crash in the translator if input IR contains DIExpression 322eeee5a44SArvind Sudarsanam // operations which don't have mapping to OpenCL.DebugInfo.100 spec. 323eeee5a44SArvind Sudarsanam TranslatorArgs.push_back("-spirv-allow-extra-diexpressions"); 324eeee5a44SArvind Sudarsanam } 325eeee5a44SArvind Sudarsanam std::string UnknownIntrinsics("-spirv-allow-unknown-intrinsics=llvm.genx."); 326eeee5a44SArvind Sudarsanam 327eeee5a44SArvind Sudarsanam TranslatorArgs.push_back(Args.MakeArgString(UnknownIntrinsics)); 328eeee5a44SArvind Sudarsanam 329eeee5a44SArvind Sudarsanam // Disable all the extensions by default 330eeee5a44SArvind Sudarsanam std::string ExtArg("-spirv-ext=-all"); 331eeee5a44SArvind Sudarsanam std::string DefaultExtArg = 332eeee5a44SArvind Sudarsanam ",+SPV_EXT_shader_atomic_float_add,+SPV_EXT_shader_atomic_float_min_max" 333eeee5a44SArvind Sudarsanam ",+SPV_KHR_no_integer_wrap_decoration,+SPV_KHR_float_controls" 334eeee5a44SArvind Sudarsanam ",+SPV_KHR_expect_assume,+SPV_KHR_linkonce_odr"; 335eeee5a44SArvind Sudarsanam std::string INTELExtArg = 336eeee5a44SArvind Sudarsanam ",+SPV_INTEL_subgroups,+SPV_INTEL_media_block_io" 337eeee5a44SArvind Sudarsanam ",+SPV_INTEL_device_side_avc_motion_estimation" 338eeee5a44SArvind Sudarsanam ",+SPV_INTEL_fpga_loop_controls,+SPV_INTEL_unstructured_loop_controls" 339eeee5a44SArvind Sudarsanam ",+SPV_INTEL_fpga_reg,+SPV_INTEL_blocking_pipes" 340eeee5a44SArvind Sudarsanam ",+SPV_INTEL_function_pointers,+SPV_INTEL_kernel_attributes" 341eeee5a44SArvind Sudarsanam ",+SPV_INTEL_io_pipes,+SPV_INTEL_inline_assembly" 342eeee5a44SArvind Sudarsanam ",+SPV_INTEL_arbitrary_precision_integers" 343eeee5a44SArvind Sudarsanam ",+SPV_INTEL_float_controls2,+SPV_INTEL_vector_compute" 344eeee5a44SArvind Sudarsanam ",+SPV_INTEL_fast_composite" 345eeee5a44SArvind Sudarsanam ",+SPV_INTEL_arbitrary_precision_fixed_point" 346eeee5a44SArvind Sudarsanam ",+SPV_INTEL_arbitrary_precision_floating_point" 347eeee5a44SArvind Sudarsanam ",+SPV_INTEL_variable_length_array,+SPV_INTEL_fp_fast_math_mode" 348eeee5a44SArvind Sudarsanam ",+SPV_INTEL_long_constant_composite" 349eeee5a44SArvind Sudarsanam ",+SPV_INTEL_arithmetic_fence" 350eeee5a44SArvind Sudarsanam ",+SPV_INTEL_global_variable_decorations" 351eeee5a44SArvind Sudarsanam ",+SPV_INTEL_cache_controls" 352eeee5a44SArvind Sudarsanam ",+SPV_INTEL_fpga_buffer_location" 353eeee5a44SArvind Sudarsanam ",+SPV_INTEL_fpga_argument_interfaces" 354eeee5a44SArvind Sudarsanam ",+SPV_INTEL_fpga_invocation_pipelining_attributes" 355eeee5a44SArvind Sudarsanam ",+SPV_INTEL_fpga_latency_control" 356eeee5a44SArvind Sudarsanam ",+SPV_INTEL_task_sequence" 357eeee5a44SArvind Sudarsanam ",+SPV_KHR_shader_clock" 358eeee5a44SArvind Sudarsanam ",+SPV_INTEL_bindless_images"; 359eeee5a44SArvind Sudarsanam ExtArg = ExtArg + DefaultExtArg + INTELExtArg; 360eeee5a44SArvind Sudarsanam ExtArg += ",+SPV_INTEL_token_type" 361eeee5a44SArvind Sudarsanam ",+SPV_INTEL_bfloat16_conversion" 362eeee5a44SArvind Sudarsanam ",+SPV_INTEL_joint_matrix" 363eeee5a44SArvind Sudarsanam ",+SPV_INTEL_hw_thread_queries" 364eeee5a44SArvind Sudarsanam ",+SPV_KHR_uniform_group_instructions" 365eeee5a44SArvind Sudarsanam ",+SPV_INTEL_masked_gather_scatter" 366eeee5a44SArvind Sudarsanam ",+SPV_INTEL_tensor_float32_conversion" 367eeee5a44SArvind Sudarsanam ",+SPV_INTEL_optnone" 368eeee5a44SArvind Sudarsanam ",+SPV_KHR_non_semantic_info" 369eeee5a44SArvind Sudarsanam ",+SPV_KHR_cooperative_matrix"; 370eeee5a44SArvind Sudarsanam TranslatorArgs.push_back(Args.MakeArgString(ExtArg)); 371eeee5a44SArvind Sudarsanam } 372eeee5a44SArvind Sudarsanam 373eeee5a44SArvind Sudarsanam /// Run LLVM to SPIR-V translation. 374eeee5a44SArvind Sudarsanam /// Converts 'File' from LLVM bitcode to SPIR-V format using llvm-spirv tool. 375eeee5a44SArvind Sudarsanam /// 'Args' encompasses all arguments required for linking device code and will 376eeee5a44SArvind Sudarsanam /// be parsed to generate options required to be passed into llvm-spirv tool. 377eeee5a44SArvind Sudarsanam static Expected<StringRef> runLLVMToSPIRVTranslation(StringRef File, 378eeee5a44SArvind Sudarsanam const ArgList &Args) { 379eeee5a44SArvind Sudarsanam llvm::TimeTraceScope TimeScope("LLVMToSPIRVTranslation"); 380eeee5a44SArvind Sudarsanam StringRef LLVMSPIRVPath = Args.getLastArgValue(OPT_llvm_spirv_path_EQ); 381eeee5a44SArvind Sudarsanam Expected<std::string> LLVMToSPIRVProg = 382eeee5a44SArvind Sudarsanam findProgram(Args, "llvm-spirv", {LLVMSPIRVPath}); 383eeee5a44SArvind Sudarsanam if (!LLVMToSPIRVProg) 384eeee5a44SArvind Sudarsanam return LLVMToSPIRVProg.takeError(); 385eeee5a44SArvind Sudarsanam 386eeee5a44SArvind Sudarsanam SmallVector<StringRef, 8> CmdArgs; 387eeee5a44SArvind Sudarsanam CmdArgs.push_back(*LLVMToSPIRVProg); 388eeee5a44SArvind Sudarsanam const llvm::Triple Triple(Args.getLastArgValue(OPT_triple)); 389eeee5a44SArvind Sudarsanam getSPIRVTransOpts(Args, CmdArgs, Triple); 390eeee5a44SArvind Sudarsanam StringRef LLVMToSPIRVOptions; 391eeee5a44SArvind Sudarsanam if (Arg *A = Args.getLastArg(OPT_llvm_spirv_options_EQ)) 392eeee5a44SArvind Sudarsanam LLVMToSPIRVOptions = A->getValue(); 393eeee5a44SArvind Sudarsanam LLVMToSPIRVOptions.split(CmdArgs, " ", /* MaxSplit = */ -1, 394eeee5a44SArvind Sudarsanam /* KeepEmpty = */ false); 395eeee5a44SArvind Sudarsanam CmdArgs.append({"-o", OutputFile}); 396eeee5a44SArvind Sudarsanam CmdArgs.push_back(File); 397eeee5a44SArvind Sudarsanam if (Error Err = executeCommands(*LLVMToSPIRVProg, CmdArgs)) 398eeee5a44SArvind Sudarsanam return std::move(Err); 399eeee5a44SArvind Sudarsanam 400eeee5a44SArvind Sudarsanam if (!SPIRVDumpDir.empty()) { 401eeee5a44SArvind Sudarsanam std::error_code EC = 402eeee5a44SArvind Sudarsanam llvm::sys::fs::create_directory(SPIRVDumpDir, /*IgnoreExisting*/ true); 403eeee5a44SArvind Sudarsanam if (EC) 404eeee5a44SArvind Sudarsanam return createStringError( 405eeee5a44SArvind Sudarsanam EC, 406eeee5a44SArvind Sudarsanam formatv("failed to create dump directory. path: {0}, error_code: {1}", 407eeee5a44SArvind Sudarsanam SPIRVDumpDir, EC.value())); 408eeee5a44SArvind Sudarsanam 409eeee5a44SArvind Sudarsanam StringRef Path = OutputFile; 410eeee5a44SArvind Sudarsanam StringRef Filename = llvm::sys::path::filename(Path); 411eeee5a44SArvind Sudarsanam SmallString<128> CopyPath = SPIRVDumpDir; 412eeee5a44SArvind Sudarsanam CopyPath.append(Filename); 413eeee5a44SArvind Sudarsanam EC = llvm::sys::fs::copy_file(Path, CopyPath); 414eeee5a44SArvind Sudarsanam if (EC) 415eeee5a44SArvind Sudarsanam return createStringError( 416eeee5a44SArvind Sudarsanam EC, 417eeee5a44SArvind Sudarsanam formatv( 418eeee5a44SArvind Sudarsanam "failed to copy file. original: {0}, copy: {1}, error_code: {2}", 419eeee5a44SArvind Sudarsanam Path, CopyPath, EC.value())); 420eeee5a44SArvind Sudarsanam } 421eeee5a44SArvind Sudarsanam 422eeee5a44SArvind Sudarsanam return OutputFile; 423eeee5a44SArvind Sudarsanam } 424eeee5a44SArvind Sudarsanam 425eeee5a44SArvind Sudarsanam Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) { 426eeee5a44SArvind Sudarsanam llvm::TimeTraceScope TimeScope("SYCLDeviceLink"); 427eeee5a44SArvind Sudarsanam // First llvm-link step 428eeee5a44SArvind Sudarsanam auto LinkedFile = linkDeviceInputFiles(Files, Args); 429eeee5a44SArvind Sudarsanam if (!LinkedFile) 430eeee5a44SArvind Sudarsanam reportError(LinkedFile.takeError()); 431eeee5a44SArvind Sudarsanam 432eeee5a44SArvind Sudarsanam // second llvm-link step 433eeee5a44SArvind Sudarsanam auto DeviceLinkedFile = linkDeviceLibFiles(*LinkedFile, Args); 434eeee5a44SArvind Sudarsanam if (!DeviceLinkedFile) 435eeee5a44SArvind Sudarsanam reportError(DeviceLinkedFile.takeError()); 436eeee5a44SArvind Sudarsanam 437eeee5a44SArvind Sudarsanam // LLVM to SPIR-V translation step 438eeee5a44SArvind Sudarsanam auto SPVFile = runLLVMToSPIRVTranslation(*DeviceLinkedFile, Args); 439eeee5a44SArvind Sudarsanam if (!SPVFile) 440eeee5a44SArvind Sudarsanam return SPVFile.takeError(); 441eeee5a44SArvind Sudarsanam return Error::success(); 442eeee5a44SArvind Sudarsanam } 443eeee5a44SArvind Sudarsanam 444eeee5a44SArvind Sudarsanam } // namespace 445eeee5a44SArvind Sudarsanam 446eeee5a44SArvind Sudarsanam int main(int argc, char **argv) { 447eeee5a44SArvind Sudarsanam InitLLVM X(argc, argv); 448eeee5a44SArvind Sudarsanam 449eeee5a44SArvind Sudarsanam Executable = argv[0]; 450eeee5a44SArvind Sudarsanam sys::PrintStackTraceOnErrorSignal(argv[0]); 451eeee5a44SArvind Sudarsanam 452eeee5a44SArvind Sudarsanam const OptTable &Tbl = getOptTable(); 453eeee5a44SArvind Sudarsanam BumpPtrAllocator Alloc; 454eeee5a44SArvind Sudarsanam StringSaver Saver(Alloc); 455eeee5a44SArvind Sudarsanam auto Args = Tbl.parseArgs(argc, argv, OPT_INVALID, Saver, [&](StringRef Err) { 456eeee5a44SArvind Sudarsanam reportError(createStringError(inconvertibleErrorCode(), Err)); 457eeee5a44SArvind Sudarsanam }); 458eeee5a44SArvind Sudarsanam 459eeee5a44SArvind Sudarsanam if (Args.hasArg(OPT_help) || Args.hasArg(OPT_help_hidden)) { 460eeee5a44SArvind Sudarsanam Tbl.printHelp( 461eeee5a44SArvind Sudarsanam outs(), "clang-sycl-linker [options] <options to sycl link steps>", 462eeee5a44SArvind Sudarsanam "A utility that wraps around several steps required to link SYCL " 463eeee5a44SArvind Sudarsanam "device files.\n" 464eeee5a44SArvind Sudarsanam "This enables LLVM IR linking, post-linking and code generation for " 465eeee5a44SArvind Sudarsanam "SYCL targets.", 466eeee5a44SArvind Sudarsanam Args.hasArg(OPT_help_hidden), Args.hasArg(OPT_help_hidden)); 467eeee5a44SArvind Sudarsanam return EXIT_SUCCESS; 468eeee5a44SArvind Sudarsanam } 469eeee5a44SArvind Sudarsanam 470eeee5a44SArvind Sudarsanam if (Args.hasArg(OPT_version)) 471eeee5a44SArvind Sudarsanam printVersion(outs()); 472eeee5a44SArvind Sudarsanam 473eeee5a44SArvind Sudarsanam Verbose = Args.hasArg(OPT_verbose); 474eeee5a44SArvind Sudarsanam DryRun = Args.hasArg(OPT_dry_run); 475eeee5a44SArvind Sudarsanam SaveTemps = Args.hasArg(OPT_save_temps); 476eeee5a44SArvind Sudarsanam 477eeee5a44SArvind Sudarsanam OutputFile = "a.spv"; 478eeee5a44SArvind Sudarsanam if (Args.hasArg(OPT_o)) 479eeee5a44SArvind Sudarsanam OutputFile = Args.getLastArgValue(OPT_o); 480eeee5a44SArvind Sudarsanam 481eeee5a44SArvind Sudarsanam if (Args.hasArg(OPT_spirv_dump_device_code_EQ)) { 482eeee5a44SArvind Sudarsanam Arg *A = Args.getLastArg(OPT_spirv_dump_device_code_EQ); 483eeee5a44SArvind Sudarsanam SmallString<128> Dir(A->getValue()); 484eeee5a44SArvind Sudarsanam if (Dir.empty()) 485eeee5a44SArvind Sudarsanam llvm::sys::path::native(Dir = "./"); 486eeee5a44SArvind Sudarsanam else 487eeee5a44SArvind Sudarsanam Dir.append(llvm::sys::path::get_separator()); 488eeee5a44SArvind Sudarsanam 489eeee5a44SArvind Sudarsanam SPIRVDumpDir = Dir; 490eeee5a44SArvind Sudarsanam } 491eeee5a44SArvind Sudarsanam 492eeee5a44SArvind Sudarsanam // Get the input files to pass to the linking stage. 493eeee5a44SArvind Sudarsanam auto FilesOrErr = getInput(Args); 494eeee5a44SArvind Sudarsanam if (!FilesOrErr) 495eeee5a44SArvind Sudarsanam reportError(FilesOrErr.takeError()); 496eeee5a44SArvind Sudarsanam 497eeee5a44SArvind Sudarsanam // Run SYCL linking process on the generated inputs. 498eeee5a44SArvind Sudarsanam if (Error Err = runSYCLLink(*FilesOrErr, Args)) 499eeee5a44SArvind Sudarsanam reportError(std::move(Err)); 500eeee5a44SArvind Sudarsanam 501eeee5a44SArvind Sudarsanam // Remove the temporary files created. 502eeee5a44SArvind Sudarsanam if (!Args.hasArg(OPT_save_temps)) 503eeee5a44SArvind Sudarsanam for (const auto &TempFile : TempFiles) 504eeee5a44SArvind Sudarsanam if (std::error_code EC = sys::fs::remove(TempFile)) 505eeee5a44SArvind Sudarsanam reportError(createFileError(TempFile, EC)); 506eeee5a44SArvind Sudarsanam 507eeee5a44SArvind Sudarsanam return EXIT_SUCCESS; 508eeee5a44SArvind Sudarsanam } 509