xref: /llvm-project/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp (revision dd647e3e608ed0b2bac7c588d5859b80ef4a5976)
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