diff --git a/clang/test/Driver/clang-sycl-linker-test.cpp b/clang/test/Driver/clang-sycl-linker-test.cpp index f358900b4fbd8..d9f753a559e0c 100644 --- a/clang/test/Driver/clang-sycl-linker-test.cpp +++ b/clang/test/Driver/clang-sycl-linker-test.cpp @@ -1,48 +1,38 @@ // 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 -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 // SIMPLE: "{{.*}}llvm-link{{.*}}" {{.*}}.bc {{.*}}.bc -o [[FIRSTLLVMLINKOUT:.*]].bc --suppress-warnings -// SIMPLE-NEXT: "{{.*}}llvm-spirv{{.*}}" {{.*}}-o a.spv [[FIRSTLLVMLINKOUT]].bc +// SIMPLE-NEXT: SPIR-V Backend: input: {{.*}}.bc, output: a.spv // // 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: clang-sycl-linker --dry-run -v -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-NO-LINK: SPIR-V Backend: input: {{.*}}.bc, output: a.spv // // 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: 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: "{{.*}}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 +// DEVLIBS-NEXT: SPIR-V Backend: input: [[SECONDLLVMLINKOUT]].bc, output: a.spv // // 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: %clangxx -c -target spirv64 %s -o %t.o +// RUN: not clang-sycl-linker --dry-run -v -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 -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 -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: | 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..586adae619165 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: -Xlinker --library-path=/tmp -Xlinker --device-libs=lib1.bc,lib2.bc %t.bc 2>&1 \ +// RUN: %clangxx -### --target=spirv64 --sycl-link -Xlinker -triple=spirv64 -Xlinker --library-path=/tmp \ +// RUN: -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{{.*}}" "-triple=spirv64" "--library-path=/tmp" "--device-libs=lib1.bc,lib2.bc" "{{.*}}.bc" "-o" "a.out" diff --git a/clang/tools/clang-sycl-linker/CMakeLists.txt b/clang/tools/clang-sycl-linker/CMakeLists.txt index 5665ad7d7186e..c19ce2eac834e 100644 --- a/clang/tools/clang-sycl-linker/CMakeLists.txt +++ b/clang/tools/clang-sycl-linker/CMakeLists.txt @@ -1,8 +1,10 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} BinaryFormat + MC Option Object + Target TargetParser Support ) diff --git a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp index 2bcb3757d49d0..fdc3c5e01d656 100644 --- a/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp +++ b/clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp @@ -23,6 +23,7 @@ #include "llvm/IR/DiagnosticPrinter.h" #include "llvm/IRReader/IRReader.h" #include "llvm/LTO/LTO.h" +#include "llvm/MC/TargetRegistry.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ArchiveWriter.h" #include "llvm/Object/Binary.h" @@ -46,6 +47,7 @@ #include "llvm/Support/TargetSelect.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Support/WithColor.h" +#include "llvm/Target/TargetMachine.h" using namespace llvm; using namespace llvm::opt; @@ -77,6 +79,17 @@ static const char *Executable; static SmallVector> TempFiles; namespace { + +std::once_flag InitOnceFlag; +void InitializeSPIRVTarget() { + std::call_once(InitOnceFlag, []() { + LLVMInitializeSPIRVTargetInfo(); + LLVMInitializeSPIRVTarget(); + LLVMInitializeSPIRVTargetMC(); + LLVMInitializeSPIRVAsmPrinter(); + }); +} + // Must not overlap with llvm::opt::DriverFlag. enum LinkerFlags { LinkerOnlyOption = (1 << 4) }; @@ -206,7 +219,7 @@ Expected> getInput(const ArgList &Args) { /// be parsed to generate options required to be passed into llvm-link. Expected linkDeviceInputFiles(ArrayRef InputFiles, const ArgList &Args) { - llvm::TimeTraceScope TimeScope("SYCL LinkDeviceInputFiles"); + llvm::TimeTraceScope TimeScope("Link device input files"); assert(InputFiles.size() && "No inputs to llvm-link"); // Early check to see if there is only one input. @@ -271,7 +284,7 @@ Expected> getSYCLDeviceLibs(const ArgList &Args) { /// 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"); + llvm::TimeTraceScope TimeScope("Link device library files"); auto SYCLDeviceLibFiles = getSYCLDeviceLibs(Args); if (!SYCLDeviceLibFiles) @@ -304,126 +317,72 @@ static Expected linkDeviceLibFiles(StringRef InputFile, return *OutFileOrErr; } -/// Add any llvm-spirv option that relies on a specific Triple in addition -/// to user supplied options. -static void getSPIRVTransOpts(const ArgList &Args, - SmallVector &TranslatorArgs, - const llvm::Triple Triple) { - // Enable NonSemanticShaderDebugInfo.200 for non-Windows - const bool IsWindowsMSVC = - Triple.isWindowsMSVCEnvironment() || Args.hasArg(OPT_is_windows_msvc_env); - const bool EnableNonSemanticDebug = !IsWindowsMSVC; - if (EnableNonSemanticDebug) { - TranslatorArgs.push_back( - "-spirv-debug-info-version=nonsemantic-shader-200"); - } else { - TranslatorArgs.push_back("-spirv-debug-info-version=ocl-100"); - // Prevent crash in the translator if input IR contains DIExpression - // operations which don't have mapping to OpenCL.DebugInfo.100 spec. - TranslatorArgs.push_back("-spirv-allow-extra-diexpressions"); - } - std::string UnknownIntrinsics("-spirv-allow-unknown-intrinsics=llvm.genx."); - - TranslatorArgs.push_back(Args.MakeArgString(UnknownIntrinsics)); - - // Disable all the extensions by default - std::string ExtArg("-spirv-ext=-all"); - std::string DefaultExtArg = - ",+SPV_EXT_shader_atomic_float_add,+SPV_EXT_shader_atomic_float_min_max" - ",+SPV_KHR_no_integer_wrap_decoration,+SPV_KHR_float_controls" - ",+SPV_KHR_expect_assume,+SPV_KHR_linkonce_odr"; - std::string INTELExtArg = - ",+SPV_INTEL_subgroups,+SPV_INTEL_media_block_io" - ",+SPV_INTEL_device_side_avc_motion_estimation" - ",+SPV_INTEL_fpga_loop_controls,+SPV_INTEL_unstructured_loop_controls" - ",+SPV_INTEL_fpga_reg,+SPV_INTEL_blocking_pipes" - ",+SPV_INTEL_function_pointers,+SPV_INTEL_kernel_attributes" - ",+SPV_INTEL_io_pipes,+SPV_INTEL_inline_assembly" - ",+SPV_INTEL_arbitrary_precision_integers" - ",+SPV_INTEL_float_controls2,+SPV_INTEL_vector_compute" - ",+SPV_INTEL_fast_composite" - ",+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" - ",+SPV_INTEL_fpga_buffer_location" - ",+SPV_INTEL_fpga_argument_interfaces" - ",+SPV_INTEL_fpga_invocation_pipelining_attributes" - ",+SPV_INTEL_fpga_latency_control" - ",+SPV_INTEL_task_sequence" - ",+SPV_KHR_shader_clock" - ",+SPV_INTEL_bindless_images"; - ExtArg = ExtArg + DefaultExtArg + INTELExtArg; - ExtArg += ",+SPV_INTEL_token_type" - ",+SPV_INTEL_bfloat16_conversion" - ",+SPV_INTEL_joint_matrix" - ",+SPV_INTEL_hw_thread_queries" - ",+SPV_KHR_uniform_group_instructions" - ",+SPV_INTEL_masked_gather_scatter" - ",+SPV_INTEL_tensor_float32_conversion" - ",+SPV_INTEL_optnone" - ",+SPV_KHR_non_semantic_info" - ",+SPV_KHR_cooperative_matrix"; - TranslatorArgs.push_back(Args.MakeArgString(ExtArg)); -} - /// Run LLVM to SPIR-V translation. -/// Converts 'File' from LLVM bitcode to SPIR-V format using llvm-spirv tool. +/// Converts 'File' from LLVM bitcode to SPIR-V format using SPIR-V backend. /// 'Args' encompasses all arguments required for linking device code and will -/// be parsed to generate options required to be passed into llvm-spirv tool. -static Expected runLLVMToSPIRVTranslation(StringRef File, - const ArgList &Args) { - llvm::TimeTraceScope TimeScope("LLVMToSPIRVTranslation"); - StringRef LLVMSPIRVPath = Args.getLastArgValue(OPT_llvm_spirv_path_EQ); - Expected LLVMToSPIRVProg = - findProgram(Args, "llvm-spirv", {LLVMSPIRVPath}); - if (!LLVMToSPIRVProg) - return LLVMToSPIRVProg.takeError(); - - SmallVector CmdArgs; - CmdArgs.push_back(*LLVMToSPIRVProg); - const llvm::Triple Triple(Args.getLastArgValue(OPT_triple)); - getSPIRVTransOpts(Args, CmdArgs, Triple); - StringRef LLVMToSPIRVOptions; - if (Arg *A = Args.getLastArg(OPT_llvm_spirv_options_EQ)) - LLVMToSPIRVOptions = A->getValue(); - LLVMToSPIRVOptions.split(CmdArgs, " ", /* MaxSplit = */ -1, - /* KeepEmpty = */ false); - CmdArgs.append({"-o", OutputFile}); - CmdArgs.push_back(File); - if (Error Err = executeCommands(*LLVMToSPIRVProg, CmdArgs)) - return std::move(Err); - - if (!SPIRVDumpDir.empty()) { - std::error_code EC = - llvm::sys::fs::create_directory(SPIRVDumpDir, /*IgnoreExisting*/ true); - if (EC) - return createStringError( - EC, - formatv("failed to create dump directory. path: {0}, error_code: {1}", - SPIRVDumpDir, EC.value())); - - StringRef Path = OutputFile; - StringRef Filename = llvm::sys::path::filename(Path); - SmallString<128> CopyPath = SPIRVDumpDir; - CopyPath.append(Filename); - EC = llvm::sys::fs::copy_file(Path, CopyPath); - if (EC) - return createStringError( - EC, - formatv( - "failed to copy file. original: {0}, copy: {1}, error_code: {2}", - Path, CopyPath, EC.value())); +/// be parsed to generate options required to be passed into the backend. +static Expected runSPIRVCodeGen(StringRef File, + const ArgList &Args) { + llvm::TimeTraceScope TimeScope("SPIR-V code generation"); + + // SPIR-V-specific target initialization. + InitializeSPIRVTarget(); + + // Parse input module. + SMDiagnostic Err; + LLVMContext C; + std::unique_ptr M = parseIRFile(File, Err, C); + if (!M) + return createStringError(inconvertibleErrorCode(), Err.getMessage()); + + Triple TargetTriple(Args.getLastArgValue(OPT_triple_EQ)); + M->setTargetTriple(TargetTriple); + + // Get a handle to SPIR-V target backend. + std::string Msg; + const Target *T = TargetRegistry::lookupTarget(M->getTargetTriple(), Msg); + if (!T) + return createStringError(Msg + ": " + M->getTargetTriple().str()); + + // Allocate SPIR-V target machine. + TargetOptions Options; + std::optional RM; + std::optional CM; + std::unique_ptr TM( + T->createTargetMachine(M->getTargetTriple().getTriple(), /* CPU */ "", + /* Features */ "", Options, RM, CM)); + if (!TM) + return createStringError("Could not allocate target machine!"); + + // Set data layout if needed. + if (M->getDataLayout().isDefault()) + M->setDataLayout(TM->createDataLayout()); + + // Open output file for writing. + int FD = -1; + if (std::error_code EC = sys::fs::openFileForWrite(OutputFile, FD)) + return errorCodeToError(EC); + auto OS = std::make_unique(FD, true); + + // Run SPIR-V codegen passes to generate SPIR-V file. + legacy::PassManager CodeGenPasses; + TargetLibraryInfoImpl TLII(M->getTargetTriple()); + CodeGenPasses.add(new TargetLibraryInfoWrapperPass(TLII)); + if (TM->addPassesToEmitFile(CodeGenPasses, *OS, nullptr, + CodeGenFileType::ObjectFile)) + return createStringError("Failed to execute SPIR-V Backend"); + CodeGenPasses.run(*M); + + if (Verbose) { + errs() << formatv("SPIR-V Backend: input: {0}, output: {1}\n", File, + OutputFile); } return OutputFile; } Error runSYCLLink(ArrayRef Files, const ArgList &Args) { - llvm::TimeTraceScope TimeScope("SYCLDeviceLink"); + llvm::TimeTraceScope TimeScope("SYCL device linking"); // First llvm-link step auto LinkedFile = linkDeviceInputFiles(Files, Args); if (!LinkedFile) @@ -434,8 +393,8 @@ Error runSYCLLink(ArrayRef Files, const ArgList &Args) { if (!DeviceLinkedFile) reportError(DeviceLinkedFile.takeError()); - // LLVM to SPIR-V translation step - auto SPVFile = runLLVMToSPIRVTranslation(*DeviceLinkedFile, Args); + // SPIR-V code generation step + auto SPVFile = runSPIRVCodeGen(*DeviceLinkedFile, Args); if (!SPVFile) return SPVFile.takeError(); return Error::success(); diff --git a/clang/tools/clang-sycl-linker/SYCLLinkOpts.td b/clang/tools/clang-sycl-linker/SYCLLinkOpts.td index 959fd6c3e867c..1657b01b649de 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 : Joined<["--"], "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">;