diff --git a/clang/lib/Driver/ToolChains/SYCL.cpp b/clang/lib/Driver/ToolChains/SYCL.cpp index 8692d242268af..c1b2c4142804c 100644 --- a/clang/lib/Driver/ToolChains/SYCL.cpp +++ b/clang/lib/Driver/ToolChains/SYCL.cpp @@ -13,6 +13,7 @@ #include "llvm/SYCLLowerIR/DeviceConfigFile.hpp" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Path.h" +#include "llvm/Support/VirtualFileSystem.h" #include using namespace clang::driver; @@ -55,7 +56,7 @@ const char *SYCLInstallationDetector::findLibspirvPath( // If -fsycl-libspirv-path= is specified, try to use that path directly. if (Arg *A = Args.getLastArg(options::OPT_fsycl_libspirv_path_EQ)) { - if (llvm::sys::fs::exists(A->getValue())) + if (D.getVFS().exists(A->getValue())) return A->getValue(); return nullptr; @@ -68,7 +69,7 @@ const char *SYCLInstallationDetector::findLibspirvPath( SmallString<128> LibraryPath(Path); llvm::sys::path::append(LibraryPath, a, b, c, Basename); - if (llvm::sys::fs::exists(LibraryPath)) + if (D.getVFS().exists(LibraryPath)) return Args.MakeArgString(LibraryPath); return nullptr; diff --git a/sycl-jit/jit-compiler/CMakeLists.txt b/sycl-jit/jit-compiler/CMakeLists.txt index 3349f1e1ebf84..942a650e1c871 100644 --- a/sycl-jit/jit-compiler/CMakeLists.txt +++ b/sycl-jit/jit-compiler/CMakeLists.txt @@ -1,4 +1,50 @@ + +set(SYCL_JIT_RESOURCE_CPP "${CMAKE_CURRENT_BINARY_DIR}/resource.cpp") +set(SYCL_JIT_RESOURCE_OBJ "${CMAKE_CURRENT_BINARY_DIR}/resource.cpp.o") + +if (WIN32) +set(SYCL_JIT_VIRTUAL_TOOLCHAIN_ROOT "c:/sycl-jit-toolchain/") +else() +set(SYCL_JIT_VIRTUAL_TOOLCHAIN_ROOT "/sycl-jit-toolchain/") +endif() + +add_custom_command( + OUTPUT ${SYCL_JIT_RESOURCE_CPP} + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/utils/generate.py --toolchain-dir ${CMAKE_BINARY_DIR} --output ${SYCL_JIT_RESOURCE_CPP} --prefix ${SYCL_JIT_VIRTUAL_TOOLCHAIN_ROOT} + DEPENDS + sycl-headers # include/sycl + libclc # lib/clc + clang # lib/clang + # TODO: libdevice + ${CMAKE_CURRENT_SOURCE_DIR}/utils/generate.py +) + +# We use C23/C++26's `#embed` to implement this resource creation, and "current" +# CMAKE_CXX_COMPILER might not have support for it. As such, use freshly built +# `clang++` instead. +if (WIN32) + set(clang_exe ${CMAKE_BINARY_DIR}/bin/clang-cl.exe) + set(SYCL_JIT_RESOURCE_CXX_FLAGS /O2 /std:c++17 /W0) + if (CMAKE_BUILD_TYPE MATCHES "Debug") + list(APPEND SYCL_JIT_RESOURCE_CXX_FLAGS /MDd) + else() + list(APPEND SYCL_JIT_RESOURCE_CXX_FLAGS /MD) + endif() +else() + get_host_tool_path( clang CLANG clang_exe clang_target ) + set(SYCL_JIT_RESOURCE_CXX_FLAGS -O2 -Wno-c23-extensions -std=c++17 -fPIC -fvisibility=hidden) +endif() + +add_custom_command( + OUTPUT ${SYCL_JIT_RESOURCE_OBJ} + COMMAND + ${clang_exe} ${SYCL_JIT_RESOURCE_CPP} -I ${CMAKE_CURRENT_SOURCE_DIR}/include -c -o ${SYCL_JIT_RESOURCE_OBJ} ${SYCL_JIT_RESOURCE_CXX_FLAGS} + DEPENDS + ${SYCL_JIT_RESOURCE_CPP} + ${CMAKE_CURRENT_SOURCE_DIR}/include/Resource.h +) + add_llvm_library(sycl-jit lib/translation/JITContext.cpp lib/translation/SPIRVLLVMTranslation.cpp @@ -11,6 +57,8 @@ add_llvm_library(sycl-jit lib/helper/ConfigHelper.cpp lib/helper/ErrorHelper.cpp + ${SYCL_JIT_RESOURCE_OBJ} + SHARED DEPENDS diff --git a/sycl-jit/jit-compiler/include/Resource.h b/sycl-jit/jit-compiler/include/Resource.h new file mode 100644 index 0000000000000..613934dd02830 --- /dev/null +++ b/sycl-jit/jit-compiler/include/Resource.h @@ -0,0 +1,20 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include +#include +#include + +namespace jit_compiler { +// Defined in the auto-generated file: +extern const std::pair ToolchainFiles[]; +extern size_t NumToolchainFiles; +extern std::string_view ToolchainPrefix; +} // namespace jit_compiler diff --git a/sycl-jit/jit-compiler/lib/rtc/DeviceCompilation.cpp b/sycl-jit/jit-compiler/lib/rtc/DeviceCompilation.cpp index 036afb8ed72db..59eda6f548faa 100644 --- a/sycl-jit/jit-compiler/lib/rtc/DeviceCompilation.cpp +++ b/sycl-jit/jit-compiler/lib/rtc/DeviceCompilation.cpp @@ -9,6 +9,7 @@ #include "DeviceCompilation.h" #include "ESIMD.h" #include "JITBinaryInfo.h" +#include "Resource.h" #include "translation/Translation.h" #include @@ -178,7 +179,12 @@ class HashPreprocessedAction : public PreprocessorFrontendAction { }; class SYCLToolchain { - SYCLToolchain() {} + SYCLToolchain() { + for (size_t i = 0; i < NumToolchainFiles; ++i) { + auto [Path, Content] = ToolchainFiles[i]; + ToolchainFS->addFile(Path, 0, llvm::MemoryBuffer::getMemBuffer(Content)); + } + } // Similar to FrontendActionFactory, but we don't take ownership of // `FrontendAction`, nor do we create copies of it as we only perform a single @@ -231,6 +237,7 @@ class SYCLToolchain { DiagnosticConsumer *DiagConsumer = nullptr) { auto FS = llvm::makeIntrusiveRefCnt( llvm::vfs::getRealFileSystem()); + FS->pushOverlay(ToolchainFS); if (FSOverlay) FS->pushOverlay(FSOverlay); @@ -245,8 +252,14 @@ class SYCLToolchain { return TI.run(); } + std::string_view getClangXXExe() const { return ClangXXExe; } + private: clang::IgnoringDiagConsumer IgnoreDiag; + std::string ClangXXExe = + (jit_compiler::ToolchainPrefix + "/bin/clang++").str(); + llvm::IntrusiveRefCntPtr ToolchainFS = + llvm::makeIntrusiveRefCnt(); }; class ClangDiagnosticWrapper { @@ -296,14 +309,11 @@ class LLVMDiagnosticWrapper : public llvm::DiagnosticHandler { } // anonymous namespace static std::vector -createCommandLine(const InputArgList &UserArgList, std::string_view DPCPPRoot, - BinaryFormat Format, std::string_view SourceFilePath) { +createCommandLine(const InputArgList &UserArgList, BinaryFormat Format, + std::string_view SourceFilePath) { DerivedArgList DAL{UserArgList}; const auto &OptTable = getDriverOptTable(); DAL.AddFlagArg(nullptr, OptTable.getOption(OPT_fsycl_device_only)); - DAL.AddJoinedArg( - nullptr, OptTable.getOption(OPT_resource_dir_EQ), - (DPCPPRoot + "/lib/clang/" + Twine(CLANG_VERSION_MAJOR)).str()); // User args may contain options not intended for the frontend, but we can't // claim them here to tell the driver they're used later. Hence, suppress the // unused argument warning. @@ -327,7 +337,7 @@ createCommandLine(const InputArgList &UserArgList, std::string_view DPCPPRoot, std::vector CommandLine; CommandLine.reserve(ASL.size() + 2); - CommandLine.emplace_back((DPCPPRoot + "/bin/clang++").str()); + CommandLine.emplace_back(SYCLToolchain::instance().getClangXXExe()); transform(ASL, std::back_inserter(CommandLine), [](const char *AS) { return std::string{AS}; }); CommandLine.emplace_back(SourceFilePath); @@ -355,13 +365,8 @@ Expected jit_compiler::calculateHash( const InputArgList &UserArgList, BinaryFormat Format) { TimeTraceScope TTS{"calculateHash"}; - const std::string &DPCPPRoot = getDPCPPRoot(); - if (DPCPPRoot == InvalidDPCPPRoot) { - return createStringError("Could not locate DPCPP root directory"); - } - std::vector CommandLine = - createCommandLine(UserArgList, DPCPPRoot, Format, SourceFile.Path); + createCommandLine(UserArgList, Format, SourceFile.Path); HashPreprocessedAction HashAction; @@ -372,8 +377,7 @@ Expected jit_compiler::calculateHash( // unique for each query, so drop it: CommandLine.pop_back(); - // The command line contains the DPCPP root and clang major version in - // "-resource-dir=<...>" argument. + // TODO: Include hash of the current libsycl-jit.so/.dll somehow... BLAKE3Result<> CommandLineHash = BLAKE3::hash(arrayRefFromStringRef(join(CommandLine, ","))); @@ -394,18 +398,13 @@ Expected jit_compiler::compileDeviceCode( LLVMContext &Context, BinaryFormat Format) { TimeTraceScope TTS{"compileDeviceCode"}; - const std::string &DPCPPRoot = getDPCPPRoot(); - if (DPCPPRoot == InvalidDPCPPRoot) { - return createStringError("Could not locate DPCPP root directory"); - } - EmitLLVMOnlyAction ELOA{&Context}; DiagnosticOptions DiagOpts; ClangDiagnosticWrapper Wrapper(BuildLog, &DiagOpts); if (SYCLToolchain::instance().run( - createCommandLine(UserArgList, DPCPPRoot, Format, SourceFile.Path), - ELOA, getInMemoryFS(SourceFile, IncludeFiles), Wrapper.consumer())) { + createCommandLine(UserArgList, Format, SourceFile.Path), ELOA, + getInMemoryFS(SourceFile, IncludeFiles), Wrapper.consumer())) { return ELOA.takeModule(); } else { return createStringError(BuildLog); diff --git a/sycl-jit/jit-compiler/utils/generate.py b/sycl-jit/jit-compiler/utils/generate.py new file mode 100644 index 0000000000000..b4bcf6871c60e --- /dev/null +++ b/sycl-jit/jit-compiler/utils/generate.py @@ -0,0 +1,67 @@ +import os +import argparse + + +def main(): + parser = argparse.ArgumentParser( + description="Generate SYCL Headers Resource C++ file" + ) + parser.add_argument("-o", "--output", type=str, required=True, help="Output file") + parser.add_argument( + "-i", + "--toolchain-dir", + type=str, + required=True, + help="Path to toolchain root directory", + ) + parser.add_argument( + "--prefix", type=str, required=True, help="Prefix for file locations" + ) + args = parser.parse_args() + + # abspath also strips trailing "/" + toolchain_dir = os.path.abspath(args.toolchain_dir) + + with open(args.output, "w") as out: + out.write( + """ +#include + +namespace jit_compiler { +const std::pair ToolchainFiles[] = {""" + ) + + def process_dir(dir): + for root, _, files in os.walk(dir): + for file in files: + file_path = os.path.join(root, file) + out.write( + f""" +{{ + {{"{args.prefix}{os.path.relpath(file_path, toolchain_dir).replace(os.sep, "/")}"}} , + []() {{ + static const char data[] = {{ + #embed "{file_path}" if_empty(0) + , 0}}; + return std::string_view(data, std::size(data) - 1); + }}() +}},""" + ) + + process_dir(os.path.join(args.toolchain_dir, "include/")) + process_dir(os.path.join(args.toolchain_dir, "lib/clang/")) + process_dir(os.path.join(args.toolchain_dir, "lib/clc/")) + + out.write( + f""" +}}; + +size_t NumToolchainFiles = std::size(ToolchainFiles); +std::string_view ToolchainPrefix = "{args.prefix}"; +}} // namespace jit_compiler +""" + ) + + +if __name__ == "__main__": + main()