[clang] [Clang][SYCL] Introduce clang-sycl-link-wrapper to link SYCL offloading device code (Part 1 of many) (PR #112245)
Arvind Sudarsanam via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 15 11:11:04 PDT 2024
================
@@ -0,0 +1,528 @@
+//=-- clang-sycl-link-wrapper/ClangSYCLLinkWrapper.cpp - SYCL linker util --=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------------===//
+//
+// This tool wraps around the sequence of steps required to link device code in
+// SYCL fat objects. SYCL device code linking requires a complex sequence of
+// steps that include linking of llvm bitcode files, linking device library
+// files with the fully linked source bitcode file(s), running several SYCL
+// specific post-link steps on the fully linked bitcode file(s), and finally
+// generating target-specific device code. This tool can be removed once SYCL
+// linking is ported to `ld.lld`.
+//
+//===---------------------------------------------------------------------===//
+
+#include "clang/Basic/Version.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Bitcode/BitcodeWriter.h"
+#include "llvm/CodeGen/CommandFlags.h"
+#include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/LTO/LTO.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ArchiveWriter.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Object/IRObjectFile.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/OffloadBinary.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
+#include "llvm/Remarks/HotnessThresholdParser.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/StringSaver.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/TimeProfiler.h"
+#include "llvm/Support/WithColor.h"
+
+using namespace llvm;
+using namespace llvm::opt;
+using namespace llvm::object;
+
+/// Save intermediary results.
+static bool SaveTemps = false;
+
+/// Print arguments without executing.
+static bool DryRun = false;
+
+/// Print verbose output.
+static bool Verbose = false;
+
+/// Filename of the output being created.
+static StringRef OutputFile;
+
+/// Directory to dump SPIR-V IR if requested by user.
+static SmallString<128> SPIRVDumpDir;
+
+static void printVersion(raw_ostream &OS) {
+ OS << clang::getClangToolFullVersion("clang-sycl-link-wrapper") << '\n';
+}
+
+/// The value of `argv[0]` when run.
+static const char *Executable;
+
+/// Temporary files to be cleaned up.
+static SmallVector<SmallString<128>> TempFiles;
+
+namespace {
+// Must not overlap with llvm::opt::DriverFlag.
+enum WrapperFlags { WrapperOnlyOption = (1 << 4) };
+
+enum ID {
+ OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
+#include "SYCLLinkOpts.inc"
+ LastOption
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) \
+ static constexpr StringLiteral NAME##_init[] = VALUE; \
+ static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
+ std::size(NAME##_init) - 1);
+#include "SYCLLinkOpts.inc"
+#undef PREFIX
+
+static constexpr OptTable::Info InfoTable[] = {
+#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
+#include "SYCLLinkOpts.inc"
+#undef OPTION
+};
+
+class WrapperOptTable : public opt::GenericOptTable {
+public:
+ WrapperOptTable() : opt::GenericOptTable(InfoTable) {}
+};
+
+const OptTable &getOptTable() {
+ static const WrapperOptTable *Table = []() {
+ auto Result = std::make_unique<WrapperOptTable>();
+ return Result.release();
+ }();
+ return *Table;
+}
+
+[[noreturn]] void reportError(Error E) {
+ outs().flush();
+ logAllUnhandledErrors(std::move(E), WithColor::error(errs(), Executable));
+ exit(EXIT_FAILURE);
+}
+
+std::string getMainExecutable(const char *Name) {
+ void *Ptr = (void *)(intptr_t)&getMainExecutable;
+ auto COWPath = sys::fs::getMainExecutable(Name, Ptr);
+ return sys::path::parent_path(COWPath).str();
+}
+
+Expected<StringRef> createTempFile(const ArgList &Args, const Twine &Prefix,
+ StringRef Extension) {
+ SmallString<128> OutputFile;
+ if (Args.hasArg(OPT_save_temps)) {
+ // Generate a unique path name without creating a file
+ sys::fs::createUniquePath(Prefix + "-%%%%%%." + Extension, OutputFile,
+ /*MakeAbsolute=*/false);
+ } else {
+ if (std::error_code EC =
+ sys::fs::createTemporaryFile(Prefix, Extension, OutputFile))
+ return createFileError(OutputFile, EC);
+ }
+
+ TempFiles.emplace_back(std::move(OutputFile));
+ return TempFiles.back();
+}
+
+Expected<std::string> findProgram(const ArgList &Args, StringRef Name,
+ ArrayRef<StringRef> Paths) {
+ if (Args.hasArg(OPT_dry_run))
+ return Name.str();
+ ErrorOr<std::string> Path = sys::findProgramByName(Name, Paths);
+ if (!Path)
+ Path = sys::findProgramByName(Name);
+ if (!Path)
+ return createStringError(Path.getError(),
+ "Unable to find '" + Name + "' in path");
+ return *Path;
+}
+
+std::optional<std::string> findFile(StringRef Dir, StringRef Root,
+ const Twine &Name) {
+ SmallString<128> Path;
+ if (Dir.starts_with("="))
+ sys::path::append(Path, Root, Dir.substr(1), Name);
+ else
+ sys::path::append(Path, Dir, Name);
+
+ if (sys::fs::exists(Path))
+ return static_cast<std::string>(Path);
+ return std::nullopt;
+}
+
+void printCommands(ArrayRef<StringRef> CmdArgs) {
+ if (CmdArgs.empty())
+ return;
+
+ llvm::errs() << " \"" << CmdArgs.front() << "\" ";
+ llvm::errs() << llvm::join(std::next(CmdArgs.begin()), CmdArgs.end(), " ")
+ << "\n";
+}
+
+/// Execute the command \p ExecutablePath with the arguments \p Args.
+Error executeCommands(StringRef ExecutablePath, ArrayRef<StringRef> Args) {
+ if (Verbose || DryRun)
+ printCommands(Args);
+
+ if (!DryRun)
+ if (sys::ExecuteAndWait(ExecutablePath, Args))
+ return createStringError(
+ "'%s' failed", sys::path::filename(ExecutablePath).str().c_str());
+ return Error::success();
+}
+
+Expected<SmallVector<std::string>> getInput(const ArgList &Args) {
+ // Collect all input bitcode files to be passed to llvm-link.
+ SmallVector<std::string> BitcodeFiles;
+ for (const opt::Arg *Arg : Args.filtered(OPT_INPUT)) {
+ std::optional<std::string> Filename = std::string(Arg->getValue());
+ if (!Filename || !sys::fs::exists(*Filename) ||
+ sys::fs::is_directory(*Filename))
+ continue;
+ file_magic Magic;
+ if (auto EC = identify_magic(*Filename, Magic))
+ return createStringError("Failed to open file " + *Filename);
+ if (Magic != file_magic::bitcode)
+ return createStringError("Unsupported file type");
+ BitcodeFiles.push_back(*Filename);
+ }
+ return BitcodeFiles;
+}
+
+/// Link all SYCL device input files into one before adding device library
+/// files. Device linking is performed using llvm-link tool.
+/// 'InputFiles' is the list of all LLVM IR device input files.
+/// 'Args' encompasses all arguments required for linking and wrapping device
+/// code and will be parsed to generate options required to be passed into the
+/// llvm-link tool.
+Expected<StringRef> linkDeviceInputFiles(ArrayRef<std::string> InputFiles,
+ const ArgList &Args) {
+ llvm::TimeTraceScope TimeScope("SYCL LinkDeviceInputFiles");
+ Expected<std::string> LLVMLinkPath =
+ findProgram(Args, "llvm-link", {getMainExecutable("llvm-link")});
+ if (!LLVMLinkPath)
+ return LLVMLinkPath.takeError();
+
+ SmallVector<StringRef> CmdArgs;
+ CmdArgs.push_back(*LLVMLinkPath);
+ for (auto &File : InputFiles)
+ CmdArgs.push_back(File);
+ // Create a new file to write the linked device file to.
+ auto OutFileOrErr =
+ createTempFile(Args, sys::path::filename(OutputFile), "bc");
+ if (!OutFileOrErr)
+ return OutFileOrErr.takeError();
+ CmdArgs.push_back("-o");
+ CmdArgs.push_back(*OutFileOrErr);
+ CmdArgs.push_back("--suppress-warnings");
+ if (Error Err = executeCommands(*LLVMLinkPath, CmdArgs))
+ return std::move(Err);
+ return *OutFileOrErr;
+}
+
+const SmallVector<std::string> SYCLDeviceLibNames = {
----------------
asudarsa wrote:
Added a flag that the driver can use to pass in the libdevice libs in 307c74ea4f910a7698a6084db710ff7b699c5c93
However, we have not upstreamed the device library sources and build process. Will pass the files via the driver once available.
Thanks
https://github.com/llvm/llvm-project/pull/112245
More information about the cfe-commits
mailing list