From ce34ccd6fa0c358c7432662b54a44230a583e29f Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Tue, 25 Mar 2025 19:49:56 -0700 Subject: [PATCH 01/16] [clang-sycl-linker] Replace llvm-link with API calls Signed-off-by: Arvind Sudarsanam --- .../clang-sycl-linker/ClangSYCLLinker.cpp | 255 +++++++++++------- 1 file changed, 159 insertions(+), 96 deletions(-) diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index 2bcb3757d49d0..3a650a561a16c 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -21,8 +21,11 @@ #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" #include "llvm/LTO/LTO.h" +#include "llvm/Linker/Linker.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/Binary.h" @@ -66,6 +69,9 @@ static StringRef OutputFile; /// Directory to dump SPIR-V IR if requested by user. static SmallString<128> SPIRVDumpDir; +/// Mutex lock to protect writes to shared TempFiles in parallel. +static std::mutex TempFilesMutex; + static void printVersion(raw_ostream &OS) { OS << clang::getClangToolFullVersion("clang-sycl-linker") << '\n'; } @@ -179,60 +185,59 @@ Error executeCommands(StringRef ExecutablePath, ArrayRef Args) { return Error::success(); } -Expected> getInput(const ArgList &Args) { - // Collect all input bitcode files to be passed to llvm-link. +/// Returns a new ArgList containg arguments used for the device linking phase. +DerivedArgList getLinkerArgs(ArrayRef Input, + const InputArgList &Args) { + DerivedArgList DAL = DerivedArgList(DerivedArgList(Args)); + for (Arg *A : Args) + DAL.append(A); + + // Set the subarchitecture and target triple for this compilation. + const OptTable &Tbl = getOptTable(); + DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_arch), + Args.MakeArgString(Input.front().getBinary()->getArch())); + DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_triple), + Args.MakeArgString(Input.front().getBinary()->getTriple())); + + return DAL; +} + +Expected> getObjectsFromArgs(const ArgList &Args) { + // Collect all input bitcode files to be passed to llvm bitcode linking. SmallVector BitcodeFiles; for (const opt::Arg *Arg : Args.filtered(OPT_INPUT)) { std::optional 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); - // TODO: Current use case involves LLVM IR bitcode files as input. - // This will be extended to support objects and SPIR-V IR files. - if (Magic != file_magic::bitcode) - return createStringError("Unsupported file type"); - BitcodeFiles.push_back(*Filename); + BitcodeFiles.emplace_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 device code and will -/// be parsed to generate options required to be passed into llvm-link. -Expected linkDeviceInputFiles(ArrayRef InputFiles, - const ArgList &Args) { - llvm::TimeTraceScope TimeScope("SYCL LinkDeviceInputFiles"); - - assert(InputFiles.size() && "No inputs to llvm-link"); - // Early check to see if there is only one input. - if (InputFiles.size() < 2) - return InputFiles[0]; - - Expected LLVMLinkPath = - findProgram(Args, "llvm-link", {getMainExecutable("llvm-link")}); - if (!LLVMLinkPath) - return LLVMLinkPath.takeError(); - - SmallVector 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 Args.MakeArgString(*OutFileOrErr); +Expected> +getBitcodeInputs(SmallVector &Filenames) { + // Collect all input bitcode files to be passed to llvm bitcode linking. + SmallVector BitcodeFiles; + for (auto Filename : Filenames) { + ErrorOr> BufferOrErr = + MemoryBuffer::getFile(Filename); + if (std::error_code EC = BufferOrErr.getError()) + return createFileError(Filename, EC); + MemoryBufferRef Buffer = **BufferOrErr; + SmallVector Binaries; + if (Error Err = extractOffloadBinaries(Buffer, Binaries)) + return std::move(Err); + + auto ContainsBitcode = [](const OffloadFile &F) { + return identify_magic(F.getBinary()->getImage()) == file_magic::bitcode; + }; + for (auto &OffloadFile : Binaries) { + if (ContainsBitcode(OffloadFile)) + BitcodeFiles.emplace_back(std::move(OffloadFile)); + } + } + return BitcodeFiles; } // This utility function is used to gather all SYCL device library files that @@ -264,44 +269,86 @@ Expected> getSYCLDeviceLibs(const ArgList &Args) { return DeviceLibFiles; } -/// Link all device library files and input file into one LLVM IR file. This -/// linking is performed using llvm-link tool. +/// Link all SYCL device input files into one before adding device library +/// files. Device linking is performed using the LTO backend. /// 'InputFiles' is the list of all LLVM IR device input files. -/// 'Args' encompasses all arguments required for linking device code and will -/// be parsed to generate options required to be passed into llvm-link tool. -static Expected linkDeviceLibFiles(StringRef InputFile, - const ArgList &Args) { - llvm::TimeTraceScope TimeScope("LinkDeviceLibraryFiles"); +/// 'Args' encompasses all arguments required for linking device code. +Error linkDeviceInputFiles(ArrayRef BitcodeInputFiles, + const ArgList &Args, + std::unique_ptr BitcodeOutput) { + llvm::TimeTraceScope TimeScope("SYCL LinkDeviceInputFiles"); + LLVMContext Context; + SMDiagnostic Err; + assert(InputFiles.size() && "No inputs to link"); + // Early check to see if there is only one input. + if (BitcodeInputFiles.size() < 2) { + auto TheImage = BitcodeInputFiles[0].getBinary()->getImage(); + BitcodeOutput = parseIR(MemoryBufferRef(TheImage, ""), Err, Context); + if (!BitcodeOutput || verifyModule(*BitcodeOutput, &errs())) + return createStringError("Could not parse IR"); + return Error::success(); + } + Linker L(*BitcodeOutput); + for (auto &File : BitcodeInputFiles) { + auto TheImage = File.getBinary()->getImage(); + std::unique_ptr ModOrErr = + parseIR(MemoryBufferRef(TheImage, ""), Err, Context); + if (!ModOrErr || verifyModule(*ModOrErr, &errs())) + return createStringError("Could not parse IR"); + if (L.linkInModule(std::move(ModOrErr))) + return createStringError("Could not link IR"); + } + llvm::errs() << "Inside linkDeviceInputFiles: BitcodeOutput = \n" + << *BitcodeOutput << "\n"; + return Error::success(); +} - auto SYCLDeviceLibFiles = getSYCLDeviceLibs(Args); +/// Link all SYCL device input files into one before adding device library +/// files. Device linking is performed using the LTO backend. +/// 'InputFiles' is the list of all LLVM IR device input files. +/// 'Args' encompasses all arguments required for linking device code. +Expected linkDeviceLibFiles(std::unique_ptr Dest, + const ArgList &Args) { + llvm::TimeTraceScope TimeScope("SYCL LinkDeviceLibFiles"); + LLVMContext Context; + SMDiagnostic Err; + Linker L(*Dest); + // Link SYCL device library files + auto SYCLDeviceLibFilenames = getSYCLDeviceLibs(Args); + if (!SYCLDeviceLibFilenames) + return SYCLDeviceLibFilenames.takeError(); + auto SYCLDeviceLibFiles = getBitcodeInputs(*SYCLDeviceLibFilenames); if (!SYCLDeviceLibFiles) return SYCLDeviceLibFiles.takeError(); - if ((*SYCLDeviceLibFiles).empty()) - return InputFile; - - Expected LLVMLinkPath = - findProgram(Args, "llvm-link", {getMainExecutable("llvm-link")}); - if (!LLVMLinkPath) - return LLVMLinkPath.takeError(); - + if (!(*SYCLDeviceLibFiles).empty()) { + SMDiagnostic Err; + LLVMContext C; + const llvm::Triple Triple(Args.getLastArgValue(OPT_triple)); + for (auto &File : *SYCLDeviceLibFiles) { + auto TheImage = File.getBinary()->getImage(); + std::unique_ptr M = + parseIR(MemoryBufferRef(TheImage, ""), Err, C); + if (!M || verifyModule(*M, &errs())) + return createStringError("Could not parse IR in SYCL device lib file"); + if (M->getTargetTriple() == Triple) { + unsigned Flags = Linker::Flags::None; + Flags |= Linker::Flags::LinkOnlyNeeded; + if (L.linkInModule(std::move(M), Flags)) + return createStringError("Could not link IR"); + } + } + } // Create a new file to write the linked device file to. - auto OutFileOrErr = + auto BitcodeOutput = createTempFile(Args, sys::path::filename(OutputFile), "bc"); - if (!OutFileOrErr) - return OutFileOrErr.takeError(); - - SmallVector CmdArgs; - CmdArgs.push_back(*LLVMLinkPath); - CmdArgs.push_back("-only-needed"); - CmdArgs.push_back(InputFile); - for (auto &File : *SYCLDeviceLibFiles) - CmdArgs.push_back(File); - 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; + if (!BitcodeOutput) + return BitcodeOutput.takeError(); + int FD = -1; + if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeOutput, FD)) + return errorCodeToError(EC); + llvm::raw_fd_ostream OS(FD, true); + WriteBitcodeToFile(*Dest, OS); + return *BitcodeOutput; } /// Add any llvm-spirv option that relies on a specific Triple in addition @@ -345,7 +392,6 @@ static void getSPIRVTransOpts(const ArgList &Args, ",+SPV_INTEL_arbitrary_precision_fixed_point" ",+SPV_INTEL_arbitrary_precision_floating_point" ",+SPV_INTEL_variable_length_array,+SPV_INTEL_fp_fast_math_mode" - ",+SPV_INTEL_long_constant_composite" ",+SPV_INTEL_arithmetic_fence" ",+SPV_INTEL_global_variable_decorations" ",+SPV_INTEL_cache_controls" @@ -422,20 +468,29 @@ static Expected runLLVMToSPIRVTranslation(StringRef File, return OutputFile; } -Error runSYCLLink(ArrayRef Files, const ArgList &Args) { +Error runSYCLLink(ArrayRef Files, const ArgList &Args, char **Argv, + int Argc) { llvm::TimeTraceScope TimeScope("SYCLDeviceLink"); - // First llvm-link step - auto LinkedFile = linkDeviceInputFiles(Files, Args); - if (!LinkedFile) - reportError(LinkedFile.takeError()); - - // second llvm-link step - auto DeviceLinkedFile = linkDeviceLibFiles(*LinkedFile, Args); - if (!DeviceLinkedFile) - reportError(DeviceLinkedFile.takeError()); + // Update Args List for the SPIR-V device + const OptTable &Tbl = getOptTable(); + BumpPtrAllocator Alloc; + StringSaver Saver(Alloc); + auto BaseArgs = + Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, + [](StringRef Err) { reportError(createStringError(Err)); }); + auto LinkerArgs = getLinkerArgs(Files, BaseArgs); + // llvm-link step + LLVMContext C; + auto Dest = std::make_unique("link-file", C); + if (Error Err = linkDeviceInputFiles(Files, LinkerArgs, std::move(Dest))) + return Err; + llvm::errs() << "In main: Dest = \n" << *Dest << "\n"; + auto LinkedFileOrErr = linkDeviceLibFiles(std::move(Dest), LinkerArgs); + if (!LinkedFileOrErr) + reportError(LinkedFileOrErr.takeError()); // LLVM to SPIR-V translation step - auto SPVFile = runLLVMToSPIRVTranslation(*DeviceLinkedFile, Args); + auto SPVFile = runLLVMToSPIRVTranslation(*LinkedFileOrErr, LinkerArgs); if (!SPVFile) return SPVFile.takeError(); return Error::success(); @@ -443,16 +498,22 @@ Error runSYCLLink(ArrayRef Files, const ArgList &Args) { } // namespace -int main(int argc, char **argv) { - InitLLVM X(argc, argv); +int main(int Argc, char **Argv) { + InitLLVM X(Argc, Argv); - Executable = argv[0]; - sys::PrintStackTraceOnErrorSignal(argv[0]); + InitializeAllTargetInfos(); + InitializeAllTargets(); + InitializeAllTargetMCs(); + InitializeAllAsmParsers(); + InitializeAllAsmPrinters(); + + Executable = Argv[0]; + sys::PrintStackTraceOnErrorSignal(Argv[0]); const OptTable &Tbl = getOptTable(); BumpPtrAllocator Alloc; StringSaver Saver(Alloc); - auto Args = Tbl.parseArgs(argc, argv, OPT_INVALID, Saver, [&](StringRef Err) { + auto Args = Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, [&](StringRef Err) { reportError(createStringError(inconvertibleErrorCode(), Err)); }); @@ -490,12 +551,14 @@ int main(int argc, char **argv) { } // Get the input files to pass to the linking stage. - auto FilesOrErr = getInput(Args); + auto FilenamesOrErr = getObjectsFromArgs(Args); + if (!FilenamesOrErr) + reportError(FilenamesOrErr.takeError()); + auto FilesOrErr = getBitcodeInputs(*FilenamesOrErr); if (!FilesOrErr) reportError(FilesOrErr.takeError()); - // Run SYCL linking process on the generated inputs. - if (Error Err = runSYCLLink(*FilesOrErr, Args)) + if (Error Err = runSYCLLink(*FilesOrErr, Args, Argv, Argc)) reportError(std::move(Err)); // Remove the temporary files created. From ae0210ff043bfa830b3e661ef3ab07081c753d31 Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Wed, 26 Mar 2025 09:33:08 -0700 Subject: [PATCH 02/16] More changes Signed-off-by: Arvind Sudarsanam --- .../clang-sycl-linker/ClangSYCLLinker.cpp | 127 +++++++++--------- 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index 3a650a561a16c..3571cb2abe362 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -194,16 +194,19 @@ DerivedArgList getLinkerArgs(ArrayRef Input, // Set the subarchitecture and target triple for this compilation. const OptTable &Tbl = getOptTable(); - DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_arch), - Args.MakeArgString(Input.front().getBinary()->getArch())); - DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_triple), - Args.MakeArgString(Input.front().getBinary()->getTriple())); + if (!DryRun) { + DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_arch), + Args.MakeArgString(Input.front().getBinary()->getArch())); + DAL.AddJoinedArg( + nullptr, Tbl.getOption(OPT_triple), + Args.MakeArgString(Input.front().getBinary()->getTriple())); + } return DAL; } +/// Collect all fat objects from user input. Expected> getObjectsFromArgs(const ArgList &Args) { - // Collect all input bitcode files to be passed to llvm bitcode linking. SmallVector BitcodeFiles; for (const opt::Arg *Arg : Args.filtered(OPT_INPUT)) { std::optional Filename = std::string(Arg->getValue()); @@ -215,6 +218,7 @@ Expected> getObjectsFromArgs(const ArgList &Args) { return BitcodeFiles; } +/// Collect all input bitcode images from a list of fat objects. Expected> getBitcodeInputs(SmallVector &Filenames) { // Collect all input bitcode files to be passed to llvm bitcode linking. @@ -269,61 +273,69 @@ Expected> getSYCLDeviceLibs(const ArgList &Args) { return DeviceLibFiles; } -/// Link all SYCL device input files into one before adding device library -/// files. Device linking is performed using the LTO backend. -/// 'InputFiles' is the list of all LLVM IR device input files. -/// 'Args' encompasses all arguments required for linking device code. -Error linkDeviceInputFiles(ArrayRef BitcodeInputFiles, - const ArgList &Args, - std::unique_ptr BitcodeOutput) { - llvm::TimeTraceScope TimeScope("SYCL LinkDeviceInputFiles"); - LLVMContext Context; - SMDiagnostic Err; - assert(InputFiles.size() && "No inputs to link"); - // Early check to see if there is only one input. - if (BitcodeInputFiles.size() < 2) { - auto TheImage = BitcodeInputFiles[0].getBinary()->getImage(); - BitcodeOutput = parseIR(MemoryBufferRef(TheImage, ""), Err, Context); - if (!BitcodeOutput || verifyModule(*BitcodeOutput, &errs())) - return createStringError("Could not parse IR"); - return Error::success(); +/// Following tasks are performed: +/// 1. Link all SYCL device bitcode images into one image. Device linking is +/// performed using the linkModules API. +/// 2. Gather all SYCL device library bitcode images. +/// 3. Link all the images gathered in Step 2 with the output of Step 1 using +/// linkModules API. LinkOnlyNeeded flag is used. +Expected linkDeviceCode(ArrayRef BitcodeInputFiles, + const ArgList &Args, LLVMContext &C) { + llvm::TimeTraceScope TimeScope("SYCL Link Device Code"); + // Gather SYCL device library files. + auto SYCLDeviceLibFilenames = getSYCLDeviceLibs(Args); + if (!SYCLDeviceLibFilenames) + return SYCLDeviceLibFilenames.takeError(); + // Create a new file to write the linked device file to. + auto BitcodeOutput = + createTempFile(Args, sys::path::filename(OutputFile), "bc"); + if (!BitcodeOutput) + return BitcodeOutput.takeError(); + int FD = -1; + if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeOutput, FD)) + return errorCodeToError(EC); + if (DryRun || Verbose) { + std::for_each((*SYCLDeviceLibFilenames).begin(), + (*SYCLDeviceLibFilenames).end(), + [](std::string &Filename) { Filename += "; "; }); + std::string LibInputs = + std::accumulate((*SYCLDeviceLibFilenames).begin(), + (*SYCLDeviceLibFilenames).end(), std::string("")); + + auto FilenamesOrErr = getObjectsFromArgs(Args); + if (!FilenamesOrErr) + reportError(FilenamesOrErr.takeError()); + std::for_each((*FilenamesOrErr).begin(), (*FilenamesOrErr).end(), + [](std::string &Filename) { Filename += "; "; }); + std::string Inputs = std::accumulate( + (*FilenamesOrErr).begin(), (*FilenamesOrErr).end(), std::string("")); + + errs() << formatv( + "sycl-device-link: input: {0} libfiles: {1} output: {2}\n", Inputs, + LibInputs, *BitcodeOutput); + if (DryRun) + return *BitcodeOutput; } - Linker L(*BitcodeOutput); + SMDiagnostic Err; + assert(BitcodeInputFiles.size() && "No inputs to link"); + auto Output = std::make_unique("sycl-device-link", C); + Linker L(*Output); + // Link SYCL device input files. for (auto &File : BitcodeInputFiles) { auto TheImage = File.getBinary()->getImage(); std::unique_ptr ModOrErr = - parseIR(MemoryBufferRef(TheImage, ""), Err, Context); + parseIR(MemoryBufferRef(TheImage, ""), Err, C); if (!ModOrErr || verifyModule(*ModOrErr, &errs())) return createStringError("Could not parse IR"); if (L.linkInModule(std::move(ModOrErr))) return createStringError("Could not link IR"); } - llvm::errs() << "Inside linkDeviceInputFiles: BitcodeOutput = \n" - << *BitcodeOutput << "\n"; - return Error::success(); -} - -/// Link all SYCL device input files into one before adding device library -/// files. Device linking is performed using the LTO backend. -/// 'InputFiles' is the list of all LLVM IR device input files. -/// 'Args' encompasses all arguments required for linking device code. -Expected linkDeviceLibFiles(std::unique_ptr Dest, - const ArgList &Args) { - llvm::TimeTraceScope TimeScope("SYCL LinkDeviceLibFiles"); - LLVMContext Context; - SMDiagnostic Err; - Linker L(*Dest); - // Link SYCL device library files - auto SYCLDeviceLibFilenames = getSYCLDeviceLibs(Args); - if (!SYCLDeviceLibFilenames) - return SYCLDeviceLibFilenames.takeError(); auto SYCLDeviceLibFiles = getBitcodeInputs(*SYCLDeviceLibFilenames); if (!SYCLDeviceLibFiles) return SYCLDeviceLibFiles.takeError(); if (!(*SYCLDeviceLibFiles).empty()) { - SMDiagnostic Err; - LLVMContext C; const llvm::Triple Triple(Args.getLastArgValue(OPT_triple)); + // Link in SYCL device library files. for (auto &File : *SYCLDeviceLibFiles) { auto TheImage = File.getBinary()->getImage(); std::unique_ptr M = @@ -338,16 +350,8 @@ Expected linkDeviceLibFiles(std::unique_ptr Dest, } } } - // Create a new file to write the linked device file to. - auto BitcodeOutput = - createTempFile(Args, sys::path::filename(OutputFile), "bc"); - if (!BitcodeOutput) - return BitcodeOutput.takeError(); - int FD = -1; - if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeOutput, FD)) - return errorCodeToError(EC); llvm::raw_fd_ostream OS(FD, true); - WriteBitcodeToFile(*Dest, OS); + WriteBitcodeToFile(*Output, OS); return *BitcodeOutput; } @@ -475,24 +479,21 @@ Error runSYCLLink(ArrayRef Files, const ArgList &Args, char **Argv, const OptTable &Tbl = getOptTable(); BumpPtrAllocator Alloc; StringSaver Saver(Alloc); + LLVMContext C; auto BaseArgs = Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, [](StringRef Err) { reportError(createStringError(Err)); }); auto LinkerArgs = getLinkerArgs(Files, BaseArgs); - // llvm-link step - LLVMContext C; - auto Dest = std::make_unique("link-file", C); - if (Error Err = linkDeviceInputFiles(Files, LinkerArgs, std::move(Dest))) - return Err; - llvm::errs() << "In main: Dest = \n" << *Dest << "\n"; - auto LinkedFileOrErr = linkDeviceLibFiles(std::move(Dest), LinkerArgs); + // Linking device code + auto LinkedFileOrErr = linkDeviceCode(Files, LinkerArgs, C); if (!LinkedFileOrErr) reportError(LinkedFileOrErr.takeError()); - // LLVM to SPIR-V translation step + // LLVM to SPIR-V translation auto SPVFile = runLLVMToSPIRVTranslation(*LinkedFileOrErr, LinkerArgs); if (!SPVFile) return SPVFile.takeError(); + return Error::success(); } From ee487ff47c35fdac39463a46ff5cca3d7c858430 Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Wed, 26 Mar 2025 11:30:43 -0700 Subject: [PATCH 03/16] More changes Signed-off-by: Arvind Sudarsanam --- .../clang-sycl-linker/ClangSYCLLinker.cpp | 42 +++++++------------ 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index 3571cb2abe362..c572a0f5f2c96 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -69,9 +69,6 @@ static StringRef OutputFile; /// Directory to dump SPIR-V IR if requested by user. static SmallString<128> SPIRVDumpDir; -/// Mutex lock to protect writes to shared TempFiles in parallel. -static std::mutex TempFilesMutex; - static void printVersion(raw_ostream &OS) { OS << clang::getClangToolFullVersion("clang-sycl-linker") << '\n'; } @@ -191,16 +188,15 @@ DerivedArgList getLinkerArgs(ArrayRef Input, DerivedArgList DAL = DerivedArgList(DerivedArgList(Args)); for (Arg *A : Args) DAL.append(A); + if (DryRun) + return DAL; // Set the subarchitecture and target triple for this compilation. const OptTable &Tbl = getOptTable(); - if (!DryRun) { - DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_arch), - Args.MakeArgString(Input.front().getBinary()->getArch())); - DAL.AddJoinedArg( - nullptr, Tbl.getOption(OPT_triple), - Args.MakeArgString(Input.front().getBinary()->getTriple())); - } + DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_arch), + Args.MakeArgString(Input.front().getBinary()->getArch())); + DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_triple), + Args.MakeArgString(Input.front().getBinary()->getTriple())); return DAL; } @@ -472,19 +468,15 @@ static Expected runLLVMToSPIRVTranslation(StringRef File, return OutputFile; } -Error runSYCLLink(ArrayRef Files, const ArgList &Args, char **Argv, - int Argc) { +Error runSYCLLink(ArrayRef Files, const InputArgList &Args) { llvm::TimeTraceScope TimeScope("SYCLDeviceLink"); - // Update Args List for the SPIR-V device - const OptTable &Tbl = getOptTable(); - BumpPtrAllocator Alloc; - StringSaver Saver(Alloc); + LLVMContext C; - auto BaseArgs = - Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, - [](StringRef Err) { reportError(createStringError(Err)); }); - auto LinkerArgs = getLinkerArgs(Files, BaseArgs); - // Linking device code + + // Update args list with triple and arch information for SPIR-V device + auto LinkerArgs = getLinkerArgs(Files, Args); + + // Linking device bitcode files auto LinkedFileOrErr = linkDeviceCode(Files, LinkerArgs, C); if (!LinkedFileOrErr) reportError(LinkedFileOrErr.takeError()); @@ -502,12 +494,6 @@ Error runSYCLLink(ArrayRef Files, const ArgList &Args, char **Argv, int main(int Argc, char **Argv) { InitLLVM X(Argc, Argv); - InitializeAllTargetInfos(); - InitializeAllTargets(); - InitializeAllTargetMCs(); - InitializeAllAsmParsers(); - InitializeAllAsmPrinters(); - Executable = Argv[0]; sys::PrintStackTraceOnErrorSignal(Argv[0]); @@ -559,7 +545,7 @@ int main(int Argc, char **Argv) { if (!FilesOrErr) reportError(FilesOrErr.takeError()); // Run SYCL linking process on the generated inputs. - if (Error Err = runSYCLLink(*FilesOrErr, Args, Argv, Argc)) + if (Error Err = runSYCLLink(*FilesOrErr, Args)) reportError(std::move(Err)); // Remove the temporary files created. From a9255b448e671942b5a037bf4675db59cf9910cf Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Wed, 26 Mar 2025 12:05:20 -0700 Subject: [PATCH 04/16] Add tests Signed-off-by: Arvind Sudarsanam --- clang/test/Driver/clang-sycl-linker-test.cpp | 43 +++++++------------ clang/test/Driver/sycl-link-spirv-target.cpp | 6 +-- .../clang-sycl-linker/ClangSYCLLinker.cpp | 3 ++ 3 files changed, 21 insertions(+), 31 deletions(-) diff --git a/clang/test/Driver/clang-sycl-linker-test.cpp b/clang/test/Driver/clang-sycl-linker-test.cpp index f358900b4fbd8..ea02d82c28518 100644 --- a/clang/test/Driver/clang-sycl-linker-test.cpp +++ b/clang/test/Driver/clang-sycl-linker-test.cpp @@ -1,48 +1,35 @@ // Tests the clang-sycl-linker tool. // // Test a simple case without arguments. -// RUN: %clangxx -emit-llvm -c %s -o %t_1.bc -// RUN: %clangxx -emit-llvm -c %s -o %t_2.bc -// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ +// RUN: %clangxx -c -target spirv64 %s -o %t_1.o +// RUN: %clangxx -c -target spirv64 %s -o %t_2.o +// RUN: clang-sycl-linker --dry-run %t_1.o %t_2.o -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=SIMPLE -// SIMPLE: "{{.*}}llvm-link{{.*}}" {{.*}}.bc {{.*}}.bc -o [[FIRSTLLVMLINKOUT:.*]].bc --suppress-warnings -// SIMPLE-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[FIRSTLLVMLINKOUT]].bc -// -// Test that llvm-link is not called when only one input is present. -// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc -o a.spv 2>&1 \ -// RUN: | FileCheck %s --check-prefix=SIMPLE-NO-LINK -// SIMPLE-NO-LINK: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv {{.*}}.bc +// SIMPLE: sycl-device-link: input: {{.*}}.o; {{.*}}.o; libfiles: output: [[LLVMLINKOUT:.*]].bc +// SIMPLE-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc // // Test a simple case with device library files specified. -// RUN: touch %T/lib1.bc -// RUN: touch %T/lib2.bc -// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.bc,lib2.bc -o a.spv 2>&1 \ +// RUN: touch %T/lib1.o +// RUN: touch %T/lib2.o +// RUN: clang-sycl-linker --dry-run %t_1.o %t_2.o --library-path=%T --device-libs=lib1.o,lib2.o -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBS -// DEVLIBS: "{{.*}}llvm-link{{.*}}" {{.*}}.bc {{.*}}.bc -o [[FIRSTLLVMLINKOUT:.*]].bc --suppress-warnings -// DEVLIBS-NEXT: "{{.*}}llvm-link{{.*}}" -only-needed [[FIRSTLLVMLINKOUT]].bc {{.*}}lib1.bc {{.*}}lib2.bc -o [[SECONDLLVMLINKOUT:.*]].bc --suppress-warnings -// DEVLIBS-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[SECONDLLVMLINKOUT]].bc -// -// Test a simple case with .o (fat object) as input. -// TODO: Remove this test once fat object support is added. -// RUN: %clangxx -c %s -o %t.o -// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t.o -o a.spv 2>&1 \ -// RUN: | FileCheck %s --check-prefix=FILETYPEERROR -// FILETYPEERROR: Unsupported file type +// DEVLIBS: sycl-device-link: input: {{.*}}.o; libfiles: {{.*}}lib1.o; {{.*}}lib2.o; output: [[LLVMLINKOUT:.*]].bc +// DEVLIBS-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc // // Test to see if device library related errors are emitted. -// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs= -o a.spv 2>&1 \ +// RUN: not clang-sycl-linker --dry-run %t_1.o %t_2.o --library-path=%T --device-libs= -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBSERR1 // DEVLIBSERR1: Number of device library files cannot be zero -// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.bc,lib2.bc,lib3.bc -o a.spv 2>&1 \ +// RUN: not clang-sycl-linker --dry-run %t_1.o %t_2.o --library-path=%T --device-libs=lib1.o,lib2.o,lib3.o -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBSERR2 -// DEVLIBSERR2: '{{.*}}lib3.bc' SYCL device library file is not found +// DEVLIBSERR2: '{{.*}}lib3.o' SYCL device library file is not found // // Test if correct set of llvm-spirv options are emitted for windows environment. -// RUN: clang-sycl-linker --dry-run -triple spirv64 --is-windows-msvc-env %t_1.bc %t_2.bc -o a.spv 2>&1 \ +// RUN: clang-sycl-linker --dry-run --is-windows-msvc-env %t_1.o %t_2.o -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=LLVMOPTSWIN // LLVMOPTSWIN: -spirv-debug-info-version=ocl-100 -spirv-allow-extra-diexpressions -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext= // // Test if correct set of llvm-spirv options are emitted for linux environment. -// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ +// RUN: clang-sycl-linker --dry-run %t_1.o %t_2.o -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=LLVMOPTSLIN // LLVMOPTSLIN: -spirv-debug-info-version=nonsemantic-shader-200 -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext= diff --git a/clang/test/Driver/sycl-link-spirv-target.cpp b/clang/test/Driver/sycl-link-spirv-target.cpp index 85566c67ea92b..a1214a6c3954b 100644 --- a/clang/test/Driver/sycl-link-spirv-target.cpp +++ b/clang/test/Driver/sycl-link-spirv-target.cpp @@ -2,8 +2,8 @@ // architecture. // // Test that -Xlinker options are being passed to clang-sycl-linker. -// RUN: touch %t.bc +// RUN: touch %t.o // RUN: %clangxx -### --target=spirv64 --sycl-link -Xlinker --llvm-spirv-path=/tmp \ -// RUN: -Xlinker --library-path=/tmp -Xlinker --device-libs=lib1.bc,lib2.bc %t.bc 2>&1 \ +// RUN: -Xlinker --library-path=/tmp -Xlinker --device-libs=lib1.o,lib2.o %t.o 2>&1 \ // RUN: | FileCheck %s -check-prefix=XLINKEROPTS -// XLINKEROPTS: "{{.*}}clang-sycl-linker{{.*}}" "--llvm-spirv-path=/tmp" "--library-path=/tmp" "--device-libs=lib1.bc,lib2.bc" "{{.*}}.bc" "-o" "a.out" +// XLINKEROPTS: "{{.*}}clang-sycl-linker{{.*}}" "--llvm-spirv-path=/tmp" "--library-path=/tmp" "--device-libs=lib1.o,lib2.o" "{{.*}}.o" "-o" "a.out" diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index c572a0f5f2c96..3854561da99b5 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -235,6 +235,8 @@ getBitcodeInputs(SmallVector &Filenames) { for (auto &OffloadFile : Binaries) { if (ContainsBitcode(OffloadFile)) BitcodeFiles.emplace_back(std::move(OffloadFile)); + else + return createStringError("Unsupported file type"); } } return BitcodeFiles; @@ -544,6 +546,7 @@ int main(int Argc, char **Argv) { auto FilesOrErr = getBitcodeInputs(*FilenamesOrErr); if (!FilesOrErr) reportError(FilesOrErr.takeError()); + // Run SYCL linking process on the generated inputs. if (Error Err = runSYCLLink(*FilesOrErr, Args)) reportError(std::move(Err)); From e0d185955fa51673708cf71d679fdb1ca43aabeb Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Fri, 28 Mar 2025 08:48:43 -0700 Subject: [PATCH 05/16] Major refactoring to address review comments and also add back .bc support Signed-off-by: Arvind Sudarsanam --- clang/test/Driver/clang-sycl-linker-test.cpp | 37 ++- clang/test/Driver/sycl-link-spirv-target.cpp | 6 +- .../clang-sycl-linker/ClangSYCLLinker.cpp | 238 ++++++++---------- 3 files changed, 138 insertions(+), 143 deletions(-) diff --git a/clang/test/Driver/clang-sycl-linker-test.cpp b/clang/test/Driver/clang-sycl-linker-test.cpp index ea02d82c28518..6ed826e343920 100644 --- a/clang/test/Driver/clang-sycl-linker-test.cpp +++ b/clang/test/Driver/clang-sycl-linker-test.cpp @@ -1,35 +1,50 @@ // Tests the clang-sycl-linker tool. // -// Test a simple case without arguments. +// Test a simple case to link two fat objects. // RUN: %clangxx -c -target spirv64 %s -o %t_1.o // RUN: %clangxx -c -target spirv64 %s -o %t_2.o -// RUN: clang-sycl-linker --dry-run %t_1.o %t_2.o -o a.spv 2>&1 \ -// RUN: | FileCheck %s --check-prefix=SIMPLE -// SIMPLE: sycl-device-link: input: {{.*}}.o; {{.*}}.o; libfiles: output: [[LLVMLINKOUT:.*]].bc -// SIMPLE-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc +// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.o %t_2.o -o a.spv 2>&1 \ +// RUN: | FileCheck %s --check-prefix=SIMPLE-FO +// SIMPLE-FO: sycl-device-link: inputs: {{.*}}.o, {{.*}}.o libfiles: output: [[LLVMLINKOUT:.*]].bc +// SIMPLE-FO-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc +// +// Test a simple case to link two LLVM IR bitcodes. +// RUN: %clangxx -emit-llvm -c %s -o %t_1.bc +// RUN: %clangxx -emit-llvm -c %s -o %t_2.bc +// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ +// RUN: | FileCheck %s --check-prefix=SIMPLE-BC +// SIMPLE-BC: sycl-device-link: inputs: {{.*}}.bc, {{.*}}.bc libfiles: output: [[LLVMLINKOUT:.*]].bc +// SIMPLE-BC-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc // // Test a simple case with device library files specified. // RUN: touch %T/lib1.o // RUN: touch %T/lib2.o -// RUN: clang-sycl-linker --dry-run %t_1.o %t_2.o --library-path=%T --device-libs=lib1.o,lib2.o -o a.spv 2>&1 \ +// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.o %t_2.o --library-path=%T --device-libs=lib1.o,lib2.o -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBS -// DEVLIBS: sycl-device-link: input: {{.*}}.o; libfiles: {{.*}}lib1.o; {{.*}}lib2.o; output: [[LLVMLINKOUT:.*]].bc +// DEVLIBS: sycl-device-link: inputs: {{.*}}.o libfiles: {{.*}}lib1.o, {{.*}}lib2.o output: [[LLVMLINKOUT:.*]].bc // DEVLIBS-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc // +// Test a simple case with a random file as input. +// RUN: touch %t.x.o +// RUN: clang-offload-packager -o %t.o --image=file=%t.x.o,kind=openmp,triple=spirv64,arch= +// RUN: not clang-sycl-linker -triple spirv64 %t.o -o a.spv 2>&1 \ +// RUN: | FileCheck %s --check-prefix=FILETYPEERROR +// FILETYPEERROR: Unsupported file type +// // Test to see if device library related errors are emitted. -// RUN: not clang-sycl-linker --dry-run %t_1.o %t_2.o --library-path=%T --device-libs= -o a.spv 2>&1 \ +// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs= -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBSERR1 // DEVLIBSERR1: Number of device library files cannot be zero -// RUN: not clang-sycl-linker --dry-run %t_1.o %t_2.o --library-path=%T --device-libs=lib1.o,lib2.o,lib3.o -o a.spv 2>&1 \ +// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.o,lib2.o,lib3.o -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBSERR2 // DEVLIBSERR2: '{{.*}}lib3.o' SYCL device library file is not found // // Test if correct set of llvm-spirv options are emitted for windows environment. -// RUN: clang-sycl-linker --dry-run --is-windows-msvc-env %t_1.o %t_2.o -o a.spv 2>&1 \ +// RUN: clang-sycl-linker --dry-run -triple spirv64 --is-windows-msvc-env %t_1.bc %t_2.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=LLVMOPTSWIN // LLVMOPTSWIN: -spirv-debug-info-version=ocl-100 -spirv-allow-extra-diexpressions -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext= // // Test if correct set of llvm-spirv options are emitted for linux environment. -// RUN: clang-sycl-linker --dry-run %t_1.o %t_2.o -o a.spv 2>&1 \ +// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=LLVMOPTSLIN // LLVMOPTSLIN: -spirv-debug-info-version=nonsemantic-shader-200 -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext= diff --git a/clang/test/Driver/sycl-link-spirv-target.cpp b/clang/test/Driver/sycl-link-spirv-target.cpp index a1214a6c3954b..5ae8f09972250 100644 --- a/clang/test/Driver/sycl-link-spirv-target.cpp +++ b/clang/test/Driver/sycl-link-spirv-target.cpp @@ -2,8 +2,8 @@ // architecture. // // Test that -Xlinker options are being passed to clang-sycl-linker. -// RUN: touch %t.o +// RUN: touch %t.bc // RUN: %clangxx -### --target=spirv64 --sycl-link -Xlinker --llvm-spirv-path=/tmp \ -// RUN: -Xlinker --library-path=/tmp -Xlinker --device-libs=lib1.o,lib2.o %t.o 2>&1 \ +// RUN: -Xlinker --library-path=/tmp -Xlinker --device-libs=lib1.o,lib2.o %t.bc 2>&1 \ // RUN: | FileCheck %s -check-prefix=XLINKEROPTS -// XLINKEROPTS: "{{.*}}clang-sycl-linker{{.*}}" "--llvm-spirv-path=/tmp" "--library-path=/tmp" "--device-libs=lib1.o,lib2.o" "{{.*}}.o" "-o" "a.out" +// XLINKEROPTS: "{{.*}}clang-sycl-linker{{.*}}" "--llvm-spirv-path=/tmp" "--library-path=/tmp" "--device-libs=lib1.o,lib2.o" "{{.*}}.bc" "-o" "a.out" diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index 3854561da99b5..a64dd959ddb80 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -21,8 +21,6 @@ #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/DiagnosticPrinter.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" #include "llvm/LTO/LTO.h" #include "llvm/Linker/Linker.h" @@ -182,64 +180,59 @@ Error executeCommands(StringRef ExecutablePath, ArrayRef Args) { return Error::success(); } -/// Returns a new ArgList containg arguments used for the device linking phase. -DerivedArgList getLinkerArgs(ArrayRef Input, - const InputArgList &Args) { - DerivedArgList DAL = DerivedArgList(DerivedArgList(Args)); - for (Arg *A : Args) - DAL.append(A); - if (DryRun) - return DAL; - - // Set the subarchitecture and target triple for this compilation. - const OptTable &Tbl = getOptTable(); - DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_arch), - Args.MakeArgString(Input.front().getBinary()->getArch())); - DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_triple), - Args.MakeArgString(Input.front().getBinary()->getTriple())); - - return DAL; -} - -/// Collect all fat objects from user input. -Expected> getObjectsFromArgs(const ArgList &Args) { +Expected> getInput(const ArgList &Args) { + // Collect all input bitcode files to be passed to the device linking stage. SmallVector BitcodeFiles; for (const opt::Arg *Arg : Args.filtered(OPT_INPUT)) { std::optional Filename = std::string(Arg->getValue()); if (!Filename || !sys::fs::exists(*Filename) || sys::fs::is_directory(*Filename)) continue; - BitcodeFiles.emplace_back(*Filename); + file_magic Magic; + if (auto EC = identify_magic(*Filename, Magic)) + return createStringError("Failed to open file " + *Filename); + BitcodeFiles.push_back(*Filename); } return BitcodeFiles; } -/// Collect all input bitcode images from a list of fat objects. -Expected> -getBitcodeInputs(SmallVector &Filenames) { - // Collect all input bitcode files to be passed to llvm bitcode linking. - SmallVector BitcodeFiles; - for (auto Filename : Filenames) { - ErrorOr> BufferOrErr = - MemoryBuffer::getFile(Filename); - if (std::error_code EC = BufferOrErr.getError()) - return createFileError(Filename, EC); - MemoryBufferRef Buffer = **BufferOrErr; - SmallVector Binaries; - if (Error Err = extractOffloadBinaries(Buffer, Binaries)) - return std::move(Err); - - auto ContainsBitcode = [](const OffloadFile &F) { - return identify_magic(F.getBinary()->getImage()) == file_magic::bitcode; - }; - for (auto &OffloadFile : Binaries) { - if (ContainsBitcode(OffloadFile)) - BitcodeFiles.emplace_back(std::move(OffloadFile)); - else - return createStringError("Unsupported file type"); - } - } - return BitcodeFiles; +Expected> getBitcodeModule(StringRef File, + LLVMContext &C) { + SMDiagnostic Err; + + // Handle cases where input file is a LLVM IR bitcode file. + // When clang-sycl-linker is called via clang-linker-wrapper tool, input files + // are LLVM IR bitcode files + // TODO: Support SPIR-V IR files + auto M = getLazyIRFileModule(File, Err, C); + if (M) + return std::move(M); + + // Handle cases where input file is a fat object containing LLVM IR bitcode + // SYCL device libraries are provided as fat objects containing LLVM IR + // bitcode + ErrorOr> BufferOrErr = + MemoryBuffer::getFile(File); + if (std::error_code EC = BufferOrErr.getError()) + return createFileError(File, EC); + MemoryBufferRef Buffer = **BufferOrErr; + SmallVector Binaries; + if (Error Err = extractOffloadBinaries(Buffer, Binaries)) + return std::move(Err); + // Only one binary of bitcode image type is expected + assert((Binaries.size() == 1) && "Only one binary per file is expected"); + auto Bin = std::move(Binaries.front()); + // TODO: Current use case involves LLVM IR bitcode files as input. + // This will be extended to support objects and SPIR-V IR files. + auto TheImage = Bin.getBinary()->getImage(); + if (identify_magic(TheImage) != file_magic::bitcode) + return createStringError("Unsupported file type"); + // TODO: Try to replace this call with getLazyIRModule + M = parseIR(MemoryBufferRef(TheImage, ""), Err, C); + if (M) + return std::move(M); + + return createStringError("Unsupported file type"); } // This utility function is used to gather all SYCL device library files that @@ -273,83 +266,77 @@ Expected> getSYCLDeviceLibs(const ArgList &Args) { /// Following tasks are performed: /// 1. Link all SYCL device bitcode images into one image. Device linking is -/// performed using the linkModules API. +/// performed using the linkInModule API. /// 2. Gather all SYCL device library bitcode images. /// 3. Link all the images gathered in Step 2 with the output of Step 1 using -/// linkModules API. LinkOnlyNeeded flag is used. -Expected linkDeviceCode(ArrayRef BitcodeInputFiles, +/// linkInModule API. LinkOnlyNeeded flag is used. +Expected linkDeviceCode(ArrayRef InputFiles, const ArgList &Args, LLVMContext &C) { - llvm::TimeTraceScope TimeScope("SYCL Link Device Code"); - // Gather SYCL device library files. - auto SYCLDeviceLibFilenames = getSYCLDeviceLibs(Args); - if (!SYCLDeviceLibFilenames) - return SYCLDeviceLibFilenames.takeError(); - // Create a new file to write the linked device file to. + llvm::TimeTraceScope TimeScope("SYCL link device code"); + + assert(InputFiles.size() && "No inputs to link"); + + // Create a new file to write the linked device file to auto BitcodeOutput = createTempFile(Args, sys::path::filename(OutputFile), "bc"); if (!BitcodeOutput) return BitcodeOutput.takeError(); - int FD = -1; - if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeOutput, FD)) - return errorCodeToError(EC); - if (DryRun || Verbose) { - std::for_each((*SYCLDeviceLibFilenames).begin(), - (*SYCLDeviceLibFilenames).end(), - [](std::string &Filename) { Filename += "; "; }); - std::string LibInputs = - std::accumulate((*SYCLDeviceLibFilenames).begin(), - (*SYCLDeviceLibFilenames).end(), std::string("")); - - auto FilenamesOrErr = getObjectsFromArgs(Args); - if (!FilenamesOrErr) - reportError(FilenamesOrErr.takeError()); - std::for_each((*FilenamesOrErr).begin(), (*FilenamesOrErr).end(), - [](std::string &Filename) { Filename += "; "; }); - std::string Inputs = std::accumulate( - (*FilenamesOrErr).begin(), (*FilenamesOrErr).end(), std::string("")); + // Get all SYCL device library files, if any + auto SYCLDeviceLibFiles = getSYCLDeviceLibs(Args); + if (!SYCLDeviceLibFiles) + return SYCLDeviceLibFiles.takeError(); + + if (DryRun || Verbose) { + std::string Inputs = + std::accumulate(std::next(InputFiles.begin()), InputFiles.end(), + InputFiles.front(), [](std::string a, std::string b) { + return std::move(a) + ", " + std::move(b); + }); + std::string LibInputs = ""; + if (!(*SYCLDeviceLibFiles).empty()) + LibInputs = std::accumulate( + std::next((*SYCLDeviceLibFiles).begin()), (*SYCLDeviceLibFiles).end(), + (*SYCLDeviceLibFiles).front(), [](std::string a, std::string b) { + return std::move(a) + ", " + std::move(b); + }); errs() << formatv( - "sycl-device-link: input: {0} libfiles: {1} output: {2}\n", Inputs, + "sycl-device-link: inputs: {0} libfiles: {1} output: {2}\n", Inputs, LibInputs, *BitcodeOutput); if (DryRun) return *BitcodeOutput; } - SMDiagnostic Err; - assert(BitcodeInputFiles.size() && "No inputs to link"); - auto Output = std::make_unique("sycl-device-link", C); - Linker L(*Output); + + auto LinkerOutput = std::make_unique("sycl-device-link", C); + Linker L(*LinkerOutput); // Link SYCL device input files. - for (auto &File : BitcodeInputFiles) { - auto TheImage = File.getBinary()->getImage(); - std::unique_ptr ModOrErr = - parseIR(MemoryBufferRef(TheImage, ""), Err, C); - if (!ModOrErr || verifyModule(*ModOrErr, &errs())) - return createStringError("Could not parse IR"); - if (L.linkInModule(std::move(ModOrErr))) + for (auto &File : InputFiles) { + auto ModOrErr = getBitcodeModule(File, C); + if (!ModOrErr) + return ModOrErr.takeError(); + if (L.linkInModule(std::move(*ModOrErr))) return createStringError("Could not link IR"); } - auto SYCLDeviceLibFiles = getBitcodeInputs(*SYCLDeviceLibFilenames); - if (!SYCLDeviceLibFiles) - return SYCLDeviceLibFiles.takeError(); - if (!(*SYCLDeviceLibFiles).empty()) { - const llvm::Triple Triple(Args.getLastArgValue(OPT_triple)); - // Link in SYCL device library files. - for (auto &File : *SYCLDeviceLibFiles) { - auto TheImage = File.getBinary()->getImage(); - std::unique_ptr M = - parseIR(MemoryBufferRef(TheImage, ""), Err, C); - if (!M || verifyModule(*M, &errs())) - return createStringError("Could not parse IR in SYCL device lib file"); - if (M->getTargetTriple() == Triple) { - unsigned Flags = Linker::Flags::None; - Flags |= Linker::Flags::LinkOnlyNeeded; - if (L.linkInModule(std::move(M), Flags)) - return createStringError("Could not link IR"); - } + + // Link in SYCL device library files. + const llvm::Triple Triple(Args.getLastArgValue(OPT_triple)); + for (auto &File : *SYCLDeviceLibFiles) { + auto LibMod = getBitcodeModule(File, C); + if (!LibMod) + return LibMod.takeError(); + if ((*LibMod)->getTargetTriple() == Triple) { + unsigned Flags = Linker::Flags::None; + if (L.linkInModule(std::move(*LibMod), Flags)) + return createStringError("Could not link IR"); } } + + // Write the final output into 'BitcodeOutput' file + int FD = -1; + if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeOutput, FD)) + return errorCodeToError(EC); llvm::raw_fd_ostream OS(FD, true); - WriteBitcodeToFile(*Output, OS); + WriteBitcodeToFile(*LinkerOutput, OS); return *BitcodeOutput; } @@ -394,6 +381,7 @@ static void getSPIRVTransOpts(const ArgList &Args, ",+SPV_INTEL_arbitrary_precision_fixed_point" ",+SPV_INTEL_arbitrary_precision_floating_point" ",+SPV_INTEL_variable_length_array,+SPV_INTEL_fp_fast_math_mode" + // ",+SPV_INTEL_long_constant_composite" ",+SPV_INTEL_arithmetic_fence" ",+SPV_INTEL_global_variable_decorations" ",+SPV_INTEL_cache_controls" @@ -470,39 +458,34 @@ static Expected runLLVMToSPIRVTranslation(StringRef File, return OutputFile; } -Error runSYCLLink(ArrayRef Files, const InputArgList &Args) { +Error runSYCLLink(ArrayRef Files, const ArgList &Args) { llvm::TimeTraceScope TimeScope("SYCLDeviceLink"); - LLVMContext C; - // Update args list with triple and arch information for SPIR-V device - auto LinkerArgs = getLinkerArgs(Files, Args); + // Link all input bitcode files and SYCL device library files, if any + auto LinkedFile = linkDeviceCode(Files, Args, C); + if (!LinkedFile) + reportError(LinkedFile.takeError()); - // Linking device bitcode files - auto LinkedFileOrErr = linkDeviceCode(Files, LinkerArgs, C); - if (!LinkedFileOrErr) - reportError(LinkedFileOrErr.takeError()); - - // LLVM to SPIR-V translation - auto SPVFile = runLLVMToSPIRVTranslation(*LinkedFileOrErr, LinkerArgs); + // LLVM to SPIR-V translation step + auto SPVFile = runLLVMToSPIRVTranslation(*LinkedFile, Args); if (!SPVFile) return SPVFile.takeError(); - return Error::success(); } } // namespace -int main(int Argc, char **Argv) { - InitLLVM X(Argc, Argv); +int main(int argc, char **argv) { + InitLLVM X(argc, argv); - Executable = Argv[0]; - sys::PrintStackTraceOnErrorSignal(Argv[0]); + Executable = argv[0]; + sys::PrintStackTraceOnErrorSignal(argv[0]); const OptTable &Tbl = getOptTable(); BumpPtrAllocator Alloc; StringSaver Saver(Alloc); - auto Args = Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, [&](StringRef Err) { + auto Args = Tbl.parseArgs(argc, argv, OPT_INVALID, Saver, [&](StringRef Err) { reportError(createStringError(inconvertibleErrorCode(), Err)); }); @@ -540,10 +523,7 @@ int main(int Argc, char **Argv) { } // Get the input files to pass to the linking stage. - auto FilenamesOrErr = getObjectsFromArgs(Args); - if (!FilenamesOrErr) - reportError(FilenamesOrErr.takeError()); - auto FilesOrErr = getBitcodeInputs(*FilenamesOrErr); + auto FilesOrErr = getInput(Args); if (!FilesOrErr) reportError(FilesOrErr.takeError()); From c1611f0ce17837fef61efe664344813ee07a1fed Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Fri, 28 Mar 2025 09:15:37 -0700 Subject: [PATCH 06/16] Minor changes Signed-off-by: Arvind Sudarsanam --- clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index a64dd959ddb80..78e53eca008c0 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -271,8 +271,9 @@ Expected> getSYCLDeviceLibs(const ArgList &Args) { /// 3. Link all the images gathered in Step 2 with the output of Step 1 using /// linkInModule API. LinkOnlyNeeded flag is used. Expected linkDeviceCode(ArrayRef InputFiles, - const ArgList &Args, LLVMContext &C) { + const ArgList &Args) { llvm::TimeTraceScope TimeScope("SYCL link device code"); + LLVMContext C; assert(InputFiles.size() && "No inputs to link"); @@ -381,7 +382,7 @@ static void getSPIRVTransOpts(const ArgList &Args, ",+SPV_INTEL_arbitrary_precision_fixed_point" ",+SPV_INTEL_arbitrary_precision_floating_point" ",+SPV_INTEL_variable_length_array,+SPV_INTEL_fp_fast_math_mode" - // ",+SPV_INTEL_long_constant_composite" + ",+SPV_INTEL_long_composites" ",+SPV_INTEL_arithmetic_fence" ",+SPV_INTEL_global_variable_decorations" ",+SPV_INTEL_cache_controls" @@ -460,10 +461,9 @@ static Expected runLLVMToSPIRVTranslation(StringRef File, Error runSYCLLink(ArrayRef Files, const ArgList &Args) { llvm::TimeTraceScope TimeScope("SYCLDeviceLink"); - LLVMContext C; // Link all input bitcode files and SYCL device library files, if any - auto LinkedFile = linkDeviceCode(Files, Args, C); + auto LinkedFile = linkDeviceCode(Files, Args); if (!LinkedFile) reportError(LinkedFile.takeError()); From 596488cdf625d2f2a84c381f0ddc4bc0968f73c0 Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Fri, 28 Mar 2025 10:42:23 -0700 Subject: [PATCH 07/16] Modify testcase and update CMakeLists.txt to make sure shared build works Signed-off-by: Arvind Sudarsanam --- clang/test/Driver/clang-sycl-linker-test.cpp | 16 ++++------------ clang/tools/clang-sycl-linker/CMakeLists.txt | 4 ++++ .../tools/clang-sycl-linker/ClangSYCLLinker.cpp | 1 + 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/clang/test/Driver/clang-sycl-linker-test.cpp b/clang/test/Driver/clang-sycl-linker-test.cpp index 6ed826e343920..58e4fcf8ee860 100644 --- a/clang/test/Driver/clang-sycl-linker-test.cpp +++ b/clang/test/Driver/clang-sycl-linker-test.cpp @@ -1,21 +1,13 @@ // Tests the clang-sycl-linker tool. // -// Test a simple case to link two fat objects. -// RUN: %clangxx -c -target spirv64 %s -o %t_1.o -// RUN: %clangxx -c -target spirv64 %s -o %t_2.o +// Test a simple case to link two input files. +// RUN: touch %t_1.o +// RUN: touch %t_2.o // RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.o %t_2.o -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=SIMPLE-FO // SIMPLE-FO: sycl-device-link: inputs: {{.*}}.o, {{.*}}.o libfiles: output: [[LLVMLINKOUT:.*]].bc // SIMPLE-FO-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc // -// Test a simple case to link two LLVM IR bitcodes. -// RUN: %clangxx -emit-llvm -c %s -o %t_1.bc -// RUN: %clangxx -emit-llvm -c %s -o %t_2.bc -// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ -// RUN: | FileCheck %s --check-prefix=SIMPLE-BC -// SIMPLE-BC: sycl-device-link: inputs: {{.*}}.bc, {{.*}}.bc libfiles: output: [[LLVMLINKOUT:.*]].bc -// SIMPLE-BC-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc -// // Test a simple case with device library files specified. // RUN: touch %T/lib1.o // RUN: touch %T/lib2.o @@ -24,7 +16,7 @@ // DEVLIBS: sycl-device-link: inputs: {{.*}}.o libfiles: {{.*}}lib1.o, {{.*}}lib2.o output: [[LLVMLINKOUT:.*]].bc // DEVLIBS-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc // -// Test a simple case with a random file as input. +// Test a simple case with a random file (not bitcode) as input. // RUN: touch %t.x.o // RUN: clang-offload-packager -o %t.o --image=file=%t.x.o,kind=openmp,triple=spirv64,arch= // RUN: not clang-sycl-linker -triple spirv64 %t.o -o a.spv 2>&1 \ diff --git a/clang/tools/clang-sycl-linker/CMakeLists.txt b/clang/tools/clang-sycl-linker/CMakeLists.txt index 5665ad7d7186e..382c0ca441940 100644 --- a/clang/tools/clang-sycl-linker/CMakeLists.txt +++ b/clang/tools/clang-sycl-linker/CMakeLists.txt @@ -1,6 +1,10 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} BinaryFormat + BitWriter + Core + IRReader + Linker Option Object TargetParser diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index 78e53eca008c0..a2bf30d90c80d 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -21,6 +21,7 @@ #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/CodeGen/CommandFlags.h" #include "llvm/IR/DiagnosticPrinter.h" +#include "llvm/IR/LLVMContext.h" #include "llvm/IRReader/IRReader.h" #include "llvm/LTO/LTO.h" #include "llvm/Linker/Linker.h" From 454c38614b0e8042620eb11c19e4e595d5fb3580 Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Fri, 28 Mar 2025 16:15:12 -0700 Subject: [PATCH 08/16] Adding more tests and remove support for non-bitcode inputs Signed-off-by: Arvind Sudarsanam --- clang/test/Driver/Inputs/libsycl.ll | 43 +++++++++++++++++ clang/test/Driver/Inputs/test1.ll | 35 ++++++++++++++ clang/test/Driver/Inputs/test2.ll | 29 +++++++++++ clang/test/Driver/Inputs/test3.ll | 30 ++++++++++++ clang/test/Driver/clang-sycl-linker-test.cpp | 31 ++++++------ clang/test/Driver/link-device-code.test | 22 +++++++++ .../clang-sycl-linker/ClangSYCLLinker.cpp | 48 ++++++------------- clang/tools/clang-sycl-linker/SYCLLinkOpts.td | 8 +++- 8 files changed, 194 insertions(+), 52 deletions(-) create mode 100644 clang/test/Driver/Inputs/libsycl.ll create mode 100644 clang/test/Driver/Inputs/test1.ll create mode 100644 clang/test/Driver/Inputs/test2.ll create mode 100644 clang/test/Driver/Inputs/test3.ll create mode 100644 clang/test/Driver/link-device-code.test diff --git a/clang/test/Driver/Inputs/libsycl.ll b/clang/test/Driver/Inputs/libsycl.ll new file mode 100644 index 0000000000000..fca801b5682ec --- /dev/null +++ b/clang/test/Driver/Inputs/libsycl.ll @@ -0,0 +1,43 @@ +; ModuleID = 'libsycl.cpp' +source_filename = "libsycl.cpp" +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1" +target triple = "spirv64" + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) +define spir_func noundef range(i32 -2147483643, -2147483648) i32 @_Z9lib_func1i(i32 noundef %a) local_unnamed_addr #0 { +entry: + %add = add nsw i32 %a, 5 + ret i32 %add +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) +define spir_func noundef i32 @_Z9lib_func2i(i32 noundef %a) local_unnamed_addr #0 { +entry: + %mul = mul nsw i32 %a, 5 + ret i32 %mul +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) +define spir_func noundef range(i32 -2147483648, 2147483643) i32 @_Z9lib_func3i(i32 noundef %a) local_unnamed_addr #0 { +entry: + %sub = add nsw i32 %a, -5 + ret i32 %sub +} + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) +define spir_func noundef i32 @_Z9lib_func4i(i32 noundef %a) local_unnamed_addr #0 { +entry: + %call = tail call spir_func noundef i32 @_Z9lib_func1i(i32 noundef %a) + %call1 = tail call spir_func noundef i32 @_Z9lib_func2i(i32 noundef %a) + %mul = mul nsw i32 %call1, %call + ret i32 %mul +} + +attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"frame-pointer", i32 2} +!2 = !{!"clang version 21.0.0git (https://github.com/asudarsa/llvm-project.git 596488cdf625d2f2a84c381f0ddc4bc0968f73c0)"} diff --git a/clang/test/Driver/Inputs/test1.ll b/clang/test/Driver/Inputs/test1.ll new file mode 100644 index 0000000000000..565a94efe0302 --- /dev/null +++ b/clang/test/Driver/Inputs/test1.ll @@ -0,0 +1,35 @@ +; ModuleID = 'test1.cpp' +source_filename = "test1.cpp" +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1" +target triple = "spirv64" + +; Function Attrs: mustprogress noinline +define spir_func noundef i32 @_Z9foo_func1ii(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 { +entry: + %call = tail call spir_func noundef i32 @_Z9lib_func4i(i32 noundef %b) + %call1 = tail call spir_func noundef i32 @_Z9bar_func1ii(i32 noundef %a, i32 noundef %call) + ret i32 %call1 +} + +declare spir_func noundef i32 @_Z9bar_func1ii(i32 noundef, i32 noundef) local_unnamed_addr #1 + +declare spir_func noundef i32 @_Z9lib_func4i(i32 noundef) local_unnamed_addr #1 + +; Function Attrs: mustprogress +define spir_func noundef i32 @_Z9foo_func2iii(i32 noundef %c, i32 noundef %d, i32 noundef %e) local_unnamed_addr #2 { +entry: + %call = tail call spir_func noundef i32 @_Z9foo_func1ii(i32 noundef %c, i32 noundef %d) + %mul = mul nsw i32 %call, %e + ret i32 %mul +} + +attributes #0 = { mustprogress noinline "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #2 = { mustprogress "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"frame-pointer", i32 2} +!2 = !{!"clang version 21.0.0git (https://github.com/asudarsa/llvm-project.git 596488cdf625d2f2a84c381f0ddc4bc0968f73c0)"} diff --git a/clang/test/Driver/Inputs/test2.ll b/clang/test/Driver/Inputs/test2.ll new file mode 100644 index 0000000000000..c59da3f33f3e3 --- /dev/null +++ b/clang/test/Driver/Inputs/test2.ll @@ -0,0 +1,29 @@ +; ModuleID = 'test2.cpp' +source_filename = "test2.cpp" +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1" +target triple = "spirv64" + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) +define spir_func noundef i32 @_Z9bar_func1ii(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 { +entry: + %add = add nsw i32 %b, %a + ret i32 %add +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define spir_func noundef i32 @_Z9bar_func2iii(i32 noundef %c, i32 noundef %d, i32 noundef %e) local_unnamed_addr #1 { +entry: + %call = tail call spir_func noundef i32 @_Z9bar_func1ii(i32 noundef %c, i32 noundef %d) + %add = add nsw i32 %call, %e + ret i32 %add +} + +attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"frame-pointer", i32 2} +!2 = !{!"clang version 21.0.0git (https://github.com/asudarsa/llvm-project.git 596488cdf625d2f2a84c381f0ddc4bc0968f73c0)"} diff --git a/clang/test/Driver/Inputs/test3.ll b/clang/test/Driver/Inputs/test3.ll new file mode 100644 index 0000000000000..bc39faaf4c0e4 --- /dev/null +++ b/clang/test/Driver/Inputs/test3.ll @@ -0,0 +1,30 @@ +; ModuleID = 'test3.cpp' +source_filename = "test3.cpp" +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1" +target triple = "spirv64" + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) +define spir_func noundef i32 @_Z9bar_func1ii(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 { +entry: + %mul = shl nsw i32 %a, 1 + %add = add nsw i32 %mul, %b + ret i32 %add +} + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define spir_func noundef i32 @_Z9baz_func1i(i32 noundef %a) local_unnamed_addr #1 { +entry: + %add = add nsw i32 %a, 5 + %call = tail call spir_func noundef i32 @_Z9bar_func1ii(i32 noundef %a, i32 noundef %add) + ret i32 %call +} + +attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"frame-pointer", i32 2} +!2 = !{!"clang version 21.0.0git (https://github.com/asudarsa/llvm-project.git 596488cdf625d2f2a84c381f0ddc4bc0968f73c0)"} diff --git a/clang/test/Driver/clang-sycl-linker-test.cpp b/clang/test/Driver/clang-sycl-linker-test.cpp index 58e4fcf8ee860..fbbe298a4733c 100644 --- a/clang/test/Driver/clang-sycl-linker-test.cpp +++ b/clang/test/Driver/clang-sycl-linker-test.cpp @@ -1,24 +1,23 @@ // Tests the clang-sycl-linker tool. // -// Test a simple case to link two input files. -// RUN: touch %t_1.o -// RUN: touch %t_2.o -// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.o %t_2.o -o a.spv 2>&1 \ +// Test the dry run of a simple case to link two input files. +// RUN: touch %t_1.bc +// RUN: touch %t_2.bc +// RUN: clang-sycl-linker --dry-run -v -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=SIMPLE-FO -// SIMPLE-FO: sycl-device-link: inputs: {{.*}}.o, {{.*}}.o libfiles: output: [[LLVMLINKOUT:.*]].bc +// SIMPLE-FO: sycl-device-link: inputs: {{.*}}.bc, {{.*}}.bc libfiles: output: [[LLVMLINKOUT:.*]].bc // SIMPLE-FO-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc // -// Test a simple case with device library files specified. -// RUN: touch %T/lib1.o -// RUN: touch %T/lib2.o -// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.o %t_2.o --library-path=%T --device-libs=lib1.o,lib2.o -o a.spv 2>&1 \ +// Test the dry run of a simple case with device library files specified. +// RUN: touch %T/lib1.bc +// RUN: touch %T/lib2.bc +// RUN: clang-sycl-linker --dry-run -v -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.bc,lib2.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBS -// DEVLIBS: sycl-device-link: inputs: {{.*}}.o libfiles: {{.*}}lib1.o, {{.*}}lib2.o output: [[LLVMLINKOUT:.*]].bc +// DEVLIBS: sycl-device-link: inputs: {{.*}}.bc libfiles: {{.*}}lib1.bc, {{.*}}lib2.bc output: [[LLVMLINKOUT:.*]].bc // DEVLIBS-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc // // Test a simple case with a random file (not bitcode) as input. -// RUN: touch %t.x.o -// RUN: clang-offload-packager -o %t.o --image=file=%t.x.o,kind=openmp,triple=spirv64,arch= +// RUN: touch %t.o // RUN: not clang-sycl-linker -triple spirv64 %t.o -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=FILETYPEERROR // FILETYPEERROR: Unsupported file type @@ -27,16 +26,16 @@ // RUN: not clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs= -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBSERR1 // DEVLIBSERR1: Number of device library files cannot be zero -// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.o,lib2.o,lib3.o -o a.spv 2>&1 \ +// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.bc,lib2.bc,lib3.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBSERR2 -// DEVLIBSERR2: '{{.*}}lib3.o' SYCL device library file is not found +// DEVLIBSERR2: '{{.*}}lib3.bc' SYCL device library file is not found // // Test if correct set of llvm-spirv options are emitted for windows environment. -// RUN: clang-sycl-linker --dry-run -triple spirv64 --is-windows-msvc-env %t_1.bc %t_2.bc -o a.spv 2>&1 \ +// RUN: clang-sycl-linker --dry-run -v -triple spirv64 --is-windows-msvc-env %t_1.bc %t_2.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=LLVMOPTSWIN // LLVMOPTSWIN: -spirv-debug-info-version=ocl-100 -spirv-allow-extra-diexpressions -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext= // // Test if correct set of llvm-spirv options are emitted for linux environment. -// RUN: clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ +// RUN: clang-sycl-linker --dry-run -v -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=LLVMOPTSLIN // LLVMOPTSLIN: -spirv-debug-info-version=nonsemantic-shader-200 -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext= diff --git a/clang/test/Driver/link-device-code.test b/clang/test/Driver/link-device-code.test new file mode 100644 index 0000000000000..028dc1e24c76f --- /dev/null +++ b/clang/test/Driver/link-device-code.test @@ -0,0 +1,22 @@ +# RUN: llvm-as %S/Inputs/test1.ll -o %t.test1.bc +# RUN: llvm-as %S/Inputs/test2.ll -o %t.test2.bc +# RUN: llvm-as %S/Inputs/test3.ll -o %t.test3.bc +# RUN: llvm-as %S/Inputs/libsycl.ll -o %t.libsycl.bc +# RUN: clang-sycl-linker %t.test1.bc %t.test2.bc -triple spirv64 -o test.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-SIMPLE + +# RUN: not clang-sycl-linker %t.test2.bc %t.test3.bc -triple spirv64 -o test.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-MULTIPLE-DEFS + +# RUN: clang-sycl-linker %t.test1.bc %t.test2.bc -device-libs=%t.libsycl.bc -library-path= -triple spirv64 -o test.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-DEVICE-LIB + +; CHECK-SIMPLE: define {{.*}}foo_func1{{.*}} +; CHECK-SIMPLE: define {{.*}}foo_func2{{.*}} +; CHECK-SIMPLE: define {{.*}}bar_func1{{.*}} +; CHECK-SIMPLE: define {{.*}}bar_func2{{.*}} + +;CHECK-MULTIPLE-DEFS: error: Linking globals named {{.*}}bar_func1{{.*}} symbol multiply defined! + +; CHECK-DEVICE-LIB: define {{.*}}foo_func1{{.*}} +; CHECK-DEVICE-LIB: define {{.*}}foo_func2{{.*}} +; CHECK-DEVICE-LIB: define {{.*}}bar_func1{{.*}} +; CHECK-DEVICE-LIB: define {{.*}}bar_func2{{.*}} + diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index a2bf30d90c80d..e534dfe48f128 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -192,48 +192,27 @@ Expected> getInput(const ArgList &Args) { file_magic Magic; if (auto EC = identify_magic(*Filename, Magic)) return createStringError("Failed to open file " + *Filename); + // TODO: Current use case involves LLVM IR bitcode files as input. + // This will be extended to support SPIR-V IR files. + if (Magic != file_magic::bitcode) + return createStringError("Unsupported file type"); BitcodeFiles.push_back(*Filename); } return BitcodeFiles; } +/// Handle cases where input file is a LLVM IR bitcode file +/// When clang-sycl-linker is called via clang-linker-wrapper tool, input files +/// are LLVM IR bitcode files +// TODO: Support SPIR-V IR files Expected> getBitcodeModule(StringRef File, LLVMContext &C) { SMDiagnostic Err; - // Handle cases where input file is a LLVM IR bitcode file. - // When clang-sycl-linker is called via clang-linker-wrapper tool, input files - // are LLVM IR bitcode files - // TODO: Support SPIR-V IR files auto M = getLazyIRFileModule(File, Err, C); if (M) return std::move(M); - - // Handle cases where input file is a fat object containing LLVM IR bitcode - // SYCL device libraries are provided as fat objects containing LLVM IR - // bitcode - ErrorOr> BufferOrErr = - MemoryBuffer::getFile(File); - if (std::error_code EC = BufferOrErr.getError()) - return createFileError(File, EC); - MemoryBufferRef Buffer = **BufferOrErr; - SmallVector Binaries; - if (Error Err = extractOffloadBinaries(Buffer, Binaries)) - return std::move(Err); - // Only one binary of bitcode image type is expected - assert((Binaries.size() == 1) && "Only one binary per file is expected"); - auto Bin = std::move(Binaries.front()); - // TODO: Current use case involves LLVM IR bitcode files as input. - // This will be extended to support objects and SPIR-V IR files. - auto TheImage = Bin.getBinary()->getImage(); - if (identify_magic(TheImage) != file_magic::bitcode) - return createStringError("Unsupported file type"); - // TODO: Try to replace this call with getLazyIRModule - M = parseIR(MemoryBufferRef(TheImage, ""), Err, C); - if (M) - return std::move(M); - - return createStringError("Unsupported file type"); + return createStringError("Unable to parse file"); } // This utility function is used to gather all SYCL device library files that @@ -289,7 +268,7 @@ Expected linkDeviceCode(ArrayRef InputFiles, if (!SYCLDeviceLibFiles) return SYCLDeviceLibFiles.takeError(); - if (DryRun || Verbose) { + if (Verbose) { std::string Inputs = std::accumulate(std::next(InputFiles.begin()), InputFiles.end(), InputFiles.front(), [](std::string a, std::string b) { @@ -305,8 +284,6 @@ Expected linkDeviceCode(ArrayRef InputFiles, errs() << formatv( "sycl-device-link: inputs: {0} libfiles: {1} output: {2}\n", Inputs, LibInputs, *BitcodeOutput); - if (DryRun) - return *BitcodeOutput; } auto LinkerOutput = std::make_unique("sycl-device-link", C); @@ -327,12 +304,15 @@ Expected linkDeviceCode(ArrayRef InputFiles, if (!LibMod) return LibMod.takeError(); if ((*LibMod)->getTargetTriple() == Triple) { - unsigned Flags = Linker::Flags::None; + unsigned Flags = Linker::Flags::LinkOnlyNeeded; if (L.linkInModule(std::move(*LibMod), Flags)) return createStringError("Could not link IR"); } } + if (Args.hasArg(OPT_print_linked_module)) + errs() << *LinkerOutput; + // Write the final output into 'BitcodeOutput' file int FD = -1; if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeOutput, FD)) diff --git a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td index 959fd6c3e867c..6727af7d1ec63 100644 --- a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td +++ b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td @@ -24,8 +24,8 @@ def device_libs_EQ : CommaJoined<["--", "-"], "device-libs=">, Flags<[LinkerOnlyOption]>, HelpText<"A comma separated list of device libraries that are linked during the device link.">; -def triple : Joined<["--"], "triple">, - HelpText<"The device target triple">; +def triple : Separate<["--", "-"], "triple">, + HelpText<"The device target triple">; def arch : Separate<["--", "-"], "arch">, HelpText<"Specify the name of the target architecture.">; @@ -50,3 +50,7 @@ def llvm_spirv_path_EQ : Joined<["--"], "llvm-spirv-path=">, def llvm_spirv_options_EQ : Joined<["--", "-"], "llvm-spirv-options=">, Flags<[LinkerOnlyOption]>, HelpText<"Options that will control llvm-spirv step">; + +def print_linked_module : Flag<["--"], "print-linked-module">, + Flags<[LinkerOnlyOption]>, + HelpText<"Print the linked module's IR for testing">; From f4cc3cf727606809ea22f7989a94f2f6e617865e Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Fri, 28 Mar 2025 16:25:25 -0700 Subject: [PATCH 09/16] Pruned some tests Signed-off-by: Arvind Sudarsanam --- clang/test/Driver/Inputs/libsycl.ll | 7 ------- clang/test/Driver/Inputs/test1.ll | 7 ------- clang/test/Driver/Inputs/test2.ll | 7 ------- clang/test/Driver/Inputs/test3.ll | 7 ------- 4 files changed, 28 deletions(-) diff --git a/clang/test/Driver/Inputs/libsycl.ll b/clang/test/Driver/Inputs/libsycl.ll index fca801b5682ec..526d9ed1a8797 100644 --- a/clang/test/Driver/Inputs/libsycl.ll +++ b/clang/test/Driver/Inputs/libsycl.ll @@ -34,10 +34,3 @@ entry: } attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } - -!llvm.module.flags = !{!0, !1} -!llvm.ident = !{!2} - -!0 = !{i32 1, !"wchar_size", i32 4} -!1 = !{i32 7, !"frame-pointer", i32 2} -!2 = !{!"clang version 21.0.0git (https://github.com/asudarsa/llvm-project.git 596488cdf625d2f2a84c381f0ddc4bc0968f73c0)"} diff --git a/clang/test/Driver/Inputs/test1.ll b/clang/test/Driver/Inputs/test1.ll index 565a94efe0302..f434b3b60098f 100644 --- a/clang/test/Driver/Inputs/test1.ll +++ b/clang/test/Driver/Inputs/test1.ll @@ -26,10 +26,3 @@ entry: attributes #0 = { mustprogress noinline "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } attributes #2 = { mustprogress "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } - -!llvm.module.flags = !{!0, !1} -!llvm.ident = !{!2} - -!0 = !{i32 1, !"wchar_size", i32 4} -!1 = !{i32 7, !"frame-pointer", i32 2} -!2 = !{!"clang version 21.0.0git (https://github.com/asudarsa/llvm-project.git 596488cdf625d2f2a84c381f0ddc4bc0968f73c0)"} diff --git a/clang/test/Driver/Inputs/test2.ll b/clang/test/Driver/Inputs/test2.ll index c59da3f33f3e3..3e963e5adbbd4 100644 --- a/clang/test/Driver/Inputs/test2.ll +++ b/clang/test/Driver/Inputs/test2.ll @@ -20,10 +20,3 @@ entry: attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } - -!llvm.module.flags = !{!0, !1} -!llvm.ident = !{!2} - -!0 = !{i32 1, !"wchar_size", i32 4} -!1 = !{i32 7, !"frame-pointer", i32 2} -!2 = !{!"clang version 21.0.0git (https://github.com/asudarsa/llvm-project.git 596488cdf625d2f2a84c381f0ddc4bc0968f73c0)"} diff --git a/clang/test/Driver/Inputs/test3.ll b/clang/test/Driver/Inputs/test3.ll index bc39faaf4c0e4..8ee44e51d1855 100644 --- a/clang/test/Driver/Inputs/test3.ll +++ b/clang/test/Driver/Inputs/test3.ll @@ -21,10 +21,3 @@ entry: attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } - -!llvm.module.flags = !{!0, !1} -!llvm.ident = !{!2} - -!0 = !{i32 1, !"wchar_size", i32 4} -!1 = !{i32 7, !"frame-pointer", i32 2} -!2 = !{!"clang version 21.0.0git (https://github.com/asudarsa/llvm-project.git 596488cdf625d2f2a84c381f0ddc4bc0968f73c0)"} From d76a3a1fba36cd8cc251868fb8149b8f8d38a321 Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Fri, 28 Mar 2025 16:29:48 -0700 Subject: [PATCH 10/16] Minor fix Signed-off-by: Arvind Sudarsanam --- clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index e534dfe48f128..61d4a9caaad00 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -253,7 +253,6 @@ Expected> getSYCLDeviceLibs(const ArgList &Args) { Expected linkDeviceCode(ArrayRef InputFiles, const ArgList &Args) { llvm::TimeTraceScope TimeScope("SYCL link device code"); - LLVMContext C; assert(InputFiles.size() && "No inputs to link"); @@ -286,6 +285,7 @@ Expected linkDeviceCode(ArrayRef InputFiles, LibInputs, *BitcodeOutput); } + LLVMContext C; auto LinkerOutput = std::make_unique("sycl-device-link", C); Linker L(*LinkerOutput); // Link SYCL device input files. From 7249cb86f3aa9fb4ecb5841fbbca1c5567355e70 Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Fri, 28 Mar 2025 16:33:44 -0700 Subject: [PATCH 11/16] Minor fix Signed-off-by: Arvind Sudarsanam --- clang/test/Driver/sycl-link-spirv-target.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/Driver/sycl-link-spirv-target.cpp b/clang/test/Driver/sycl-link-spirv-target.cpp index 5ae8f09972250..85566c67ea92b 100644 --- a/clang/test/Driver/sycl-link-spirv-target.cpp +++ b/clang/test/Driver/sycl-link-spirv-target.cpp @@ -4,6 +4,6 @@ // Test that -Xlinker options are being passed to clang-sycl-linker. // RUN: touch %t.bc // RUN: %clangxx -### --target=spirv64 --sycl-link -Xlinker --llvm-spirv-path=/tmp \ -// RUN: -Xlinker --library-path=/tmp -Xlinker --device-libs=lib1.o,lib2.o %t.bc 2>&1 \ +// RUN: -Xlinker --library-path=/tmp -Xlinker --device-libs=lib1.bc,lib2.bc %t.bc 2>&1 \ // RUN: | FileCheck %s -check-prefix=XLINKEROPTS -// XLINKEROPTS: "{{.*}}clang-sycl-linker{{.*}}" "--llvm-spirv-path=/tmp" "--library-path=/tmp" "--device-libs=lib1.o,lib2.o" "{{.*}}.bc" "-o" "a.out" +// XLINKEROPTS: "{{.*}}clang-sycl-linker{{.*}}" "--llvm-spirv-path=/tmp" "--library-path=/tmp" "--device-libs=lib1.bc,lib2.bc" "{{.*}}.bc" "-o" "a.out" From 59c0bcdc1c9e1e39ea7b68ede5b6bb6c31c6a7f2 Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Sat, 29 Mar 2025 10:20:10 -0700 Subject: [PATCH 12/16] Simplify tests and update comments Signed-off-by: Arvind Sudarsanam --- a.spv | Bin 0 -> 1028 bytes clang/test/Driver/Inputs/SYCL/bar.ll | 7 ++ clang/test/Driver/Inputs/SYCL/baz.ll | 15 ++++ clang/test/Driver/Inputs/SYCL/foo.ll | 19 ++++ clang/test/Driver/Inputs/SYCL/libsycl.ll | 13 +++ clang/test/Driver/Inputs/libsycl.ll | 36 -------- clang/test/Driver/Inputs/test1.ll | 28 ------ clang/test/Driver/Inputs/test2.ll | 22 ----- clang/test/Driver/Inputs/test3.ll | 23 ----- clang/test/Driver/link-device-code.test | 21 ++--- .../clang-sycl-linker/ClangSYCLLinker.cpp | 81 ++++++++++-------- 11 files changed, 108 insertions(+), 157 deletions(-) create mode 100644 a.spv create mode 100644 clang/test/Driver/Inputs/SYCL/bar.ll create mode 100644 clang/test/Driver/Inputs/SYCL/baz.ll create mode 100644 clang/test/Driver/Inputs/SYCL/foo.ll create mode 100644 clang/test/Driver/Inputs/SYCL/libsycl.ll delete mode 100644 clang/test/Driver/Inputs/libsycl.ll delete mode 100644 clang/test/Driver/Inputs/test1.ll delete mode 100644 clang/test/Driver/Inputs/test2.ll delete mode 100644 clang/test/Driver/Inputs/test3.ll diff --git a/a.spv b/a.spv new file mode 100644 index 0000000000000000000000000000000000000000..c70bcea1fea155879e40b76740e5eae31ff17a3c GIT binary patch literal 1028 zcmZ9KO-n*S6o!x9d$qFc)4qeGRRlpTD%U}7f__3=^$LM$m%vL2k z`FP=;HTl9lb>c2JwRK5qXZ>WFao*##%SmjD9ItOnYE0L|j%4BV)Eekfzq_*MJtej$ z`*k`{U26LaxrrUf)@&|z_%C+sV{L7O{hjNJcPP`^dMoxrEbM6Juex2}kM|K`%o^h{ zFQ=_{q2H+z(CIJe^n>0|H@x$yKF)==kEHXaO}C*L4+0;W%2`ruZdYt0vV;Fdwdl;t zfkn6a;{!YB@a0EdxYF`9aDiPAYw1r7Zwx1>CH^ZfzB{ou+(iwC{sKN61D`kf^7zb; zpE<$7j&Zn$6ZZ`F0w&x86AlDS_KTl9FtKAy-igDm0uG!EI-CqToCF^}hWX(lyQGHP PZ$ArfOpF@t#pd2W4#P{- literal 0 HcmV?d00001 diff --git a/clang/test/Driver/Inputs/SYCL/bar.ll b/clang/test/Driver/Inputs/SYCL/bar.ll new file mode 100644 index 0000000000000..d17221b8dca18 --- /dev/null +++ b/clang/test/Driver/Inputs/SYCL/bar.ll @@ -0,0 +1,7 @@ +target triple = "spirv64" + +define spir_func i32 @bar_func1(i32 %a, i32 %b) { +entry: + %res = add nsw i32 %b, %a + ret i32 %res +} diff --git a/clang/test/Driver/Inputs/SYCL/baz.ll b/clang/test/Driver/Inputs/SYCL/baz.ll new file mode 100644 index 0000000000000..6cdf3735ed77e --- /dev/null +++ b/clang/test/Driver/Inputs/SYCL/baz.ll @@ -0,0 +1,15 @@ +target triple = "spirv64" + +define spir_func i32 @bar_func1(i32 %a, i32 %b) { +entry: + %mul = shl nsw i32 %a, 1 + %res = add nsw i32 %mul, %b + ret i32 %res +} + +define spir_func i32 @baz_func1(i32 %a) { +entry: + %add = add nsw i32 %a, 5 + %res = tail call spir_func i32 @bar_func1(i32 %a, i32 %add) + ret i32 %res +} diff --git a/clang/test/Driver/Inputs/SYCL/foo.ll b/clang/test/Driver/Inputs/SYCL/foo.ll new file mode 100644 index 0000000000000..43aaf1424ee2d --- /dev/null +++ b/clang/test/Driver/Inputs/SYCL/foo.ll @@ -0,0 +1,19 @@ +target triple = "spirv64" + +define spir_func i32 @foo_func1(i32 %a, i32 %b) { +entry: + %call = tail call spir_func i32 @addFive(i32 %b) + %res = tail call spir_func i32 @bar_func1(i32 %a, i32 %call) + ret i32 %res +} + +declare spir_func i32 @bar_func1(i32, i32) + +declare spir_func i32 @addFive(i32) + +define spir_func i32 @foo_func2(i32 %c, i32 %d, i32 %e) { +entry: + %call = tail call spir_func i32 @foo_func1(i32 %c, i32 %d) + %res = mul nsw i32 %call, %e + ret i32 %res +} diff --git a/clang/test/Driver/Inputs/SYCL/libsycl.ll b/clang/test/Driver/Inputs/SYCL/libsycl.ll new file mode 100644 index 0000000000000..fdc4643e97b6a --- /dev/null +++ b/clang/test/Driver/Inputs/SYCL/libsycl.ll @@ -0,0 +1,13 @@ +target triple = "spirv64" + +define spir_func i32 @addFive(i32 %a) { +entry: + %res = add nsw i32 %a, 5 + ret i32 %res +} + +define spir_func i32 @unusedFunc(i32 %a) { +entry: + %res = mul nsw i32 %a, 5 + ret i32 %res +} diff --git a/clang/test/Driver/Inputs/libsycl.ll b/clang/test/Driver/Inputs/libsycl.ll deleted file mode 100644 index 526d9ed1a8797..0000000000000 --- a/clang/test/Driver/Inputs/libsycl.ll +++ /dev/null @@ -1,36 +0,0 @@ -; ModuleID = 'libsycl.cpp' -source_filename = "libsycl.cpp" -target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1" -target triple = "spirv64" - -; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) -define spir_func noundef range(i32 -2147483643, -2147483648) i32 @_Z9lib_func1i(i32 noundef %a) local_unnamed_addr #0 { -entry: - %add = add nsw i32 %a, 5 - ret i32 %add -} - -; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) -define spir_func noundef i32 @_Z9lib_func2i(i32 noundef %a) local_unnamed_addr #0 { -entry: - %mul = mul nsw i32 %a, 5 - ret i32 %mul -} - -; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) -define spir_func noundef range(i32 -2147483648, 2147483643) i32 @_Z9lib_func3i(i32 noundef %a) local_unnamed_addr #0 { -entry: - %sub = add nsw i32 %a, -5 - ret i32 %sub -} - -; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) -define spir_func noundef i32 @_Z9lib_func4i(i32 noundef %a) local_unnamed_addr #0 { -entry: - %call = tail call spir_func noundef i32 @_Z9lib_func1i(i32 noundef %a) - %call1 = tail call spir_func noundef i32 @_Z9lib_func2i(i32 noundef %a) - %mul = mul nsw i32 %call1, %call - ret i32 %mul -} - -attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } diff --git a/clang/test/Driver/Inputs/test1.ll b/clang/test/Driver/Inputs/test1.ll deleted file mode 100644 index f434b3b60098f..0000000000000 --- a/clang/test/Driver/Inputs/test1.ll +++ /dev/null @@ -1,28 +0,0 @@ -; ModuleID = 'test1.cpp' -source_filename = "test1.cpp" -target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1" -target triple = "spirv64" - -; Function Attrs: mustprogress noinline -define spir_func noundef i32 @_Z9foo_func1ii(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 { -entry: - %call = tail call spir_func noundef i32 @_Z9lib_func4i(i32 noundef %b) - %call1 = tail call spir_func noundef i32 @_Z9bar_func1ii(i32 noundef %a, i32 noundef %call) - ret i32 %call1 -} - -declare spir_func noundef i32 @_Z9bar_func1ii(i32 noundef, i32 noundef) local_unnamed_addr #1 - -declare spir_func noundef i32 @_Z9lib_func4i(i32 noundef) local_unnamed_addr #1 - -; Function Attrs: mustprogress -define spir_func noundef i32 @_Z9foo_func2iii(i32 noundef %c, i32 noundef %d, i32 noundef %e) local_unnamed_addr #2 { -entry: - %call = tail call spir_func noundef i32 @_Z9foo_func1ii(i32 noundef %c, i32 noundef %d) - %mul = mul nsw i32 %call, %e - ret i32 %mul -} - -attributes #0 = { mustprogress noinline "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } -attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } -attributes #2 = { mustprogress "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } diff --git a/clang/test/Driver/Inputs/test2.ll b/clang/test/Driver/Inputs/test2.ll deleted file mode 100644 index 3e963e5adbbd4..0000000000000 --- a/clang/test/Driver/Inputs/test2.ll +++ /dev/null @@ -1,22 +0,0 @@ -; ModuleID = 'test2.cpp' -source_filename = "test2.cpp" -target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1" -target triple = "spirv64" - -; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) -define spir_func noundef i32 @_Z9bar_func1ii(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 { -entry: - %add = add nsw i32 %b, %a - ret i32 %add -} - -; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -define spir_func noundef i32 @_Z9bar_func2iii(i32 noundef %c, i32 noundef %d, i32 noundef %e) local_unnamed_addr #1 { -entry: - %call = tail call spir_func noundef i32 @_Z9bar_func1ii(i32 noundef %c, i32 noundef %d) - %add = add nsw i32 %call, %e - ret i32 %add -} - -attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } -attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } diff --git a/clang/test/Driver/Inputs/test3.ll b/clang/test/Driver/Inputs/test3.ll deleted file mode 100644 index 8ee44e51d1855..0000000000000 --- a/clang/test/Driver/Inputs/test3.ll +++ /dev/null @@ -1,23 +0,0 @@ -; ModuleID = 'test3.cpp' -source_filename = "test3.cpp" -target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1" -target triple = "spirv64" - -; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) -define spir_func noundef i32 @_Z9bar_func1ii(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 { -entry: - %mul = shl nsw i32 %a, 1 - %add = add nsw i32 %mul, %b - ret i32 %add -} - -; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -define spir_func noundef i32 @_Z9baz_func1i(i32 noundef %a) local_unnamed_addr #1 { -entry: - %add = add nsw i32 %a, 5 - %call = tail call spir_func noundef i32 @_Z9bar_func1ii(i32 noundef %a, i32 noundef %add) - ret i32 %call -} - -attributes #0 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } -attributes #1 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } diff --git a/clang/test/Driver/link-device-code.test b/clang/test/Driver/link-device-code.test index 028dc1e24c76f..ffa9638f93fe4 100644 --- a/clang/test/Driver/link-device-code.test +++ b/clang/test/Driver/link-device-code.test @@ -1,22 +1,23 @@ -# RUN: llvm-as %S/Inputs/test1.ll -o %t.test1.bc -# RUN: llvm-as %S/Inputs/test2.ll -o %t.test2.bc -# RUN: llvm-as %S/Inputs/test3.ll -o %t.test3.bc -# RUN: llvm-as %S/Inputs/libsycl.ll -o %t.libsycl.bc -# RUN: clang-sycl-linker %t.test1.bc %t.test2.bc -triple spirv64 -o test.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-SIMPLE +# RUN: llvm-as %S/Inputs/SYCL/foo.ll -o %t.foo.bc +# RUN: llvm-as %S/Inputs/SYCL/bar.ll -o %t.bar.bc +# RUN: llvm-as %S/Inputs/SYCL/baz.ll -o %t.baz.bc +# RUN: llvm-as %S/Inputs/SYCL/libsycl.ll -o %t.libsycl.bc +# RUN: clang-sycl-linker %t.foo.bc %t.bar.bc -triple spirv64 -o a.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-SIMPLE -# RUN: not clang-sycl-linker %t.test2.bc %t.test3.bc -triple spirv64 -o test.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-MULTIPLE-DEFS +# RUN: not clang-sycl-linker %t.bar.bc %t.baz.bc -triple spirv64 -o a.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-MULTIPLE-DEFS -# RUN: clang-sycl-linker %t.test1.bc %t.test2.bc -device-libs=%t.libsycl.bc -library-path= -triple spirv64 -o test.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-DEVICE-LIB +# RUN: clang-sycl-linker %t.foo.bc %t.bar.bc -device-libs=%t.libsycl.bc -library-path=/ -triple spirv64 -o a.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-DEVICE-LIB ; CHECK-SIMPLE: define {{.*}}foo_func1{{.*}} ; CHECK-SIMPLE: define {{.*}}foo_func2{{.*}} ; CHECK-SIMPLE: define {{.*}}bar_func1{{.*}} -; CHECK-SIMPLE: define {{.*}}bar_func2{{.*}} +; CHECK-SIMPLE-NOT: define {{.*}}addFive{{.*}} +; CHECK-SIMPLE-NOT: define {{.*}}unusedFunc{{.*}} ;CHECK-MULTIPLE-DEFS: error: Linking globals named {{.*}}bar_func1{{.*}} symbol multiply defined! ; CHECK-DEVICE-LIB: define {{.*}}foo_func1{{.*}} ; CHECK-DEVICE-LIB: define {{.*}}foo_func2{{.*}} ; CHECK-DEVICE-LIB: define {{.*}}bar_func1{{.*}} -; CHECK-DEVICE-LIB: define {{.*}}bar_func2{{.*}} - +; CHECK-DEVICE-LIB: define {{.*}}addFive{{.*}} +; CHECK-DEVICE-LIB-NOT: define {{.*}}unusedFunc{{.*}} diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index 61d4a9caaad00..f8e61e1b882e5 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -201,10 +201,10 @@ Expected> getInput(const ArgList &Args) { return BitcodeFiles; } -/// Handle cases where input file is a LLVM IR bitcode file +/// Handle cases where input file is a LLVM IR bitcode file. /// When clang-sycl-linker is called via clang-linker-wrapper tool, input files -/// are LLVM IR bitcode files -// TODO: Support SPIR-V IR files +/// are LLVM IR bitcode files. +// TODO: Support SPIR-V IR files. Expected> getBitcodeModule(StringRef File, LLVMContext &C) { SMDiagnostic Err; @@ -215,9 +215,9 @@ Expected> getBitcodeModule(StringRef File, return createStringError("Unable to parse file"); } -// This utility function is used to gather all SYCL device library files that -// will be linked with input device files. -// The list of files and its location are passed from driver. +/// Gather all SYCL device library files that will be linked with input device +/// files. +/// The list of files and its location are passed from driver. Expected> getSYCLDeviceLibs(const ArgList &Args) { SmallVector DeviceLibFiles; StringRef LibraryPath; @@ -256,35 +256,6 @@ Expected linkDeviceCode(ArrayRef InputFiles, assert(InputFiles.size() && "No inputs to link"); - // Create a new file to write the linked device file to - auto BitcodeOutput = - createTempFile(Args, sys::path::filename(OutputFile), "bc"); - if (!BitcodeOutput) - return BitcodeOutput.takeError(); - - // Get all SYCL device library files, if any - auto SYCLDeviceLibFiles = getSYCLDeviceLibs(Args); - if (!SYCLDeviceLibFiles) - return SYCLDeviceLibFiles.takeError(); - - if (Verbose) { - std::string Inputs = - std::accumulate(std::next(InputFiles.begin()), InputFiles.end(), - InputFiles.front(), [](std::string a, std::string b) { - return std::move(a) + ", " + std::move(b); - }); - std::string LibInputs = ""; - if (!(*SYCLDeviceLibFiles).empty()) - LibInputs = std::accumulate( - std::next((*SYCLDeviceLibFiles).begin()), (*SYCLDeviceLibFiles).end(), - (*SYCLDeviceLibFiles).front(), [](std::string a, std::string b) { - return std::move(a) + ", " + std::move(b); - }); - errs() << formatv( - "sycl-device-link: inputs: {0} libfiles: {1} output: {2}\n", Inputs, - LibInputs, *BitcodeOutput); - } - LLVMContext C; auto LinkerOutput = std::make_unique("sycl-device-link", C); Linker L(*LinkerOutput); @@ -297,6 +268,11 @@ Expected linkDeviceCode(ArrayRef InputFiles, return createStringError("Could not link IR"); } + // Get all SYCL device library files, if any. + auto SYCLDeviceLibFiles = getSYCLDeviceLibs(Args); + if (!SYCLDeviceLibFiles) + return SYCLDeviceLibFiles.takeError(); + // Link in SYCL device library files. const llvm::Triple Triple(Args.getLastArgValue(OPT_triple)); for (auto &File : *SYCLDeviceLibFiles) { @@ -310,15 +286,41 @@ Expected linkDeviceCode(ArrayRef InputFiles, } } + // Dump linked output for testing. if (Args.hasArg(OPT_print_linked_module)) - errs() << *LinkerOutput; + outs() << *LinkerOutput; + + // Create a new file to write the linked device file to. + auto BitcodeOutput = + createTempFile(Args, sys::path::filename(OutputFile), "bc"); + if (!BitcodeOutput) + return BitcodeOutput.takeError(); - // Write the final output into 'BitcodeOutput' file + // Write the final output into 'BitcodeOutput' file. int FD = -1; if (std::error_code EC = sys::fs::openFileForWrite(*BitcodeOutput, FD)) return errorCodeToError(EC); llvm::raw_fd_ostream OS(FD, true); WriteBitcodeToFile(*LinkerOutput, OS); + + if (Verbose) { + std::string Inputs = + std::accumulate(std::next(InputFiles.begin()), InputFiles.end(), + InputFiles.front(), [](std::string a, std::string b) { + return std::move(a) + ", " + std::move(b); + }); + std::string LibInputs = ""; + if (!(*SYCLDeviceLibFiles).empty()) + LibInputs = std::accumulate( + std::next((*SYCLDeviceLibFiles).begin()), (*SYCLDeviceLibFiles).end(), + (*SYCLDeviceLibFiles).front(), [](std::string a, std::string b) { + return std::move(a) + ", " + std::move(b); + }); + errs() << formatv( + "sycl-device-link: inputs: {0} libfiles: {1} output: {2}\n", Inputs, + LibInputs, *BitcodeOutput); + } + return *BitcodeOutput; } @@ -440,10 +442,13 @@ static Expected runLLVMToSPIRVTranslation(StringRef File, return OutputFile; } +/// Performs the following steps: +/// 1. Link input device code (user code and SYCL device library code). +/// 2. Run SPIR-V code generation. Error runSYCLLink(ArrayRef Files, const ArgList &Args) { llvm::TimeTraceScope TimeScope("SYCLDeviceLink"); - // Link all input bitcode files and SYCL device library files, if any + // Link all input bitcode files and SYCL device library files, if any. auto LinkedFile = linkDeviceCode(Files, Args); if (!LinkedFile) reportError(LinkedFile.takeError()); From a5acaa3cb0c9e0b950d87333c00e379b0b838c70 Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Sat, 29 Mar 2025 10:25:46 -0700 Subject: [PATCH 13/16] Added -dry-run option to testing Signed-off-by: Arvind Sudarsanam --- clang/test/Driver/link-device-code.test | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/test/Driver/link-device-code.test b/clang/test/Driver/link-device-code.test index ffa9638f93fe4..6e95c5620bf5c 100644 --- a/clang/test/Driver/link-device-code.test +++ b/clang/test/Driver/link-device-code.test @@ -2,11 +2,11 @@ # RUN: llvm-as %S/Inputs/SYCL/bar.ll -o %t.bar.bc # RUN: llvm-as %S/Inputs/SYCL/baz.ll -o %t.baz.bc # RUN: llvm-as %S/Inputs/SYCL/libsycl.ll -o %t.libsycl.bc -# RUN: clang-sycl-linker %t.foo.bc %t.bar.bc -triple spirv64 -o a.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-SIMPLE +# RUN: clang-sycl-linker %t.foo.bc %t.bar.bc -triple spirv64 --dry-run -o a.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-SIMPLE -# RUN: not clang-sycl-linker %t.bar.bc %t.baz.bc -triple spirv64 -o a.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-MULTIPLE-DEFS +# RUN: not clang-sycl-linker %t.bar.bc %t.baz.bc -triple spirv64 --dry-run -o a.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-MULTIPLE-DEFS -# RUN: clang-sycl-linker %t.foo.bc %t.bar.bc -device-libs=%t.libsycl.bc -library-path=/ -triple spirv64 -o a.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-DEVICE-LIB +# RUN: clang-sycl-linker %t.foo.bc %t.bar.bc -device-libs=%t.libsycl.bc -library-path=/ -triple spirv64 --dry-run -o a.spv --print-linked-module 2>&1 | FileCheck %s --check-prefix=CHECK-DEVICE-LIB ; CHECK-SIMPLE: define {{.*}}foo_func1{{.*}} ; CHECK-SIMPLE: define {{.*}}foo_func2{{.*}} From 0649b618bed1b63db90a38155759b15809b110c6 Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Sat, 29 Mar 2025 12:29:47 -0700 Subject: [PATCH 14/16] Remove unnecessary file Signed-off-by: Arvind Sudarsanam --- a.spv | Bin 1028 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 a.spv diff --git a/a.spv b/a.spv deleted file mode 100644 index c70bcea1fea155879e40b76740e5eae31ff17a3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1028 zcmZ9KO-n*S6o!x9d$qFc)4qeGRRlpTD%U}7f__3=^$LM$m%vL2k z`FP=;HTl9lb>c2JwRK5qXZ>WFao*##%SmjD9ItOnYE0L|j%4BV)Eekfzq_*MJtej$ z`*k`{U26LaxrrUf)@&|z_%C+sV{L7O{hjNJcPP`^dMoxrEbM6Juex2}kM|K`%o^h{ zFQ=_{q2H+z(CIJe^n>0|H@x$yKF)==kEHXaO}C*L4+0;W%2`ruZdYt0vV;Fdwdl;t zfkn6a;{!YB@a0EdxYF`9aDiPAYw1r7Zwx1>CH^ZfzB{ou+(iwC{sKN61D`kf^7zb; zpE<$7j&Zn$6ZZ`F0w&x86AlDS_KTl9FtKAy-igDm0uG!EI-CqToCF^}hWX(lyQGHP PZ$ArfOpF@t#pd2W4#P{- From 987ab667002b5141398fcbfbfe0e116ab14ab9fa Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Mon, 31 Mar 2025 07:01:11 -0700 Subject: [PATCH 15/16] Valid LLVM IR bitcode input files are required Signed-off-by: Arvind Sudarsanam --- clang/test/Driver/clang-sycl-linker-test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/Driver/clang-sycl-linker-test.cpp b/clang/test/Driver/clang-sycl-linker-test.cpp index fbbe298a4733c..be4305506bf23 100644 --- a/clang/test/Driver/clang-sycl-linker-test.cpp +++ b/clang/test/Driver/clang-sycl-linker-test.cpp @@ -1,8 +1,8 @@ // Tests the clang-sycl-linker tool. // // Test the dry run of a simple case to link two input files. -// RUN: touch %t_1.bc -// RUN: touch %t_2.bc +// RUN: %clangxx -emit-llvm -c %s -o %t_1.bc +// RUN: %clangxx -emit-llvm -c %s -o %t_2.bc // RUN: clang-sycl-linker --dry-run -v -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=SIMPLE-FO // SIMPLE-FO: sycl-device-link: inputs: {{.*}}.bc, {{.*}}.bc libfiles: output: [[LLVMLINKOUT:.*]].bc From 917987de32182c9905a70da9231d01be9498428f Mon Sep 17 00:00:00 2001 From: Arvind Sudarsanam Date: Mon, 31 Mar 2025 12:26:04 -0700 Subject: [PATCH 16/16] Minor changes to align with changes in SPIR-V backend PR Signed-off-by: Arvind Sudarsanam --- clang/test/Driver/clang-sycl-linker-test.cpp | 16 ++++++++-------- clang/test/Driver/sycl-link-spirv-target.cpp | 4 ++-- .../tools/clang-sycl-linker/ClangSYCLLinker.cpp | 4 ++-- clang/tools/clang-sycl-linker/SYCLLinkOpts.td | 12 ++++++++---- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/clang/test/Driver/clang-sycl-linker-test.cpp b/clang/test/Driver/clang-sycl-linker-test.cpp index be4305506bf23..729561bd09cd8 100644 --- a/clang/test/Driver/clang-sycl-linker-test.cpp +++ b/clang/test/Driver/clang-sycl-linker-test.cpp @@ -1,9 +1,9 @@ // Tests the clang-sycl-linker tool. // // Test the dry run of a simple case to link two input files. -// RUN: %clangxx -emit-llvm -c %s -o %t_1.bc -// RUN: %clangxx -emit-llvm -c %s -o %t_2.bc -// RUN: clang-sycl-linker --dry-run -v -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ +// RUN: %clangxx -emit-llvm -c -target spirv64 %s -o %t_1.bc +// RUN: %clangxx -emit-llvm -c -target spirv64 %s -o %t_2.bc +// RUN: clang-sycl-linker --dry-run -v -triple=spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=SIMPLE-FO // SIMPLE-FO: sycl-device-link: inputs: {{.*}}.bc, {{.*}}.bc libfiles: output: [[LLVMLINKOUT:.*]].bc // SIMPLE-FO-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc @@ -11,7 +11,7 @@ // Test the dry run of a simple case with device library files specified. // RUN: touch %T/lib1.bc // RUN: touch %T/lib2.bc -// RUN: clang-sycl-linker --dry-run -v -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.bc,lib2.bc -o a.spv 2>&1 \ +// RUN: clang-sycl-linker --dry-run -v -triple=spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.bc,lib2.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBS // DEVLIBS: sycl-device-link: inputs: {{.*}}.bc libfiles: {{.*}}lib1.bc, {{.*}}lib2.bc output: [[LLVMLINKOUT:.*]].bc // DEVLIBS-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[LLVMLINKOUT]].bc @@ -23,19 +23,19 @@ // FILETYPEERROR: Unsupported file type // // Test to see if device library related errors are emitted. -// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs= -o a.spv 2>&1 \ +// RUN: not clang-sycl-linker --dry-run -triple=spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs= -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBSERR1 // DEVLIBSERR1: Number of device library files cannot be zero -// RUN: not clang-sycl-linker --dry-run -triple spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.bc,lib2.bc,lib3.bc -o a.spv 2>&1 \ +// RUN: not clang-sycl-linker --dry-run -triple=spirv64 %t_1.bc %t_2.bc --library-path=%T --device-libs=lib1.bc,lib2.bc,lib3.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=DEVLIBSERR2 // DEVLIBSERR2: '{{.*}}lib3.bc' SYCL device library file is not found // // Test if correct set of llvm-spirv options are emitted for windows environment. -// RUN: clang-sycl-linker --dry-run -v -triple spirv64 --is-windows-msvc-env %t_1.bc %t_2.bc -o a.spv 2>&1 \ +// RUN: clang-sycl-linker --dry-run -v -triple=spirv64 --is-windows-msvc-env %t_1.bc %t_2.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=LLVMOPTSWIN // LLVMOPTSWIN: -spirv-debug-info-version=ocl-100 -spirv-allow-extra-diexpressions -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext= // // Test if correct set of llvm-spirv options are emitted for linux environment. -// RUN: clang-sycl-linker --dry-run -v -triple spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ +// RUN: clang-sycl-linker --dry-run -v -triple=spirv64 %t_1.bc %t_2.bc -o a.spv 2>&1 \ // RUN: | FileCheck %s --check-prefix=LLVMOPTSLIN // LLVMOPTSLIN: -spirv-debug-info-version=nonsemantic-shader-200 -spirv-allow-unknown-intrinsics=llvm.genx. -spirv-ext= diff --git a/clang/test/Driver/sycl-link-spirv-target.cpp b/clang/test/Driver/sycl-link-spirv-target.cpp index 85566c67ea92b..00c132c8f1013 100644 --- a/clang/test/Driver/sycl-link-spirv-target.cpp +++ b/clang/test/Driver/sycl-link-spirv-target.cpp @@ -3,7 +3,7 @@ // // Test that -Xlinker options are being passed to clang-sycl-linker. // RUN: touch %t.bc -// RUN: %clangxx -### --target=spirv64 --sycl-link -Xlinker --llvm-spirv-path=/tmp \ +// RUN: %clangxx -### --target=spirv64 --sycl-link -Xlinker -triple=spirv64 -Xlinker --llvm-spirv-path=/tmp \ // RUN: -Xlinker --library-path=/tmp -Xlinker --device-libs=lib1.bc,lib2.bc %t.bc 2>&1 \ // RUN: | FileCheck %s -check-prefix=XLINKEROPTS -// XLINKEROPTS: "{{.*}}clang-sycl-linker{{.*}}" "--llvm-spirv-path=/tmp" "--library-path=/tmp" "--device-libs=lib1.bc,lib2.bc" "{{.*}}.bc" "-o" "a.out" +// XLINKEROPTS: "{{.*}}clang-sycl-linker{{.*}}" "--llvm-spirv-path=/tmp" "-triple=spirv64" "--library-path=/tmp" "--device-libs=lib1.bc,lib2.bc" "{{.*}}.bc" "-o" "a.out" diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index f8e61e1b882e5..edde56486e7e8 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -274,7 +274,7 @@ Expected linkDeviceCode(ArrayRef InputFiles, return SYCLDeviceLibFiles.takeError(); // Link in SYCL device library files. - const llvm::Triple Triple(Args.getLastArgValue(OPT_triple)); + const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ)); for (auto &File : *SYCLDeviceLibFiles) { auto LibMod = getBitcodeModule(File, C); if (!LibMod) @@ -405,7 +405,7 @@ static Expected runLLVMToSPIRVTranslation(StringRef File, SmallVector CmdArgs; CmdArgs.push_back(*LLVMToSPIRVProg); - const llvm::Triple Triple(Args.getLastArgValue(OPT_triple)); + const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ)); getSPIRVTransOpts(Args, CmdArgs, Triple); StringRef LLVMToSPIRVOptions; if (Arg *A = Args.getLastArg(OPT_llvm_spirv_options_EQ)) diff --git a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td index 6727af7d1ec63..40c7310076045 100644 --- a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td +++ b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td @@ -24,10 +24,14 @@ def device_libs_EQ : CommaJoined<["--", "-"], "device-libs=">, Flags<[LinkerOnlyOption]>, HelpText<"A comma separated list of device libraries that are linked during the device link.">; -def triple : Separate<["--", "-"], "triple">, - HelpText<"The device target triple">; -def arch : Separate<["--", "-"], "arch">, - HelpText<"Specify the name of the target architecture.">; +def arch_EQ : Joined<["--", "-"], "arch=">, + Flags<[LinkerOnlyOption]>, + MetaVarName<"">, + HelpText<"The device subarchitecture">; +def triple_EQ : Joined<["--", "-"], "triple=">, + Flags<[LinkerOnlyOption]>, + MetaVarName<"">, + HelpText<"The device target triple">; def save_temps : Flag<["--", "-"], "save-temps">, Flags<[LinkerOnlyOption]>, HelpText<"Save intermediate results">;